Files
leanote-ios/Leanote/util/SVProgressHUD/SVProgressHUD.m
2015-08-21 23:49:41 +08:00

1031 lines
35 KiB
Objective-C

//
// SVProgressHUD.m
//
// Copyright 2011-2014 Sam Vermette. All rights reserved.
//
// https://github.com/samvermette/SVProgressHUD
// life修改, 为什么要修改, 因为两套不同的loading和showSuccess样式不一样!!
// 两个sharedView, is2=NO
// sharedView2(for loading), is2=YES
//
#if !__has_feature(objc_arc)
#error SVProgressHUD is ARC only. Either turn on ARC for the project or use -fobjc-arc flag
#endif
#import "SVProgressHUD.h"
#import "SVIndefiniteAnimatedView.h"
#import <QuartzCore/QuartzCore.h>
NSString * const SVProgressHUDDidReceiveTouchEventNotification = @"SVProgressHUDDidReceiveTouchEventNotification";
NSString * const SVProgressHUDDidTouchDownInsideNotification = @"SVProgressHUDDidTouchDownInsideNotification";
NSString * const SVProgressHUDWillDisappearNotification = @"SVProgressHUDWillDisappearNotification";
NSString * const SVProgressHUDDidDisappearNotification = @"SVProgressHUDDidDisappearNotification";
NSString * const SVProgressHUDWillAppearNotification = @"SVProgressHUDWillAppearNotification";
NSString * const SVProgressHUDDidAppearNotification = @"SVProgressHUDDidAppearNotification";
NSString * const SVProgressHUDStatusUserInfoKey = @"SVProgressHUDStatusUserInfoKey";
static UIColor *SVProgressHUDBackgroundColor;
static UIColor *SVProgressHUDForegroundColor;
static UIColor *SVProgressHUDBackgroundColor2;
static UIColor *SVProgressHUDForegroundColor2;
static CGFloat SVProgressHUDRingThickness;
static UIFont *SVProgressHUDFont;
static UIImage *SVProgressHUDInfoImage;
static UIImage *SVProgressHUDSuccessImage;
static UIImage *SVProgressHUDErrorImage;
static SVProgressHUDMaskType SVProgressHUDDefaultMaskType;
static UIView *SVProgressHUDExtensionView;
static const CGFloat SVProgressHUDRingRadius = 18;
static const CGFloat SVProgressHUDRingNoTextRadius = 24;
static const CGFloat SVProgressHUDParallaxDepthPoints = 10;
static const CGFloat SVProgressHUDUndefinedProgress = -1;
@interface SVProgressHUD ()
@property (nonatomic, readwrite) SVProgressHUDMaskType maskType;
@property (nonatomic, strong, readonly) NSTimer *fadeOutTimer;
@property (nonatomic, readonly, getter = isClear) BOOL clear;
@property (nonatomic, strong) UIControl *overlayView;
@property (nonatomic, strong) UIView *hudView;
@property (nonatomic, strong) UILabel *stringLabel;
@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, strong) SVIndefiniteAnimatedView *indefiniteAnimatedView;
@property (nonatomic, readwrite) CGFloat progress;
@property (nonatomic, readwrite) NSUInteger activityCount;
@property (nonatomic, strong) CAShapeLayer *backgroundRingLayer;
@property (nonatomic, strong) CAShapeLayer *ringLayer;
@property (nonatomic, readonly) CGFloat visibleKeyboardHeight;
@property (nonatomic, assign) UIOffset offsetFromCenter;
@property BOOL is2;
- (void)showProgress:(float)progress status:(NSString*)string maskType:(SVProgressHUDMaskType)hudMaskType;
- (void)showImage:(UIImage*)image status:(NSString*)status duration:(NSTimeInterval)duration maskType:(SVProgressHUDMaskType)hudMaskType;
- (void)dismiss;
- (void)setStatus:(NSString*)string;
- (void)registerNotifications;
- (NSDictionary *)notificationUserInfo;
- (void)moveToPoint:(CGPoint)newCenter rotateAngle:(CGFloat)angle;
- (void)positionHUD:(NSNotification*)notification;
- (NSTimeInterval)displayDurationForString:(NSString*)string;
@end
@implementation SVProgressHUD
+ (SVProgressHUD*)sharedView {
static dispatch_once_t once;
static SVProgressHUD *sharedView;
dispatch_once(&once, ^ { sharedView = [[self alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; });
return sharedView;
}
// for progress loading
+ (SVProgressHUD*)sharedView2 {
static dispatch_once_t once2;
static SVProgressHUD *sharedView2;
dispatch_once(&once2, ^ { sharedView2 = [[self alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; });
sharedView2.is2 = YES;
return sharedView2;
}
#pragma mark - Setters
+ (void)setStatus:(NSString *)string {
[[self sharedView] setStatus:string];
}
+ (void)setBackgroundColor:(UIColor *)color {
[self sharedView].hudView.backgroundColor = color;
SVProgressHUDBackgroundColor = color;
}
+ (void)setForegroundColor:(UIColor *)color {
[self sharedView];
SVProgressHUDForegroundColor = color;
}
// for progress loading
+ (void)setBackgroundColor2:(UIColor *)color {
[self sharedView2].hudView.backgroundColor = color;
SVProgressHUDBackgroundColor2 = color;
}
+ (void)setForegroundColor2:(UIColor *)color {
[self sharedView2];
SVProgressHUDForegroundColor2 = color;
}
+ (void)setFont:(UIFont *)font {
[self sharedView];
SVProgressHUDFont = font;
}
+ (void)setRingThickness:(CGFloat)width {
[self sharedView];
SVProgressHUDRingThickness = width;
}
+ (void)setInfoImage:(UIImage*)image{
[self sharedView];
SVProgressHUDInfoImage = image;
}
+ (void)setSuccessImage:(UIImage *)image {
[self sharedView];
SVProgressHUDSuccessImage = image;
}
+ (void)setErrorImage:(UIImage *)image {
[self sharedView];
SVProgressHUDErrorImage = image;
}
+ (void)setDefaultMaskType:(SVProgressHUDMaskType)maskType{
[self sharedView];
SVProgressHUDDefaultMaskType = maskType;
}
+ (void)setViewForExtension:(UIView *)view{
[self sharedView];
SVProgressHUDExtensionView = view;
}
#pragma mark - Show Methods
// loading
+ (void)show {
// [self showWithStatus:nil];
[self sharedView2];
[self showProgress:SVProgressHUDUndefinedProgress status:nil maskType:SVProgressHUDDefaultMaskType use2:YES];
}
+ (void)showWithMaskType:(SVProgressHUDMaskType)maskType {
[self showProgress:SVProgressHUDUndefinedProgress maskType:maskType];
}
+ (void)showWithStatus:(NSString *)status {
[self showProgress:SVProgressHUDUndefinedProgress status:status];
}
+ (void)showWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType {
[self showProgress:SVProgressHUDUndefinedProgress status:status maskType:maskType];
}
// 用2
+ (void)showProgress:(float)progress {
[self sharedView2];
// [self showProgress:progress maskType:SVProgressHUDDefaultMaskType];
[self showProgress:progress status:nil maskType:SVProgressHUDDefaultMaskType use2:YES];
}
+ (void)showProgress:(float)progress maskType:(SVProgressHUDMaskType)maskType{
[self showProgress:progress status:nil maskType:maskType use2:NO];
}
// 用1
+ (void)showProgress:(float)progress status:(NSString *)status {
[self sharedView];
[self showProgress:progress status:status maskType:SVProgressHUDDefaultMaskType use2:NO];
}
+ (void)showProgress:(float)progress status:(NSString *)status maskType:(SVProgressHUDMaskType)maskType use2:(BOOL)use2 {
if(!use2) {
[[self sharedView] showProgress:progress status:status maskType:maskType];
}
else {
[[self sharedView2] showProgress:progress status:status maskType:maskType];
}
}
#pragma mark - Show then dismiss methods
+ (void)showInfoWithStatus:(NSString *)string {
[self sharedView];
[self showInfoWithStatus:string maskType:SVProgressHUDDefaultMaskType];
}
+ (void)showInfoWithStatus:(NSString *)string maskType:(SVProgressHUDMaskType)maskType {
[self sharedView];
[self showImage:SVProgressHUDInfoImage status:string maskType:maskType];
}
+ (void)showSuccessWithStatus:(NSString *)string {
[self sharedView];
[self showSuccessWithStatus:string maskType:SVProgressHUDDefaultMaskType];
}
+ (void)showSuccessWithStatus:(NSString *)string maskType:(SVProgressHUDMaskType)maskType {
[self sharedView];
[self showImage:SVProgressHUDSuccessImage status:string maskType:maskType];
}
+ (void)showErrorWithStatus:(NSString *)string {
[self sharedView];
[self showErrorWithStatus:string maskType:SVProgressHUDDefaultMaskType];
}
+ (void)showErrorWithStatus:(NSString *)string maskType:(SVProgressHUDMaskType)maskType {
[self sharedView];
[self showImage:SVProgressHUDErrorImage status:string maskType:maskType];
}
+ (void)showImage:(UIImage *)image status:(NSString *)string {
[self sharedView];
[self showImage:image status:string maskType:SVProgressHUDDefaultMaskType];
}
+ (void)showImage:(UIImage *)image status:(NSString *)string maskType:(SVProgressHUDMaskType)maskType {
NSTimeInterval displayInterval = [[self sharedView] displayDurationForString:string];
[[self sharedView] showImage:image status:string duration:displayInterval maskType:maskType];
}
#pragma mark - Dismiss Methods
+ (void)popActivity {
if([self sharedView].activityCount > 0)
[self sharedView].activityCount--;
if([self sharedView].activityCount == 0)
[[self sharedView] dismiss];
}
+ (void)dismiss {
if ([self isVisible]) {
[[self sharedView] dismiss];
}
if ([self isVisible2]) {
[[self sharedView2] dismiss];
}
}
#pragma mark - Offset
+ (void)setOffsetFromCenter:(UIOffset)offset {
[self sharedView].offsetFromCenter = offset;
}
+ (void)resetOffsetFromCenter {
[self setOffsetFromCenter:UIOffsetZero];
}
#pragma mark - Instance Methods
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
self.userInteractionEnabled = NO;
self.backgroundColor = [UIColor clearColor];
self.alpha = 0.0f;
self.activityCount = 0;
// SVProgressHUDBackgroundColor = [UIColor whiteColor];
// SVProgressHUDForegroundColor = [UIColor blackColor];
if ([UIFont respondsToSelector:@selector(preferredFontForTextStyle:)]) {
SVProgressHUDFont = [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
} else {
SVProgressHUDFont = [UIFont systemFontOfSize:14.0f];
// SVProgressHUDBackgroundColor = [UIColor colorWithWhite:0.0f alpha:0.8f];
// SVProgressHUDForegroundColor = [UIColor whiteColor];
}
NSBundle *bundle = [NSBundle bundleForClass:self.class];
NSURL *url = [bundle URLForResource:@"SVProgressHUD" withExtension:@"bundle"];
NSBundle *imageBundle = [NSBundle bundleWithURL:url];
UIImage* infoImage = [UIImage imageWithContentsOfFile:[imageBundle pathForResource:@"info" ofType:@"png"]];
UIImage* successImage = [UIImage imageWithContentsOfFile:[imageBundle pathForResource:@"success" ofType:@"png"]];
UIImage* errorImage = [UIImage imageWithContentsOfFile:[imageBundle pathForResource:@"error" ofType:@"png"]];
if ([[UIImage class] instancesRespondToSelector:@selector(imageWithRenderingMode:)]) {
SVProgressHUDInfoImage = [infoImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
// SVProgressHUDSuccessImage = [successImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
// SVProgressHUDErrorImage = [errorImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
} else {
SVProgressHUDInfoImage = infoImage;
// SVProgressHUDSuccessImage = successImage;
// SVProgressHUDErrorImage = errorImage;
}
SVProgressHUDRingThickness = 2;
SVProgressHUDDefaultMaskType = SVProgressHUDMaskTypeNone;
}
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);
CGFloat freeHeight = CGRectGetHeight(self.bounds) - self.visibleKeyboardHeight;
CGPoint center = CGPointMake(CGRectGetWidth(self.bounds)/2, freeHeight/2);
float radius = MIN(CGRectGetWidth(self.bounds) , CGRectGetHeight(self.bounds)) ;
CGContextDrawRadialGradient (context, gradient, center, 0, center, radius, kCGGradientDrawsAfterEndLocation);
CGGradientRelease(gradient);
break;
}
default:
break;
}
}
- (void)updatePosition {
CGFloat hudWidth = 100.0f;
CGFloat hudHeight = 100.0f;
CGFloat stringHeightBuffer = 20.0f;
CGFloat stringAndContentHeightBuffer = 80.0f;
CGFloat stringWidth = 0.0f;
CGFloat stringHeight = 0.0f;
CGRect labelRect = CGRectZero;
NSString *string = self.stringLabel.text;
// Check if an image or progress ring is displayed
BOOL imageUsed = (self.imageView.image) || (self.imageView.hidden);
BOOL progressUsed = (self.progress != SVProgressHUDUndefinedProgress) && (self.progress >= 0.0f);
if(string) {
CGSize constraintSize = CGSizeMake(200.0f, 300.0f);
CGRect stringRect;
if ([string respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) {
stringRect = [string boundingRectWithSize:constraintSize
options:(NSStringDrawingUsesFontLeading|NSStringDrawingTruncatesLastVisibleLine|NSStringDrawingUsesLineFragmentOrigin)
attributes:@{NSFontAttributeName: self.stringLabel.font}
context:NULL];
} else {
CGSize stringSize;
if ([string respondsToSelector:@selector(sizeWithAttributes:)])
stringSize = [string sizeWithAttributes:@{NSFontAttributeName:[UIFont fontWithName:self.stringLabel.font.fontName size:self.stringLabel.font.pointSize]}];
else
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
stringSize = [string sizeWithFont:self.stringLabel.font constrainedToSize:CGSizeMake(200.0f, 300.0f)];
#pragma clang diagnostic pop
stringRect = CGRectMake(0.0f, 0.0f, stringSize.width, stringSize.height);
}
stringWidth = stringRect.size.width;
stringHeight = ceil(CGRectGetHeight(stringRect));
if (imageUsed || progressUsed)
hudHeight = stringAndContentHeightBuffer + stringHeight;
else
hudHeight = stringHeightBuffer + stringHeight;
if(stringWidth > hudWidth)
hudWidth = ceil(stringWidth/2)*2;
CGFloat labelRectY = (imageUsed || progressUsed) ? 68.0f : 9.0f;
if(hudHeight > 100.0f) {
labelRect = CGRectMake(12.0f, labelRectY, hudWidth, stringHeight);
hudWidth += 24.0f;
} else {
hudWidth += 24.0f;
labelRect = CGRectMake(0.0f, labelRectY, hudWidth, stringHeight);
}
}
self.hudView.bounds = CGRectMake(0.0f, 0.0f, hudWidth, hudHeight);
if(string)
self.imageView.center = CGPointMake(CGRectGetWidth(self.hudView.bounds)/2, 36.0f);
else
self.imageView.center = CGPointMake(CGRectGetWidth(self.hudView.bounds)/2, CGRectGetHeight(self.hudView.bounds)/2);
self.stringLabel.hidden = NO;
self.stringLabel.frame = labelRect;
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
if(string) {
self.indefiniteAnimatedView.radius = SVProgressHUDRingRadius;
[self.indefiniteAnimatedView sizeToFit];
CGPoint center = CGPointMake((CGRectGetWidth(self.hudView.bounds)/2), 36.0f);
self.indefiniteAnimatedView.center = center;
if(self.progress != SVProgressHUDUndefinedProgress)
self.backgroundRingLayer.position = self.ringLayer.position = CGPointMake((CGRectGetWidth(self.hudView.bounds)/2), 36.0f);
} else {
self.indefiniteAnimatedView.radius = SVProgressHUDRingNoTextRadius;
[self.indefiniteAnimatedView sizeToFit];
CGPoint center = CGPointMake((CGRectGetWidth(self.hudView.bounds)/2), CGRectGetHeight(self.hudView.bounds)/2);
self.indefiniteAnimatedView.center = center;
if(self.progress != SVProgressHUDUndefinedProgress)
self.backgroundRingLayer.position = self.ringLayer.position = CGPointMake((CGRectGetWidth(self.hudView.bounds)/2), CGRectGetHeight(self.hudView.bounds)/2);
}
[CATransaction commit];
}
- (void)setStatus:(NSString *)string {
self.stringLabel.text = string;
[self updatePosition];
}
- (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];
}
- (NSDictionary *)notificationUserInfo{
return (self.stringLabel.text ? @{SVProgressHUDStatusUserInfoKey : self.stringLabel.text} : nil);
}
- (void)positionHUD:(NSNotification*)notification {
CGFloat keyboardHeight = 0.0f;
double animationDuration = 0.0;
self.frame = UIScreen.mainScreen.bounds;
#if !defined(SV_APP_EXTENSIONS)
UIInterfaceOrientation orientation = UIApplication.sharedApplication.statusBarOrientation;
#else
UIInterfaceOrientation orientation = CGRectGetWidth(self.frame) > CGRectGetHeight(self.frame) ? UIInterfaceOrientationLandscapeLeft : UIInterfaceOrientationPortrait;
#endif
// no transforms applied to window in iOS 8, but only if compiled with iOS 8 sdk as base sdk, otherwise system supports old rotation logic.
BOOL ignoreOrientation = NO;
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
if ([[NSProcessInfo processInfo] respondsToSelector:@selector(operatingSystemVersion)]) {
ignoreOrientation = YES;
}
#endif
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(ignoreOrientation || UIInterfaceOrientationIsPortrait(orientation))
keyboardHeight = CGRectGetHeight(keyboardFrame);
else
keyboardHeight = CGRectGetWidth(keyboardFrame);
}
} else {
keyboardHeight = self.visibleKeyboardHeight;
}
CGRect orientationFrame = self.bounds;
#if !defined(SV_APP_EXTENSIONS)
CGRect statusBarFrame = UIApplication.sharedApplication.statusBarFrame;
#else
CGRect statusBarFrame = CGRectZero;
#endif
if(!ignoreOrientation && UIInterfaceOrientationIsLandscape(orientation)) {
float temp = CGRectGetWidth(orientationFrame);
orientationFrame.size.width = CGRectGetHeight(orientationFrame);
orientationFrame.size.height = temp;
temp = CGRectGetWidth(statusBarFrame);
statusBarFrame.size.width = CGRectGetHeight(statusBarFrame);
statusBarFrame.size.height = temp;
}
CGFloat activeHeight = CGRectGetHeight(orientationFrame);
if(keyboardHeight > 0)
activeHeight += CGRectGetHeight(statusBarFrame)*2;
activeHeight -= keyboardHeight;
CGFloat posY = floor(activeHeight*0.45);
CGFloat posX = CGRectGetWidth(orientationFrame)/2;
CGPoint newCenter;
CGFloat rotateAngle;
if (ignoreOrientation) {
rotateAngle = 0.0;
newCenter = CGPointMake(posX, posY);
} else {
switch (orientation) {
case UIInterfaceOrientationPortraitUpsideDown:
rotateAngle = M_PI;
newCenter = CGPointMake(posX, CGRectGetHeight(orientationFrame)-posY);
break;
case UIInterfaceOrientationLandscapeLeft:
rotateAngle = -M_PI/2.0f;
newCenter = CGPointMake(posY, posX);
break;
case UIInterfaceOrientationLandscapeRight:
rotateAngle = M_PI/2.0f;
newCenter = CGPointMake(CGRectGetHeight(orientationFrame)-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];
[self.hudView setNeedsDisplay];
} completion:NULL];
} else {
[self moveToPoint:newCenter rotateAngle:rotateAngle];
[self.hudView setNeedsDisplay];
}
}
- (void)moveToPoint:(CGPoint)newCenter rotateAngle:(CGFloat)angle {
self.hudView.transform = CGAffineTransformMakeRotation(angle);
self.hudView.center = CGPointMake(newCenter.x + self.offsetFromCenter.horizontal, newCenter.y + self.offsetFromCenter.vertical);
}
- (void)overlayViewDidReceiveTouchEvent:(id)sender forEvent:(UIEvent *)event {
[[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidReceiveTouchEventNotification object:event];
UITouch *touch = event.allTouches.anyObject;
CGPoint touchLocation = [touch locationInView:self];
if (CGRectContainsPoint(self.hudView.frame, touchLocation)) {
[[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidTouchDownInsideNotification object:event];
}
}
#pragma mark - Master show/dismiss methods
- (void)showProgress:(float)progress status:(NSString*)string maskType:(SVProgressHUDMaskType)hudMaskType {
if(!self.overlayView.superview){
#if !defined(SV_APP_EXTENSIONS)
NSEnumerator *frontToBackWindows = [UIApplication.sharedApplication.windows reverseObjectEnumerator];
for (UIWindow *window in frontToBackWindows){
BOOL windowOnMainScreen = window.screen == UIScreen.mainScreen;
BOOL windowIsVisible = !window.hidden && window.alpha > 0;
BOOL windowLevelNormal = window.windowLevel == UIWindowLevelNormal;
if (windowOnMainScreen && windowIsVisible && windowLevelNormal) {
[window addSubview:self.overlayView];
break;
}
}
#else
if(SVProgressHUDExtensionView){
[SVProgressHUDExtensionView addSubview:self.overlayView];
}
#endif
} else {
// Ensure that overlay will be exactly on top of rootViewController (which may be changed during runtime).
[self.overlayView.superview bringSubviewToFront:self.overlayView];
}
if(!self.superview)
[self.overlayView addSubview:self];
self.fadeOutTimer = nil;
self.imageView.hidden = YES;
self.maskType = hudMaskType;
self.progress = progress;
self.stringLabel.text = string;
[self updatePosition];
if(progress >= 0) {
self.imageView.image = nil;
self.imageView.hidden = NO;
[self.indefiniteAnimatedView removeFromSuperview];
self.ringLayer.strokeEnd = progress;
if(progress == 0)
self.activityCount++;
} else {
self.activityCount++;
[self cancelRingLayerAnimation];
[self.hudView addSubview:self.indefiniteAnimatedView];
}
if(self.maskType != SVProgressHUDMaskTypeNone) {
self.overlayView.userInteractionEnabled = YES;
self.accessibilityLabel = string;
self.isAccessibilityElement = YES;
} else {
self.overlayView.userInteractionEnabled = NO;
self.hudView.accessibilityLabel = string;
self.hudView.isAccessibilityElement = YES;
}
[self.overlayView setHidden:NO];
self.overlayView.backgroundColor = [UIColor clearColor];
[self positionHUD:nil];
if(self.alpha != 1 || self.hudView.alpha != 1) {
NSDictionary *userInfo = [self notificationUserInfo];
[[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDWillAppearNotification
object:nil
userInfo:userInfo];
[self registerNotifications];
self.hudView.transform = CGAffineTransformScale(self.hudView.transform, 1.3, 1.3);
if(self.isClear) {
self.alpha = 1;
self.hudView.alpha = 0;
}
[UIView animateWithDuration:0.15
delay:0
options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.hudView.transform = CGAffineTransformScale(self.hudView.transform, 1/1.3, 1/1.3);
if(self.isClear) // handle iOS 7 and 8 UIToolbar which not answers well to hierarchy opacity change
self.hudView.alpha = 1;
else
self.alpha = 1;
}
completion:^(BOOL finished){
[[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidAppearNotification
object:nil
userInfo:userInfo];
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, string);
}];
[self setNeedsDisplay];
}
}
- (UIImage *)image:(UIImage *)image withTintColor:(UIColor *)color{
CGRect rect = CGRectMake(0.0f, 0.0f, image.size.width, image.size.height);
UIGraphicsBeginImageContextWithOptions(rect.size, NO, image.scale);
CGContextRef c = UIGraphicsGetCurrentContext();
[image drawInRect:rect];
CGContextSetFillColorWithColor(c, [color CGColor]);
CGContextSetBlendMode(c, kCGBlendModeSourceAtop);
CGContextFillRect(c, rect);
UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return tintedImage;
}
- (void)showImage:(UIImage *)image status:(NSString *)string duration:(NSTimeInterval)duration maskType:(SVProgressHUDMaskType)hudMaskType {
self.progress = SVProgressHUDUndefinedProgress;
self.maskType = hudMaskType;
[self cancelRingLayerAnimation];
if(![self.class isVisible])
[self.class showWithMaskType:self.maskType];
if ([self.imageView respondsToSelector:@selector(setTintColor:)]) {
self.imageView.tintColor = self.is2 ? SVProgressHUDForegroundColor2 : SVProgressHUDForegroundColor ;
} else {
image = [self image:image withTintColor:self.is2 ? SVProgressHUDForegroundColor2 : SVProgressHUDForegroundColor ];
}
self.imageView.image = image;
self.imageView.hidden = NO;
self.stringLabel.text = string;
[self updatePosition];
[self.indefiniteAnimatedView removeFromSuperview];
if(self.maskType != SVProgressHUDMaskTypeNone) {
self.overlayView.userInteractionEnabled = YES;
self.accessibilityLabel = string;
self.isAccessibilityElement = YES;
} else {
self.overlayView.userInteractionEnabled = NO;
self.hudView.accessibilityLabel = string;
self.hudView.isAccessibilityElement = YES;
}
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, string);
self.fadeOutTimer = [NSTimer timerWithTimeInterval:duration target:self selector:@selector(dismiss) userInfo:nil repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:self.fadeOutTimer forMode:NSRunLoopCommonModes];
}
- (void)dismiss {
NSDictionary *userInfo = [self notificationUserInfo];
[[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDWillDisappearNotification
object:nil
userInfo:userInfo];
self.activityCount = 0;
[UIView animateWithDuration:0.15
delay:0
options:UIViewAnimationCurveEaseIn | UIViewAnimationOptionAllowUserInteraction
animations:^{
self.hudView.transform = CGAffineTransformScale(self.hudView.transform, 0.8f, 0.8f);
if(self.isClear) // handle iOS 7 UIToolbar not answer well to hierarchy opacity change
self.hudView.alpha = 0.0f;
else
self.alpha = 0.0f;
}
completion:^(BOOL finished){
if(self.alpha == 0.0f || self.hudView.alpha == 0.0f) {
self.alpha = 0.0f;
self.hudView.alpha = 0.0f;
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self cancelRingLayerAnimation];
[_hudView removeFromSuperview];
_hudView = nil;
[_overlayView removeFromSuperview];
_overlayView = nil;
[_indefiniteAnimatedView removeFromSuperview];
_indefiniteAnimatedView = nil;
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);
[[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidDisappearNotification
object:nil
userInfo:userInfo];
// Tell the rootViewController to update the StatusBar appearance
#if !defined(SV_APP_EXTENSIONS)
UIViewController *rootController = [[UIApplication sharedApplication] keyWindow].rootViewController;
if ([rootController respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) {
[rootController setNeedsStatusBarAppearanceUpdate];
}
#endif
// uncomment to make sure UIWindow is gone from app.windows
//NSLog(@"%@", [UIApplication sharedApplication].windows);
//NSLog(@"keyWindow = %@", [UIApplication sharedApplication].keyWindow);
}
}];
}
#pragma mark - Ring progress animation
- (SVIndefiniteAnimatedView *)indefiniteAnimatedView {
if (_indefiniteAnimatedView == nil) {
_indefiniteAnimatedView = [[SVIndefiniteAnimatedView alloc] initWithFrame:CGRectZero];
_indefiniteAnimatedView.strokeThickness = SVProgressHUDRingThickness;
_indefiniteAnimatedView.strokeColor = self.is2 ? SVProgressHUDForegroundColor2 : SVProgressHUDForegroundColor ;
_indefiniteAnimatedView.radius = self.stringLabel.text ? SVProgressHUDRingRadius : SVProgressHUDRingNoTextRadius;
[_indefiniteAnimatedView sizeToFit];
}
return _indefiniteAnimatedView;
}
- (CAShapeLayer *)ringLayer {
if(!_ringLayer) {
CGPoint center = CGPointMake(CGRectGetWidth(_hudView.frame)/2, CGRectGetHeight(_hudView.frame)/2);
_ringLayer = [self createRingLayerWithCenter:center
radius:SVProgressHUDRingRadius
lineWidth:SVProgressHUDRingThickness
color:self.is2 ? SVProgressHUDForegroundColor2 : SVProgressHUDForegroundColor ];
[self.hudView.layer addSublayer:_ringLayer];
}
return _ringLayer;
}
- (CAShapeLayer *)backgroundRingLayer {
if(!_backgroundRingLayer) {
CGPoint center = CGPointMake(CGRectGetWidth(_hudView.frame)/2, CGRectGetHeight(_hudView.frame)/2);
_backgroundRingLayer = [self createRingLayerWithCenter:center
radius:SVProgressHUDRingRadius
lineWidth:SVProgressHUDRingThickness
color:[self.is2 ? SVProgressHUDForegroundColor2 : SVProgressHUDForegroundColor colorWithAlphaComponent:0.1f]];
_backgroundRingLayer.strokeEnd = 1;
[self.hudView.layer addSublayer:_backgroundRingLayer];
}
return _backgroundRingLayer;
}
- (void)cancelRingLayerAnimation {
[CATransaction begin];
[CATransaction setDisableActions:YES];
[_hudView.layer removeAllAnimations];
_ringLayer.strokeEnd = 0.0f;
if (_ringLayer.superlayer) {
[_ringLayer removeFromSuperlayer];
}
_ringLayer = nil;
if (_backgroundRingLayer.superlayer) {
[_backgroundRingLayer removeFromSuperlayer];
}
_backgroundRingLayer = nil;
[CATransaction commit];
}
- (CAShapeLayer *)createRingLayerWithCenter:(CGPoint)center radius:(CGFloat)radius lineWidth:(CGFloat)lineWidth color:(UIColor *)color {
UIBezierPath* smoothedPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(radius, radius) radius:radius startAngle:-M_PI_2 endAngle:(M_PI + M_PI_2) clockwise:YES];
CAShapeLayer *slice = [CAShapeLayer layer];
slice.contentsScale = [[UIScreen mainScreen] scale];
slice.frame = CGRectMake(center.x-radius, center.y-radius, radius*2, radius*2);
slice.fillColor = [UIColor clearColor].CGColor;
slice.strokeColor = color.CGColor;
slice.lineWidth = lineWidth;
slice.lineCap = kCALineCapRound;
slice.lineJoin = kCALineJoinBevel;
slice.path = smoothedPath.CGPath;
return slice;
}
#pragma mark - Utilities
+ (BOOL)isVisible {
return ([self sharedView].alpha == 1);
}
+ (BOOL)isVisible2 {
return ([self sharedView2].alpha == 1);
}
#pragma mark - Getters
- (NSTimeInterval)displayDurationForString:(NSString*)string {
return MIN((float)string.length*0.06 + 0.5, 5.0);
}
- (BOOL)isClear { // used for iOS 7 and above
return (self.maskType == SVProgressHUDMaskTypeClear || self.maskType == SVProgressHUDMaskTypeNone);
}
- (UIControl *)overlayView {
if(!_overlayView) {
_overlayView = [[UIControl alloc] initWithFrame:[UIScreen mainScreen].bounds];
_overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_overlayView.backgroundColor = [UIColor clearColor];
[_overlayView addTarget:self action:@selector(overlayViewDidReceiveTouchEvent:forEvent:) forControlEvents:UIControlEventTouchDown];
}
return _overlayView;
}
- (UIView *)hudView {
if(!_hudView) {
_hudView = [[UIView alloc] initWithFrame:CGRectZero];
_hudView.backgroundColor = self.is2 ? SVProgressHUDBackgroundColor2 : SVProgressHUDBackgroundColor;
_hudView.layer.cornerRadius = 14;
_hudView.layer.masksToBounds = YES;
_hudView.autoresizingMask = (UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin);
if ([_hudView respondsToSelector:@selector(addMotionEffect:)]) {
UIInterpolatingMotionEffect *effectX = [[UIInterpolatingMotionEffect alloc] initWithKeyPath: @"center.x" type: UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
effectX.minimumRelativeValue = @(-SVProgressHUDParallaxDepthPoints);
effectX.maximumRelativeValue = @(SVProgressHUDParallaxDepthPoints);
UIInterpolatingMotionEffect *effectY = [[UIInterpolatingMotionEffect alloc] initWithKeyPath: @"center.y" type: UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
effectY.minimumRelativeValue = @(-SVProgressHUDParallaxDepthPoints);
effectY.maximumRelativeValue = @(SVProgressHUDParallaxDepthPoints);
UIMotionEffectGroup *effectGroup = [[UIMotionEffectGroup alloc] init];
effectGroup.motionEffects = @[effectX, effectY];
[_hudView addMotionEffect:effectGroup];
}
}
if(!_hudView.superview)
[self addSubview:_hudView];
return _hudView;
}
- (UILabel *)stringLabel {
if (!_stringLabel) {
_stringLabel = [[UILabel alloc] initWithFrame:CGRectZero];
_stringLabel.backgroundColor = [UIColor clearColor];
_stringLabel.adjustsFontSizeToFitWidth = YES;
_stringLabel.textAlignment = NSTextAlignmentCenter;
_stringLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
_stringLabel.numberOfLines = 0;
}
if(!_stringLabel.superview)
[self.hudView addSubview:_stringLabel];
_stringLabel.textColor = self.is2 ? SVProgressHUDForegroundColor2 : SVProgressHUDForegroundColor;
_stringLabel.font = SVProgressHUDFont;
return _stringLabel;
}
- (UIImageView *)imageView {
if (!_imageView)
_imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 28.0f, 28.0f)];
if(!_imageView.superview)
[self.hudView addSubview:_imageView];
return _imageView;
}
- (CGFloat)visibleKeyboardHeight {
#if !defined(SV_APP_EXTENSIONS)
UIWindow *keyboardWindow = nil;
for (UIWindow *testWindow in [[UIApplication sharedApplication] windows]) {
if(![[testWindow class] isEqual:[UIWindow class]]) {
keyboardWindow = testWindow;
break;
}
}
for (__strong UIView *possibleKeyboard in [keyboardWindow subviews]) {
if ([possibleKeyboard isKindOfClass:NSClassFromString(@"UIPeripheralHostView")] || [possibleKeyboard isKindOfClass:NSClassFromString(@"UIKeyboard")]) {
return CGRectGetHeight(possibleKeyboard.bounds);
} else if ([possibleKeyboard isKindOfClass:NSClassFromString(@"UIInputSetContainerView")]) {
for (__strong UIView *possibleKeyboardSubview in [possibleKeyboard subviews]) {
if ([possibleKeyboardSubview isKindOfClass:NSClassFromString(@"UIInputSetHostView")]) {
return CGRectGetHeight(possibleKeyboardSubview.bounds);
}
}
}
}
#endif
return 0;
}
@end