//
// SVProgressHUD.h
//
// Created by Sam Vermette on 27.03.11.
// Copyright 2011 Sam Vermette. All rights reserved.
//
// https://github.com/samvermette/SVProgressHUD
//
#import <UIKit/UIKit.h>
#import <AvailabilityMacros.h>
enum {
// 允许用户进行其他界面操作
SVProgressHUDMaskTypeNone = 1,// allow user interactions while HUD is displayed
// 不允许用户进行其他界面操作
SVProgressHUDMaskTypeClear, // don't allow
SVProgressHUDMaskTypeBlack, // don't allow and dim the UI in the back of the HUD
SVProgressHUDMaskTypeGradient // don't allow and dim the UI with a a-la-alert-view bg gradient
};
typedef NSUInteger SVProgressHUDMaskType;
@interface SVProgressHUD : UIView
//展示提示框
+ (void)show;
+ (void)showWithStatus:(NSString*)status;
+ (void)showWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType;
+ (void)showWithMaskType:(SVProgressHUDMaskType)maskType;
+ (void)showSuccessWithStatus:(NSString*)string;
+ (void)showSuccessWithStatus:(NSString *)string duration:(NSTimeInterval)duration;
+ (void)showErrorWithStatus:(NSString *)string;
+ (void)showErrorWithStatus:(NSString *)string duration:(NSTimeInterval)duration;
//改变当前正在展示的提示框文字
+ (void)setStatus:(NSString*)string;// change the HUD loading status while it's showing
//关闭当前提示
+ (void)dismiss;// simply dismiss the HUD with a fade+scale out animation
+ (void)dismissWithSuccess:(NSString*)successString;// also displays the success icon image
+ (void)dismissWithSuccess:(NSString*)successString afterDelay:(NSTimeInterval)seconds;
+ (void)dismissWithError:(NSString*)errorString;// also displays the error icon image
+ (void)dismissWithError:(NSString*)errorString afterDelay:(NSTimeInterval)seconds;
+ (BOOL)isVisible;
@end
//
// SVProgressHUD.m
//
// Created by Sam Vermette on 27.03.11.
// Copyright 2011 Sam Vermette. All rights reserved.
//
// https://github.com/samvermette/SVProgressHUD
//
#import "SVProgressHUD.h"
#import <QuartzCore/QuartzCore.h>
@interface SVProgressHUD ()
@property (nonatomic,readwrite) SVProgressHUDMaskType maskType;
@property (nonatomic,strong, readonly) NSTimer *fadeOutTimer;
@property (nonatomic,strong, readonly) UIWindow *overlayWindow;
@property (nonatomic,strong, readonly) UIView *hudView;
@property (nonatomic,strong, readonly) UILabel *stringLabel;
@property (nonatomic,strong, readonly) UIImageView *imageView;
@property (nonatomic,strong, readonly) UIActivityIndicatorView *spinnerView;
@property (nonatomic,readonly) CGFloat visibleKeyboardHeight;
- (void)showWithStatus:(NSString*)string maskType:(SVProgressHUDMaskType)hudMaskType networkIndicator:(BOOL)show;
- (void)setStatus:(NSString*)string;
- (void)registerNotifications;
- (void)moveToPoint:(CGPoint)newCenter rotateAngle:(CGFloat)angle;
- (void)positionHUD:(NSNotification*)notification;
- (void)dismiss;
- (void)dismissWithStatus:(NSString*)string error:(BOOL)error;
- (void)dismissWithStatus:(NSString*)string error:(BOOL)error afterDelay:(NSTimeInterval)seconds;
@end
@implementation SVProgressHUD
@synthesize overlayWindow, hudView, maskType, fadeOutTimer, stringLabel, imageView, spinnerView, visibleKeyboardHeight;
- (void)dealloc {
self.fadeOutTimer =nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
+ (SVProgressHUD*)sharedView {
static dispatch_once_t once;
static SVProgressHUD *sharedView;
dispatch_once(&once, ^ { sharedView = [[SVProgressHUD alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; });
return sharedView;
}
+ (void)setStatus:(NSString *)string {
[[SVProgressHUD sharedView] setStatus:string];
}
#pragma mark - Show Methods
+ (void)show {
[[SVProgressHUD sharedView] showWithStatus:nil maskType:SVProgressHUDMaskTypeNone networkIndicator:NO];
}
+ (void)showWithStatus:(NSString *)status {
[[SVProgressHUD sharedView] showWithStatus:status maskType:SVProgressHUDMaskTypeNone networkIndicator:NO];
}
+ (void)showWithMaskType:(SVProgressHUDMaskType)maskType {
[[SVProgressHUD sharedView] showWithStatus:nil maskType:maskType networkIndicator:NO];
}
+ (void)showWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType {
[[SVProgressHUD sharedView] showWithStatus:status maskType:maskType networkIndicator:NO];
}
+ (void)showSuccessWithStatus:(NSString *)string {
[SVProgressHUD showSuccessWithStatus:string duration:1];
}
+ (void)showSuccessWithStatus:(NSString *)string duration:(NSTimeInterval)duration {
[SVProgressHUD show];
[SVProgressHUD dismissWithSuccess:string afterDelay:duration];
}
+ (void)showErrorWithStatus:(NSString *)string {
[SVProgressHUD showErrorWithStatus:string duration:1];
}
+ (void)showErrorWithStatus:(NSString *)string duration:(NSTimeInterval)duration {
[SVProgressHUD show];
[SVProgressHUD dismissWithError:string afterDelay:duration];
}
#pragma mark - Dismiss Methods
+ (void)dismiss {
[[SVProgressHUD sharedView] dismiss];
}
+ (void)dismissWithSuccess:(NSString*)successString {
[[SVProgressHUD sharedView] dismissWithStatus:successString error:NO];
}
+ (void)dismissWithSuccess:(NSString *)successString afterDelay:(NSTimeInterval)seconds {
[[SVProgressHUD sharedView] dismissWithStatus:successString error:NO afterDelay:seconds];
}
+ (void)dismissWithError:(NSString*)errorString {
[[SVProgressHUD sharedView] dismissWithStatus:errorString error:YES];
}
+ (void)dismissWithError:(NSString *)errorString afterDelay:(NSTimeInterval)seconds {
[[SVProgressHUD sharedView] dismissWithStatus:errorString error:YES afterDelay:seconds];
}
#pragma mark - Instance Methods
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
self.userInteractionEnabled =NO;
self.backgroundColor = [UIColor clearColor];
self.alpha =0;
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
return self;
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
switch (self.maskType) {
case SVProgressHUDMaskTypeBlack: {
[[UIColor colorWithWhite:0 alpha:0.5] set];
CGContextFillRect(context, self.bounds);
break;
}
case SVProgressHUDMaskTypeGradient: {
size_t locationsCount = 2;
CGFloat locations[2] = {0.0f,1.0f};
CGFloat colors[8] = {0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.75f};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, locations, locationsCount);
CGColorSpaceRelease(colorSpace);
CGPoint center = CGPointMake(self.bounds.size.width/2,self.bounds.size.height/2);
float radius = MIN(self.bounds.size.width ,self.bounds.size.height) ;
CGContextDrawRadialGradient (context, gradient, center, 0, center, radius, kCGGradientDrawsAfterEndLocation);
CGGradientRelease(gradient);
break;
}
}
}
- (void)setStatus:(NSString *)string {
CGFloat hudWidth = 100;
CGFloat hudHeight = 100;
CGFloat stringWidth = 0;
CGFloat stringHeight = 0;
CGRect labelRect = CGRectZero;
if(string) {
CGSize stringSize = [string sizeWithFont:self.stringLabel.font constrainedToSize:CGSizeMake(200,300)];
stringWidth = stringSize.width;
stringHeight = stringSize.height;
hudHeight = 80+stringHeight;
if(stringWidth > hudWidth)
hudWidth = ceil(stringWidth/2)*2;
if(hudHeight > 100) {
labelRect = CGRectMake(12, 66, hudWidth, stringHeight);
hudWidth+=24;
} else {
hudWidth+=24;
labelRect = CGRectMake(0, 66, hudWidth, stringHeight);
}
}
self.hudView.bounds = CGRectMake(0,0, hudWidth, hudHeight);
if(string)
self.imageView.center = CGPointMake(CGRectGetWidth(self.hudView.bounds)/2,36);
else
self.imageView.center = CGPointMake(CGRectGetWidth(self.hudView.bounds)/2, CGRectGetHeight(self.hudView.bounds)/2);
self.stringLabel.hidden =NO;
self.stringLabel.text = string;
self.stringLabel.frame = labelRect;
if(string)
self.spinnerView.center = CGPointMake(ceil(CGRectGetWidth(self.hudView.bounds)/2)+0.5,40.5);
else
self.spinnerView.center = CGPointMake(ceil(CGRectGetWidth(self.hudView.bounds)/2)+0.5, ceil(self.hudView.bounds.size.height/2)+0.5);
}
- (void)setFadeOutTimer:(NSTimer *)newTimer {
if(fadeOutTimer)
[fadeOutTimer invalidate], fadeOutTimer = nil;
if(newTimer)
fadeOutTimer = newTimer;
}
- (void)registerNotifications {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(positionHUD:)
name:UIApplicationDidChangeStatusBarOrientationNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(positionHUD:)
name:UIKeyboardWillHideNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(positionHUD:)
name:UIKeyboardDidHideNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(positionHUD:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(positionHUD:)
name:UIKeyboardDidShowNotification
object:nil];
}
- (void)positionHUD:(NSNotification*)notification {
CGFloat keyboardHeight;
double animationDuration;
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if(notification) {
NSDictionary* keyboardInfo = [notification userInfo];
CGRect keyboardFrame = [[keyboardInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
animationDuration = [[keyboardInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
if(notification.name == UIKeyboardWillShowNotification || notification.name == UIKeyboardDidShowNotification) {
if(UIInterfaceOrientationIsPortrait(orientation))
keyboardHeight = keyboardFrame.size.height;
else
keyboardHeight = keyboardFrame.size.width;
} else
keyboardHeight = 0;
} else {
keyboardHeight = self.visibleKeyboardHeight;
}
CGRect orientationFrame = [UIScreen mainScreen].bounds;
CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
if(UIInterfaceOrientationIsLandscape(orientation)) {
float temp = orientationFrame.size.width;
orientationFrame.size.width = orientationFrame.size.height;
orientationFrame.size.height = temp;
temp = statusBarFrame.size.width;
statusBarFrame.size.width = statusBarFrame.size.height;
statusBarFrame.size.height = temp;
}
CGFloat activeHeight = orientationFrame.size.height;
if(keyboardHeight > 0)
activeHeight += statusBarFrame.size.height*2;
activeHeight -= keyboardHeight;
CGFloat posY = floor(activeHeight*0.45);
CGFloat posX = orientationFrame.size.width/2;
CGPoint newCenter;
CGFloat rotateAngle;
switch (orientation) {
case UIInterfaceOrientationPortraitUpsideDown:
rotateAngle = M_PI;
newCenter = CGPointMake(posX, orientationFrame.size.height-posY);
break;
case UIInterfaceOrientationLandscapeLeft:
rotateAngle = -M_PI/2.0f;
newCenter = CGPointMake(posY, posX);
break;
case UIInterfaceOrientationLandscapeRight:
rotateAngle = M_PI/2.0f;
newCenter = CGPointMake(orientationFrame.size.height-posY, posX);
break;
default:// as UIInterfaceOrientationPortrait
rotateAngle = 0.0;
newCenter = CGPointMake(posX, posY);
break;
}
if(notification) {
[UIView animateWithDuration:animationDuration
delay:0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
[self moveToPoint:newCenter rotateAngle:rotateAngle];
} completion:NULL];
}
else {
[self moveToPoint:newCenter rotateAngle:rotateAngle];
}
}
- (void)moveToPoint:(CGPoint)newCenter rotateAngle:(CGFloat)angle {
self.hudView.transform = CGAffineTransformMakeRotation(angle);
self.hudView.center = newCenter;
}
#pragma mark - Master show/dismiss methods
- (void)showWithStatus:(NSString*)string maskType:(SVProgressHUDMaskType)hudMaskType networkIndicator:(BOOL)show {
dispatch_async(dispatch_get_main_queue(), ^{
if(!self.superview)
[self.overlayWindow addSubview:self];
self.fadeOutTimer = nil;
self.imageView.hidden = YES;
self.maskType = hudMaskType;
[self setStatus:string];
[self.spinnerView startAnimating];
if(self.maskType != SVProgressHUDMaskTypeNone) {
self.overlayWindow.userInteractionEnabled =YES;
} else {
self.overlayWindow.userInteractionEnabled =NO;
}
[self.overlayWindow makeKeyAndVisible];
[self positionHUD:nil];
if(self.alpha !=1) {
[self registerNotifications];
self.hudView.transform = CGAffineTransformScale(self.hudView.transform,1.3, 1.3);
[UIView animateWithDuration:0.15
delay:0
options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.hudView.transform = CGAffineTransformScale(self.hudView.transform,1/1.3, 1/1.3);
self.alpha = 1;
}
completion:NULL];
}
[self setNeedsDisplay];
});
}
- (void)dismissWithStatus:(NSString*)string error:(BOOL)error {
[self dismissWithStatus:string error:error afterDelay:0.9];
}
- (void)dismissWithStatus:(NSString *)string error:(BOOL)error afterDelay:(NSTimeInterval)seconds {
dispatch_async(dispatch_get_main_queue(), ^{
if(self.alpha !=1)
return;
if(error)
self.imageView.image = [UIImage imageNamed:@"SVProgressHUD.bundle/error.png"];
else
self.imageView.image = [UIImage imageNamed:@"SVProgressHUD.bundle/success.png"];
self.imageView.hidden = NO;
[self setStatus:string];
[self.spinnerView stopAnimating];
self.fadeOutTimer = [NSTimer scheduledTimerWithTimeInterval:seconds target:self selector:@selector(dismiss) userInfo:nil repeats:NO];
});
}
- (void)dismiss {
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.15
delay:0
options:UIViewAnimationCurveEaseIn | UIViewAnimationOptionAllowUserInteraction
animations:^{
self.hudView.transform = CGAffineTransformScale(self.hudView.transform,0.8, 0.8);
self.alpha = 0;
}
completion:^(BOOL finished){
if(self.alpha ==0) {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[hudView removeFromSuperview];
hudView = nil;
// Make sure to remove the overlay window from the list of windows
// before trying to find the key window in that same list
NSMutableArray *windows = [[NSMutableArray alloc] initWithArray:[UIApplication sharedApplication].windows];
[windows removeObject:overlayWindow];
overlayWindow = nil;
[windows enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(UIWindow *window, NSUInteger idx,BOOL *stop) {
if([window isKindOfClass:[UIWindow class]] && window.windowLevel == UIWindowLevelNormal) {
[window makeKeyWindow];
*stop = YES;
}
}];
// uncomment to make sure UIWindow is gone from app.windows
//NSLog(@"%@", [UIApplication sharedApplication].windows);
//NSLog(@"keyWindow = %@", [UIApplication sharedApplication].keyWindow);
}
}];
});
}
#pragma mark - Utilities
+ (BOOL)isVisible {
return ([SVProgressHUD sharedView].alpha ==1);
}
#pragma mark - Getters
- (UIWindow *)overlayWindow {
if(!overlayWindow) {
overlayWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
overlayWindow.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
overlayWindow.backgroundColor = [UIColor clearColor];
overlayWindow.userInteractionEnabled = NO;
}
return overlayWindow;
}
- (UIView *)hudView {
if(!hudView) {
hudView = [[UIView alloc] initWithFrame:CGRectZero];
hudView.layer.cornerRadius = 10;
hudView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.8];
hudView.autoresizingMask = (UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin);
[self addSubview:hudView];
}
return hudView;
}
- (UILabel *)stringLabel {
if (stringLabel == nil) {
stringLabel = [[UILabel alloc] initWithFrame:CGRectZero];
stringLabel.textColor = [UIColor whiteColor];
stringLabel.backgroundColor = [UIColor clearColor];
stringLabel.adjustsFontSizeToFitWidth = YES;
stringLabel.textAlignment = UITextAlignmentCenter;
stringLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
stringLabel.font = [UIFont boldSystemFontOfSize:16];
stringLabel.shadowColor = [UIColor blackColor];
stringLabel.shadowOffset = CGSizeMake(0, -1);
stringLabel.numberOfLines = 0;
}
if(!stringLabel.superview)
[self.hudView addSubview:stringLabel];
return stringLabel;
}
- (UIImageView *)imageView {
if (imageView == nil)
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0,0, 28, 28)];
if(!imageView.superview)
[self.hudView addSubview:imageView];
return imageView;
}
- (UIActivityIndicatorView *)spinnerView {
if (spinnerView == nil) {
spinnerView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
spinnerView.hidesWhenStopped = YES;
spinnerView.bounds = CGRectMake(0,0, 37, 37);
}
if(!spinnerView.superview)
[self.hudView addSubview:spinnerView];
return spinnerView;
}
- (CGFloat)visibleKeyboardHeight {
UIWindow *keyboardWindow = nil;
for (UIWindow *testWindow in [[UIApplication sharedApplication] windows]) {
if(![[testWindow class] isEqual:[UIWindow class]]) {
keyboardWindow = testWindow;
break;
}
}
// Locate UIKeyboard.
UIView *foundKeyboard = nil;
for (__strong UIView *possibleKeyboardin [keyboardWindow subviews]) {
// iOS 4 sticks the UIKeyboard inside a UIPeripheralHostView.
if ([[possibleKeyboard description] hasPrefix:@"<UIPeripheralHostView"]) {
possibleKeyboard = [[possibleKeyboard subviews] objectAtIndex:0];
}
if ([[possibleKeyboard description] hasPrefix:@"<UIKeyboard"]) {
foundKeyboard = possibleKeyboard;
break;
}
}
if(foundKeyboard && foundKeyboard.bounds.size.height >100)
return foundKeyboard.bounds.size.height;
return 0;
}
@end