SVProgressHUD.m 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967
  1. //
  2. // SVProgressHUD.m
  3. //
  4. // Created by Sam Vermette on 27.03.11.
  5. // Copyright 2011 Sam Vermette. All rights reserved.
  6. //
  7. // https://github.com/samvermette/SVProgressHUD
  8. //
  9. #if !__has_feature(objc_arc)
  10. #error SVProgressHUD is ARC only. Either turn on ARC for the project or use -fobjc-arc flag
  11. #endif
  12. #define IS_IOS7 [[[UIDevice currentDevice] systemVersion] hasPrefix:@"7"]
  13. #import "SVProgressHUD.h"
  14. #import <QuartzCore/QuartzCore.h>
  15. NSString * const SVProgressHUDDidReceiveTouchEventNotification = @"SVProgressHUDDidReceiveTouchEventNotification";
  16. NSString * const SVProgressHUDWillDisappearNotification = @"SVProgressHUDWillDisappearNotification";
  17. NSString * const SVProgressHUDDidDisappearNotification = @"SVProgressHUDDidDisappearNotification";
  18. NSString * const SVProgressHUDWillAppearNotification = @"SVProgressHUDWillAppearNotification";
  19. NSString * const SVProgressHUDDidAppearNotification = @"SVProgressHUDDidAppearNotification";
  20. NSString * const SVProgressHUDStatusUserInfoKey = @"SVProgressHUDStatusUserInfoKey";
  21. static UIColor *SVProgressHUDBackgroundColor;
  22. static UIColor *SVProgressHUDForegroundColor;
  23. static CGFloat SVProgressHUDRingThickness;
  24. static UIFont *SVProgressHUDFont;
  25. static UIImage *SVProgressHUDSuccessImage;
  26. static UIImage *SVProgressHUDErrorImage;
  27. static const CGFloat SVProgressHUDRingRadius = 18;
  28. static const CGFloat SVProgressHUDRingNoTextRadius = 24;
  29. static const CGFloat SVProgressHUDParallaxDepthPoints = 10;
  30. @interface SVProgressHUD ()
  31. @property (nonatomic, readwrite) SVProgressHUDMaskType maskType;
  32. @property (nonatomic, strong, readonly) NSTimer *fadeOutTimer;
  33. @property (nonatomic, readonly, getter = isClear) BOOL clear;
  34. @property (nonatomic, strong) UIControl *overlayView;
  35. @property (nonatomic, strong) UIView *hudView;
  36. @property (nonatomic, strong) UILabel *stringLabel;
  37. @property (nonatomic, strong) UIImageView *imageView;
  38. @property (nonatomic, strong) SVIndefiniteAnimatedView *indefiniteAnimatedView;
  39. @property (nonatomic, readwrite) CGFloat progress;
  40. @property (nonatomic, readwrite) NSUInteger activityCount;
  41. @property (nonatomic, strong) CAShapeLayer *backgroundRingLayer;
  42. @property (nonatomic, strong) CAShapeLayer *ringLayer;
  43. @property (nonatomic, readonly) CGFloat visibleKeyboardHeight;
  44. @property (nonatomic, assign) UIOffset offsetFromCenter;
  45. - (void)showProgress:(float)progress
  46. status:(NSString*)string
  47. maskType:(SVProgressHUDMaskType)hudMaskType;
  48. - (void)showImage:(UIImage*)image
  49. status:(NSString*)status
  50. duration:(NSTimeInterval)duration;
  51. - (void)dismiss;
  52. - (void)setStatus:(NSString*)string;
  53. - (void)registerNotifications;
  54. - (NSDictionary *)notificationUserInfo;
  55. - (void)moveToPoint:(CGPoint)newCenter rotateAngle:(CGFloat)angle;
  56. - (void)positionHUD:(NSNotification*)notification;
  57. - (NSTimeInterval)displayDurationForString:(NSString*)string;
  58. @end
  59. @implementation SVProgressHUD
  60. + (SVProgressHUD*)sharedView {
  61. static dispatch_once_t once;
  62. static SVProgressHUD *sharedView;
  63. dispatch_once(&once, ^ { sharedView = [[self alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; });
  64. return sharedView;
  65. }
  66. #pragma mark - Setters
  67. + (void)setStatus:(NSString *)string {
  68. [[self sharedView] setStatus:string];
  69. }
  70. + (void)setBackgroundColor:(UIColor *)color {
  71. [self sharedView].hudView.backgroundColor = color;
  72. SVProgressHUDBackgroundColor = color;
  73. }
  74. + (void)setForegroundColor:(UIColor *)color {
  75. [self sharedView];
  76. SVProgressHUDForegroundColor = color;
  77. }
  78. + (void)setFont:(UIFont *)font {
  79. [self sharedView];
  80. SVProgressHUDFont = font;
  81. }
  82. + (void)setRingThickness:(CGFloat)width {
  83. [self sharedView];
  84. SVProgressHUDRingThickness = width;
  85. }
  86. + (void)setSuccessImage:(UIImage *)image {
  87. [self sharedView];
  88. SVProgressHUDSuccessImage = image;
  89. }
  90. + (void)setErrorImage:(UIImage *)image {
  91. [self sharedView];
  92. SVProgressHUDErrorImage = image;
  93. }
  94. #pragma mark - Show Methods
  95. + (void)show {
  96. [[self sharedView] showProgress:-1 status:nil maskType:SVProgressHUDMaskTypeNone];
  97. }
  98. + (void)showWithStatus:(NSString *)status {
  99. [[self sharedView] showProgress:-1 status:status maskType:SVProgressHUDMaskTypeNone];
  100. }
  101. + (void)showWithMaskType:(SVProgressHUDMaskType)maskType {
  102. [[self sharedView] showProgress:-1 status:nil maskType:maskType];
  103. }
  104. + (void)showWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType {
  105. [[self sharedView] showProgress:-1 status:status maskType:maskType];
  106. }
  107. + (void)showProgress:(float)progress {
  108. [[self sharedView] showProgress:progress status:nil maskType:SVProgressHUDMaskTypeNone];
  109. }
  110. + (void)showProgress:(float)progress status:(NSString *)status {
  111. [[self sharedView] showProgress:progress status:status maskType:SVProgressHUDMaskTypeNone];
  112. }
  113. + (void)showProgress:(float)progress status:(NSString *)status maskType:(SVProgressHUDMaskType)maskType {
  114. [[self sharedView] showProgress:progress status:status maskType:maskType];
  115. }
  116. #pragma mark - Show then dismiss methods
  117. + (void)showSuccessWithStatus:(NSString *)string {
  118. [self sharedView];
  119. [self showImage:SVProgressHUDSuccessImage status:string];
  120. }
  121. + (void)showErrorWithStatus:(NSString *)string {
  122. [self sharedView];
  123. [self showImage:SVProgressHUDErrorImage status:string];
  124. }
  125. + (void)showImage:(UIImage *)image status:(NSString *)string {
  126. NSTimeInterval displayInterval = [[SVProgressHUD sharedView] displayDurationForString:string];
  127. [[self sharedView] showImage:image status:string duration:displayInterval];
  128. }
  129. #pragma mark - Dismiss Methods
  130. + (void)popActivity {
  131. [self sharedView].activityCount--;
  132. if([self sharedView].activityCount == 0)
  133. [[self sharedView] dismiss];
  134. }
  135. + (void)dismiss {
  136. if ([self isVisible]) {
  137. [[self sharedView] dismiss];
  138. }
  139. }
  140. #pragma mark - Offset
  141. + (void)setOffsetFromCenter:(UIOffset)offset {
  142. [self sharedView].offsetFromCenter = offset;
  143. }
  144. + (void)resetOffsetFromCenter {
  145. [self setOffsetFromCenter:UIOffsetZero];
  146. }
  147. #pragma mark - Instance Methods
  148. - (id)initWithFrame:(CGRect)frame {
  149. if ((self = [super initWithFrame:frame])) {
  150. self.userInteractionEnabled = NO;
  151. self.backgroundColor = [UIColor clearColor];
  152. self.alpha = 0;
  153. self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  154. self.activityCount = 0;
  155. SVProgressHUDBackgroundColor = [UIColor whiteColor];
  156. SVProgressHUDForegroundColor = [UIColor blackColor];
  157. if (IS_IOS7) {
  158. SVProgressHUDFont = [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
  159. SVProgressHUDSuccessImage = [[UIImage imageNamed:@"SVProgressHUD.bundle/success"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
  160. SVProgressHUDErrorImage = [[UIImage imageNamed:@"SVProgressHUD.bundle/error"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
  161. }
  162. SVProgressHUDRingThickness = 4;
  163. }
  164. return self;
  165. }
  166. - (void)drawRect:(CGRect)rect {
  167. CGContextRef context = UIGraphicsGetCurrentContext();
  168. switch (self.maskType) {
  169. case SVProgressHUDMaskTypeBlack: {
  170. [[UIColor colorWithWhite:0 alpha:0.5] set];
  171. CGContextFillRect(context, self.bounds);
  172. break;
  173. }
  174. case SVProgressHUDMaskTypeGradient: {
  175. size_t locationsCount = 2;
  176. CGFloat locations[2] = {0.0f, 1.0f};
  177. CGFloat colors[8] = {0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.75f};
  178. CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  179. CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, locations, locationsCount);
  180. CGColorSpaceRelease(colorSpace);
  181. CGFloat freeHeight = self.bounds.size.height - self.visibleKeyboardHeight;
  182. CGPoint center = CGPointMake(self.bounds.size.width/2, freeHeight/2);
  183. float radius = MIN(self.bounds.size.width , self.bounds.size.height) ;
  184. CGContextDrawRadialGradient (context, gradient, center, 0, center, radius, kCGGradientDrawsAfterEndLocation);
  185. CGGradientRelease(gradient);
  186. break;
  187. }
  188. }
  189. }
  190. - (void)updatePosition {
  191. CGFloat hudWidth = 100;
  192. CGFloat hudHeight = 100;
  193. CGFloat stringHeightBuffer = 20;
  194. CGFloat stringAndImageHeightBuffer = 80;
  195. CGFloat stringWidth = 0;
  196. CGFloat stringHeight = 0;
  197. CGRect labelRect = CGRectZero;
  198. NSString *string = self.stringLabel.text;
  199. // False if it's text-only
  200. BOOL imageUsed = (self.imageView.image) || (self.imageView.hidden);
  201. if(string) {
  202. CGSize constraintSize = CGSizeMake(200, 300);
  203. CGRect stringRect = CGRectZero;
  204. if (IS_IOS7) {
  205. stringRect = [string boundingRectWithSize:constraintSize
  206. options:(NSStringDrawingUsesFontLeading|NSStringDrawingTruncatesLastVisibleLine|NSStringDrawingUsesLineFragmentOrigin)
  207. attributes:@{NSFontAttributeName: self.stringLabel.font}
  208. context:NULL];
  209. } else {
  210. CGSize stringSize = [string boundingRectWithSize:constraintSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : self.stringLabel.font} context:nil].size;
  211. stringRect = CGRectMake(0, 0, stringSize.width, stringSize.height);
  212. }
  213. stringWidth = stringRect.size.width;
  214. stringHeight = ceil(stringRect.size.height);
  215. if (imageUsed)
  216. hudHeight = stringAndImageHeightBuffer + stringHeight;
  217. else
  218. hudHeight = stringHeightBuffer + stringHeight;
  219. if(stringWidth > hudWidth)
  220. hudWidth = ceil(stringWidth/2)*2;
  221. CGFloat labelRectY = imageUsed ? 68 : 9;
  222. if(hudHeight > 100) {
  223. labelRect = CGRectMake(12, labelRectY, hudWidth, stringHeight);
  224. hudWidth+=24;
  225. } else {
  226. hudWidth+=24;
  227. labelRect = CGRectMake(0, labelRectY, hudWidth, stringHeight);
  228. }
  229. }
  230. self.hudView.bounds = CGRectMake(0, 0, hudWidth, hudHeight);
  231. if(string)
  232. self.imageView.center = CGPointMake(CGRectGetWidth(self.hudView.bounds)/2, 36);
  233. else
  234. self.imageView.center = CGPointMake(CGRectGetWidth(self.hudView.bounds)/2, CGRectGetHeight(self.hudView.bounds)/2);
  235. self.stringLabel.hidden = NO;
  236. self.stringLabel.frame = labelRect;
  237. [CATransaction begin];
  238. [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
  239. if(string) {
  240. self.indefiniteAnimatedView.radius = SVProgressHUDRingRadius;
  241. [self.indefiniteAnimatedView sizeToFit];
  242. CGPoint center = CGPointMake((CGRectGetWidth(self.hudView.bounds)/2), 36);
  243. self.indefiniteAnimatedView.center = center;
  244. if(self.progress != -1)
  245. self.backgroundRingLayer.position = self.ringLayer.position = CGPointMake((CGRectGetWidth(self.hudView.bounds)/2), 36);
  246. }
  247. else {
  248. self.indefiniteAnimatedView.radius = SVProgressHUDRingNoTextRadius;
  249. [self.indefiniteAnimatedView sizeToFit];
  250. CGPoint center = CGPointMake((CGRectGetWidth(self.hudView.bounds)/2), CGRectGetHeight(self.hudView.bounds)/2);
  251. self.indefiniteAnimatedView.center = center;
  252. if(self.progress != -1)
  253. self.backgroundRingLayer.position = self.ringLayer.position = CGPointMake((CGRectGetWidth(self.hudView.bounds)/2), CGRectGetHeight(self.hudView.bounds)/2);
  254. }
  255. [CATransaction commit];
  256. }
  257. - (void)setStatus:(NSString *)string {
  258. self.stringLabel.text = string;
  259. [self updatePosition];
  260. }
  261. - (void)setFadeOutTimer:(NSTimer *)newTimer {
  262. if(_fadeOutTimer)
  263. (void)([_fadeOutTimer invalidate]), _fadeOutTimer = nil;
  264. if(newTimer)
  265. _fadeOutTimer = newTimer;
  266. }
  267. - (void)registerNotifications {
  268. [[NSNotificationCenter defaultCenter] addObserver:self
  269. selector:@selector(positionHUD:)
  270. name:UIApplicationDidChangeStatusBarOrientationNotification
  271. object:nil];
  272. [[NSNotificationCenter defaultCenter] addObserver:self
  273. selector:@selector(positionHUD:)
  274. name:UIKeyboardWillHideNotification
  275. object:nil];
  276. [[NSNotificationCenter defaultCenter] addObserver:self
  277. selector:@selector(positionHUD:)
  278. name:UIKeyboardDidHideNotification
  279. object:nil];
  280. [[NSNotificationCenter defaultCenter] addObserver:self
  281. selector:@selector(positionHUD:)
  282. name:UIKeyboardWillShowNotification
  283. object:nil];
  284. [[NSNotificationCenter defaultCenter] addObserver:self
  285. selector:@selector(positionHUD:)
  286. name:UIKeyboardDidShowNotification
  287. object:nil];
  288. }
  289. - (NSDictionary *)notificationUserInfo
  290. {
  291. return (self.stringLabel.text ? @{SVProgressHUDStatusUserInfoKey : self.stringLabel.text} : nil);
  292. }
  293. - (void)positionHUD:(NSNotification*)notification {
  294. CGFloat keyboardHeight;
  295. double animationDuration;
  296. UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
  297. if(notification) {
  298. NSDictionary* keyboardInfo = [notification userInfo];
  299. CGRect keyboardFrame = [[keyboardInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
  300. animationDuration = [[keyboardInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
  301. if(notification.name == UIKeyboardWillShowNotification || notification.name == UIKeyboardDidShowNotification) {
  302. if(UIInterfaceOrientationIsPortrait(orientation))
  303. keyboardHeight = keyboardFrame.size.height;
  304. else
  305. keyboardHeight = keyboardFrame.size.width;
  306. } else
  307. keyboardHeight = 0;
  308. } else {
  309. keyboardHeight = self.visibleKeyboardHeight;
  310. }
  311. CGRect orientationFrame = [UIScreen mainScreen].bounds;
  312. CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
  313. if(UIInterfaceOrientationIsLandscape(orientation)) {
  314. float temp = orientationFrame.size.width;
  315. orientationFrame.size.width = orientationFrame.size.height;
  316. orientationFrame.size.height = temp;
  317. temp = statusBarFrame.size.width;
  318. statusBarFrame.size.width = statusBarFrame.size.height;
  319. statusBarFrame.size.height = temp;
  320. }
  321. CGFloat activeHeight = orientationFrame.size.height;
  322. if(keyboardHeight > 0)
  323. activeHeight += statusBarFrame.size.height*2;
  324. activeHeight -= keyboardHeight;
  325. CGFloat posY = floor(activeHeight*0.45);
  326. CGFloat posX = orientationFrame.size.width/2;
  327. CGPoint newCenter;
  328. CGFloat rotateAngle;
  329. switch (orientation) {
  330. case UIInterfaceOrientationPortraitUpsideDown:
  331. rotateAngle = M_PI;
  332. newCenter = CGPointMake(posX, orientationFrame.size.height-posY);
  333. break;
  334. case UIInterfaceOrientationLandscapeLeft:
  335. rotateAngle = -M_PI/2.0f;
  336. newCenter = CGPointMake(posY, posX);
  337. break;
  338. case UIInterfaceOrientationLandscapeRight:
  339. rotateAngle = M_PI/2.0f;
  340. newCenter = CGPointMake(orientationFrame.size.height-posY, posX);
  341. break;
  342. default: // as UIInterfaceOrientationPortrait
  343. rotateAngle = 0.0;
  344. newCenter = CGPointMake(posX, posY);
  345. break;
  346. }
  347. if(notification) {
  348. [UIView animateWithDuration:animationDuration
  349. delay:0
  350. options:UIViewAnimationOptionAllowUserInteraction
  351. animations:^{
  352. [self moveToPoint:newCenter rotateAngle:rotateAngle];
  353. } completion:NULL];
  354. }
  355. else {
  356. [self moveToPoint:newCenter rotateAngle:rotateAngle];
  357. }
  358. }
  359. - (void)moveToPoint:(CGPoint)newCenter rotateAngle:(CGFloat)angle {
  360. self.hudView.transform = CGAffineTransformMakeRotation(angle);
  361. self.hudView.center = CGPointMake(newCenter.x + self.offsetFromCenter.horizontal, newCenter.y + self.offsetFromCenter.vertical);
  362. }
  363. - (void)overlayViewDidReceiveTouchEvent:(id)sender forEvent:(UIEvent *)event {
  364. [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidReceiveTouchEventNotification object:event];
  365. }
  366. #pragma mark - Master show/dismiss methods
  367. - (void)showProgress:(float)progress status:(NSString*)string maskType:(SVProgressHUDMaskType)hudMaskType {
  368. if(!self.overlayView.superview){
  369. NSEnumerator *frontToBackWindows = [[[UIApplication sharedApplication]windows]reverseObjectEnumerator];
  370. for (UIWindow *window in frontToBackWindows)
  371. if (window.windowLevel == UIWindowLevelNormal) {
  372. [window addSubview:self.overlayView];
  373. break;
  374. }
  375. }
  376. if(!self.superview)
  377. [self.overlayView addSubview:self];
  378. self.fadeOutTimer = nil;
  379. self.imageView.hidden = YES;
  380. self.maskType = hudMaskType;
  381. self.progress = progress;
  382. self.stringLabel.text = string;
  383. [self updatePosition];
  384. if(progress >= 0) {
  385. self.imageView.image = nil;
  386. self.imageView.hidden = NO;
  387. [self.indefiniteAnimatedView removeFromSuperview];
  388. self.ringLayer.strokeEnd = progress;
  389. if(progress == 0)
  390. self.activityCount++;
  391. }
  392. else {
  393. self.activityCount++;
  394. [self cancelRingLayerAnimation];
  395. [self.hudView addSubview:self.indefiniteAnimatedView];
  396. }
  397. if(self.maskType != SVProgressHUDMaskTypeNone) {
  398. self.overlayView.userInteractionEnabled = YES;
  399. self.accessibilityLabel = string;
  400. self.isAccessibilityElement = YES;
  401. }
  402. else {
  403. self.overlayView.userInteractionEnabled = NO;
  404. self.hudView.accessibilityLabel = string;
  405. self.hudView.isAccessibilityElement = YES;
  406. }
  407. [self.overlayView setHidden:NO];
  408. self.overlayView.backgroundColor = [UIColor clearColor];
  409. [self positionHUD:nil];
  410. if(self.alpha != 1) {
  411. NSDictionary *userInfo = [self notificationUserInfo];
  412. [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDWillAppearNotification
  413. object:nil
  414. userInfo:userInfo];
  415. [self registerNotifications];
  416. self.hudView.transform = CGAffineTransformScale(self.hudView.transform, 1.3, 1.3);
  417. if(self.isClear) {
  418. self.alpha = 1;
  419. self.hudView.alpha = 0;
  420. }
  421. [UIView animateWithDuration:0.15
  422. delay:0
  423. options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState
  424. animations:^{
  425. self.hudView.transform = CGAffineTransformScale(self.hudView.transform, 1/1.3, 1/1.3);
  426. if(self.isClear) // handle iOS 7 UIToolbar not answer well to hierarchy opacity change
  427. self.hudView.alpha = 1;
  428. else
  429. self.alpha = 1;
  430. }
  431. completion:^(BOOL finished){
  432. [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidAppearNotification
  433. object:nil
  434. userInfo:userInfo];
  435. UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);
  436. UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, string);
  437. }];
  438. [self setNeedsDisplay];
  439. }
  440. }
  441. - (void)showImage:(UIImage *)image status:(NSString *)string duration:(NSTimeInterval)duration {
  442. self.progress = -1;
  443. [self cancelRingLayerAnimation];
  444. if(![self.class isVisible])
  445. [self.class show];
  446. self.imageView.tintColor = SVProgressHUDForegroundColor;
  447. self.imageView.image = image;
  448. self.imageView.hidden = NO;
  449. self.stringLabel.text = string;
  450. [self updatePosition];
  451. [self.indefiniteAnimatedView removeFromSuperview];
  452. if(self.maskType != SVProgressHUDMaskTypeNone) {
  453. self.accessibilityLabel = string;
  454. self.isAccessibilityElement = YES;
  455. } else {
  456. self.hudView.accessibilityLabel = string;
  457. self.hudView.isAccessibilityElement = YES;
  458. }
  459. UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);
  460. UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, string);
  461. self.fadeOutTimer = [NSTimer timerWithTimeInterval:duration target:self selector:@selector(dismiss) userInfo:nil repeats:NO];
  462. [[NSRunLoop mainRunLoop] addTimer:self.fadeOutTimer forMode:NSRunLoopCommonModes];
  463. }
  464. - (void)dismiss {
  465. NSDictionary *userInfo = [self notificationUserInfo];
  466. [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDWillDisappearNotification
  467. object:nil
  468. userInfo:userInfo];
  469. self.activityCount = 0;
  470. [UIView animateWithDuration:0.15
  471. delay:0
  472. options:UIViewAnimationCurveEaseIn | UIViewAnimationOptionAllowUserInteraction
  473. animations:^{
  474. self.hudView.transform = CGAffineTransformScale(self.hudView.transform, 0.8, 0.8);
  475. if(self.isClear) // handle iOS 7 UIToolbar not answer well to hierarchy opacity change
  476. self.hudView.alpha = 0;
  477. else
  478. self.alpha = 0;
  479. }
  480. completion:^(BOOL finished){
  481. if(self.alpha == 0 || self.hudView.alpha == 0) {
  482. self.alpha = 0;
  483. self.hudView.alpha = 0;
  484. [[NSNotificationCenter defaultCenter] removeObserver:self];
  485. [self cancelRingLayerAnimation];
  486. [_hudView removeFromSuperview];
  487. _hudView = nil;
  488. [_overlayView removeFromSuperview];
  489. _overlayView = nil;
  490. [_indefiniteAnimatedView removeFromSuperview];
  491. _indefiniteAnimatedView = nil;
  492. UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);
  493. [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidDisappearNotification
  494. object:nil
  495. userInfo:userInfo];
  496. // Tell the rootViewController to update the StatusBar appearance
  497. UIViewController *rootController = [[UIApplication sharedApplication] keyWindow].rootViewController;
  498. if ([rootController respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) {
  499. [rootController setNeedsStatusBarAppearanceUpdate];
  500. }
  501. // uncomment to make sure UIWindow is gone from app.windows
  502. //NSLog(@"%@", [UIApplication sharedApplication].windows);
  503. //NSLog(@"keyWindow = %@", [UIApplication sharedApplication].keyWindow);
  504. }
  505. }];
  506. }
  507. #pragma mark - Ring progress animation
  508. - (SVIndefiniteAnimatedView *)indefiniteAnimatedView {
  509. if (_indefiniteAnimatedView == nil) {
  510. _indefiniteAnimatedView = [[SVIndefiniteAnimatedView alloc] initWithFrame:CGRectZero];
  511. _indefiniteAnimatedView.radius = self.stringLabel.text ? SVProgressHUDRingRadius : SVProgressHUDRingNoTextRadius;
  512. [_indefiniteAnimatedView sizeToFit];
  513. }
  514. return _indefiniteAnimatedView;
  515. }
  516. - (CAShapeLayer *)ringLayer {
  517. if(!_ringLayer) {
  518. CGPoint center = CGPointMake(CGRectGetWidth(_hudView.frame)/2, CGRectGetHeight(_hudView.frame)/2);
  519. _ringLayer = [self createRingLayerWithCenter:center
  520. radius:SVProgressHUDRingRadius
  521. lineWidth:SVProgressHUDRingThickness
  522. color:SVProgressHUDForegroundColor];
  523. [self.hudView.layer addSublayer:_ringLayer];
  524. }
  525. return _ringLayer;
  526. }
  527. - (CAShapeLayer *)backgroundRingLayer {
  528. if(!_backgroundRingLayer) {
  529. CGPoint center = CGPointMake(CGRectGetWidth(_hudView.frame)/2, CGRectGetHeight(_hudView.frame)/2);
  530. _backgroundRingLayer = [self createRingLayerWithCenter:center
  531. radius:SVProgressHUDRingRadius
  532. lineWidth:SVProgressHUDRingThickness
  533. color:[SVProgressHUDForegroundColor colorWithAlphaComponent:0.1]];
  534. _backgroundRingLayer.strokeEnd = 1;
  535. [self.hudView.layer addSublayer:_backgroundRingLayer];
  536. }
  537. return _backgroundRingLayer;
  538. }
  539. - (void)cancelRingLayerAnimation {
  540. [CATransaction begin];
  541. [CATransaction setDisableActions:YES];
  542. [_hudView.layer removeAllAnimations];
  543. _ringLayer.strokeEnd = 0.0f;
  544. if (_ringLayer.superlayer) {
  545. [_ringLayer removeFromSuperlayer];
  546. }
  547. _ringLayer = nil;
  548. if (_backgroundRingLayer.superlayer) {
  549. [_backgroundRingLayer removeFromSuperlayer];
  550. }
  551. _backgroundRingLayer = nil;
  552. [CATransaction commit];
  553. }
  554. - (CAShapeLayer *)createRingLayerWithCenter:(CGPoint)center radius:(CGFloat)radius lineWidth:(CGFloat)lineWidth color:(UIColor *)color {
  555. UIBezierPath* smoothedPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(radius, radius) radius:radius startAngle:-M_PI_2 endAngle:(M_PI + M_PI_2) clockwise:YES];
  556. CAShapeLayer *slice = [CAShapeLayer layer];
  557. slice.contentsScale = [[UIScreen mainScreen] scale];
  558. slice.frame = CGRectMake(center.x-radius, center.y-radius, radius*2, radius*2);
  559. slice.fillColor = [UIColor clearColor].CGColor;
  560. slice.strokeColor = color.CGColor;
  561. slice.lineWidth = lineWidth;
  562. slice.lineCap = kCALineCapRound;
  563. slice.lineJoin = kCALineJoinBevel;
  564. slice.path = smoothedPath.CGPath;
  565. return slice;
  566. }
  567. #pragma mark - Utilities
  568. + (BOOL)isVisible {
  569. return ([self sharedView].alpha == 1);
  570. }
  571. #pragma mark - Getters
  572. - (NSTimeInterval)displayDurationForString:(NSString*)string {
  573. return MIN((float)string.length*0.06 + 0.3, 5.0);
  574. }
  575. - (BOOL)isClear { // used for iOS 7
  576. return (self.maskType == SVProgressHUDMaskTypeClear || self.maskType == SVProgressHUDMaskTypeNone);
  577. }
  578. - (UIControl *)overlayView {
  579. if(!_overlayView) {
  580. _overlayView = [[UIControl alloc] initWithFrame:[UIScreen mainScreen].bounds];
  581. _overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  582. _overlayView.backgroundColor = [UIColor clearColor];
  583. [_overlayView addTarget:self action:@selector(overlayViewDidReceiveTouchEvent:forEvent:) forControlEvents:UIControlEventTouchDown];
  584. }
  585. return _overlayView;
  586. }
  587. - (UIView *)hudView {
  588. if(!_hudView) {
  589. _hudView = [[UIView alloc] initWithFrame:CGRectZero];
  590. _hudView.backgroundColor = SVProgressHUDBackgroundColor;
  591. _hudView.layer.cornerRadius = 14;
  592. _hudView.layer.masksToBounds = YES;
  593. _hudView.autoresizingMask = (UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin |
  594. UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin);
  595. if (IS_IOS7) {
  596. UIInterpolatingMotionEffect *effectX = [[UIInterpolatingMotionEffect alloc] initWithKeyPath: @"center.x" type: UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
  597. effectX.minimumRelativeValue = @(-SVProgressHUDParallaxDepthPoints);
  598. effectX.maximumRelativeValue = @(SVProgressHUDParallaxDepthPoints);
  599. UIInterpolatingMotionEffect *effectY = [[UIInterpolatingMotionEffect alloc] initWithKeyPath: @"center.y" type: UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
  600. effectY.minimumRelativeValue = @(-SVProgressHUDParallaxDepthPoints);
  601. effectY.maximumRelativeValue = @(SVProgressHUDParallaxDepthPoints);
  602. [_hudView addMotionEffect: effectX];
  603. [_hudView addMotionEffect: effectY];
  604. }
  605. [self addSubview:_hudView];
  606. }
  607. return _hudView;
  608. }
  609. - (UILabel *)stringLabel {
  610. if (_stringLabel == nil) {
  611. _stringLabel = [[UILabel alloc] initWithFrame:CGRectZero];
  612. _stringLabel.backgroundColor = [UIColor clearColor];
  613. _stringLabel.adjustsFontSizeToFitWidth = YES;
  614. _stringLabel.textAlignment = NSTextAlignmentCenter;
  615. _stringLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
  616. _stringLabel.textColor = SVProgressHUDForegroundColor;
  617. _stringLabel.font = SVProgressHUDFont;
  618. _stringLabel.numberOfLines = 0;
  619. }
  620. if(!_stringLabel.superview)
  621. [self.hudView addSubview:_stringLabel];
  622. return _stringLabel;
  623. }
  624. - (UIImageView *)imageView {
  625. if (_imageView == nil)
  626. _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 28, 28)];
  627. if(!_imageView.superview)
  628. [self.hudView addSubview:_imageView];
  629. return _imageView;
  630. }
  631. - (CGFloat)visibleKeyboardHeight {
  632. UIWindow *keyboardWindow = nil;
  633. for (UIWindow *testWindow in [[UIApplication sharedApplication] windows]) {
  634. if(![[testWindow class] isEqual:[UIWindow class]]) {
  635. keyboardWindow = testWindow;
  636. break;
  637. }
  638. }
  639. for (__strong UIView *possibleKeyboard in [keyboardWindow subviews]) {
  640. if([possibleKeyboard isKindOfClass:NSClassFromString(@"UIPeripheralHostView")] || [possibleKeyboard isKindOfClass:NSClassFromString(@"UIKeyboard")])
  641. return possibleKeyboard.bounds.size.height;
  642. }
  643. return 0;
  644. }
  645. @end
  646. #pragma mark SVIndefiniteAnimatedView
  647. @interface SVIndefiniteAnimatedView ()
  648. @property (nonatomic, strong) CAShapeLayer *indefiniteAnimatedLayer;
  649. @end
  650. @implementation SVIndefiniteAnimatedView
  651. - (id)initWithFrame:(CGRect)frame {
  652. if (self = [super initWithFrame:frame]) {
  653. self.strokeThickness = SVProgressHUDRingThickness;
  654. self.radius = SVProgressHUDRingRadius;
  655. self.strokeColor = SVProgressHUDForegroundColor;
  656. }
  657. return self;
  658. }
  659. - (void)willMoveToSuperview:(UIView *)newSuperview {
  660. if (newSuperview != nil) {
  661. [self layoutAnimatedLayer];
  662. }
  663. else {
  664. [_indefiniteAnimatedLayer removeFromSuperlayer];
  665. _indefiniteAnimatedLayer = nil;
  666. }
  667. }
  668. - (void)layoutAnimatedLayer {
  669. CALayer *layer = self.indefiniteAnimatedLayer;
  670. [self.layer addSublayer:layer];
  671. layer.position = CGPointMake(self.bounds.size.width - layer.bounds.size.width / 2, self.bounds.size.height - layer.bounds.size.height / 2);
  672. }
  673. - (CAShapeLayer*)indefiniteAnimatedLayer {
  674. if(!_indefiniteAnimatedLayer) {
  675. CGPoint arcCenter = CGPointMake(self.radius+self.strokeThickness/2+5, self.radius+self.strokeThickness/2+5);
  676. CGRect rect = CGRectMake(0, 0, arcCenter.x*2, arcCenter.y*2);
  677. UIBezierPath* smoothedPath = [UIBezierPath bezierPathWithArcCenter:arcCenter
  678. radius:self.radius
  679. startAngle:M_PI*3/2
  680. endAngle:M_PI/2+M_PI*5
  681. clockwise:YES];
  682. _indefiniteAnimatedLayer = [CAShapeLayer layer];
  683. _indefiniteAnimatedLayer.contentsScale = [[UIScreen mainScreen] scale];
  684. _indefiniteAnimatedLayer.frame = rect;
  685. _indefiniteAnimatedLayer.fillColor = [UIColor clearColor].CGColor;
  686. _indefiniteAnimatedLayer.strokeColor = self.strokeColor.CGColor;
  687. _indefiniteAnimatedLayer.lineWidth = self.strokeThickness;
  688. _indefiniteAnimatedLayer.lineCap = kCALineCapRound;
  689. _indefiniteAnimatedLayer.lineJoin = kCALineJoinBevel;
  690. _indefiniteAnimatedLayer.path = smoothedPath.CGPath;
  691. CALayer *maskLayer = [CALayer layer];
  692. maskLayer.contents = (id)[[UIImage imageNamed:@"SVProgressHUD.bundle/angle-mask@2x.png"] CGImage];
  693. maskLayer.frame = _indefiniteAnimatedLayer.bounds;
  694. _indefiniteAnimatedLayer.mask = maskLayer;
  695. NSTimeInterval animationDuration = 1;
  696. CAMediaTimingFunction *linearCurve = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
  697. CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
  698. animation.fromValue = 0;
  699. animation.toValue = [NSNumber numberWithFloat:M_PI*2];
  700. animation.duration = animationDuration;
  701. animation.timingFunction = linearCurve;
  702. animation.removedOnCompletion = NO;
  703. animation.repeatCount = INFINITY;
  704. animation.fillMode = kCAFillModeForwards;
  705. animation.autoreverses = NO;
  706. [_indefiniteAnimatedLayer.mask addAnimation:animation forKey:@"rotate"];
  707. CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
  708. animationGroup.duration = animationDuration;
  709. animationGroup.repeatCount = INFINITY;
  710. animationGroup.removedOnCompletion = NO;
  711. animationGroup.timingFunction = linearCurve;
  712. CABasicAnimation *strokeStartAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
  713. strokeStartAnimation.fromValue = @0.015;
  714. strokeStartAnimation.toValue = @0.515;
  715. CABasicAnimation *strokeEndAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
  716. strokeEndAnimation.fromValue = @0.485;
  717. strokeEndAnimation.toValue = @0.985;
  718. animationGroup.animations = @[strokeStartAnimation, strokeEndAnimation];
  719. [_indefiniteAnimatedLayer addAnimation:animationGroup forKey:@"progress"];
  720. }
  721. return _indefiniteAnimatedLayer;
  722. }
  723. - (void)setFrame:(CGRect)frame {
  724. [super setFrame:frame];
  725. if (self.superview != nil) {
  726. [self layoutAnimatedLayer];
  727. }
  728. }
  729. - (void)setRadius:(CGFloat)radius {
  730. _radius = radius;
  731. [_indefiniteAnimatedLayer removeFromSuperlayer];
  732. _indefiniteAnimatedLayer = nil;
  733. [self layoutAnimatedLayer];
  734. }
  735. - (void)setStrokeColor:(UIColor *)strokeColor {
  736. _strokeColor = strokeColor;
  737. _indefiniteAnimatedLayer.strokeColor = strokeColor.CGColor;
  738. }
  739. - (void)setStrokeThickness:(CGFloat)strokeThickness {
  740. _strokeThickness = strokeThickness;
  741. _indefiniteAnimatedLayer.lineWidth = _strokeThickness;
  742. }
  743. - (CGSize)sizeThatFits:(CGSize)size {
  744. return CGSizeMake((self.radius+self.strokeThickness/2+5)*2, (self.radius+self.strokeThickness/2+5)*2);
  745. }
  746. @end