SVProgressHUD.m 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407
  1. //
  2. // SVProgressHUD.h
  3. // SVProgressHUD, https://github.com/SVProgressHUD/SVProgressHUD
  4. //
  5. // Copyright (c) 2011-2016 Sam Vermette and contributors. All rights reserved.
  6. //
  7. #if !__has_feature(objc_arc)
  8. #error SVProgressHUD is ARC only. Either turn on ARC for the project or use -fobjc-arc flag
  9. #endif
  10. #import "SVProgressHUD.h"
  11. #import "SVIndefiniteAnimatedView.h"
  12. #import "SVProgressAnimatedView.h"
  13. #import "SVRadialGradientLayer.h"
  14. NSString * const SVProgressHUDDidReceiveTouchEventNotification = @"SVProgressHUDDidReceiveTouchEventNotification";
  15. NSString * const SVProgressHUDDidTouchDownInsideNotification = @"SVProgressHUDDidTouchDownInsideNotification";
  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 const CGFloat SVProgressHUDParallaxDepthPoints = 10;
  22. static const CGFloat SVProgressHUDUndefinedProgress = -1;
  23. static const CGFloat SVProgressHUDDefaultAnimationDuration = 0.15;
  24. @interface SVProgressHUD ()
  25. @property (nonatomic, strong, readonly) NSTimer *fadeOutTimer;
  26. @property (nonatomic, readonly, getter = isClear) BOOL clear;
  27. @property (nonatomic, strong) UIControl *overlayView;
  28. @property (nonatomic, strong) UIView *hudView;
  29. @property (nonatomic, strong) UILabel *statusLabel;
  30. @property (nonatomic, strong) UIImageView *imageView;
  31. @property (nonatomic, strong) UIView *indefiniteAnimatedView;
  32. @property (nonatomic, strong) SVProgressAnimatedView *ringView;
  33. @property (nonatomic, strong) SVProgressAnimatedView *backgroundRingView;
  34. @property (nonatomic, strong) CALayer *backgroundLayer;
  35. @property (nonatomic, readwrite) CGFloat progress;
  36. @property (nonatomic, readwrite) NSUInteger activityCount;
  37. @property (nonatomic, readonly) CGFloat visibleKeyboardHeight;
  38. - (void)updateHUDFrame;
  39. - (void)updateMask;
  40. - (void)updateBlurBounds;
  41. #if TARGET_OS_IOS
  42. - (void)updateMotionEffectForOrientation:(UIInterfaceOrientation)orientation;
  43. #endif
  44. - (void)updateMotionEffectForXMotionEffectType:(UIInterpolatingMotionEffectType)xMotionEffectType yMotionEffectType:(UIInterpolatingMotionEffectType)yMotionEffectType;
  45. - (void)updateViewHierachy;
  46. - (void)setStatus:(NSString*)status;
  47. - (void)setFadeOutTimer:(NSTimer*)timer;
  48. - (void)registerNotifications;
  49. - (NSDictionary*)notificationUserInfo;
  50. - (void)positionHUD:(NSNotification*)notification;
  51. - (void)moveToPoint:(CGPoint)newCenter rotateAngle:(CGFloat)angle;
  52. - (void)overlayViewDidReceiveTouchEvent:(id)sender forEvent:(UIEvent*)event;
  53. - (void)showProgress:(float)progress status:(NSString*)status;
  54. - (void)showImage:(UIImage*)image status:(NSString*)status duration:(NSTimeInterval)duration;
  55. - (void)showStatus:(NSString*)status;
  56. - (void)dismiss;
  57. - (void)dismissWithDelay:(NSTimeInterval)delay;
  58. - (UIView*)indefiniteAnimatedView;
  59. - (SVProgressAnimatedView*)ringView;
  60. - (SVProgressAnimatedView*)backgroundRingView;
  61. - (void)cancelRingLayerAnimation;
  62. - (void)cancelIndefiniteAnimatedViewAnimation;
  63. - (UIColor*)foregroundColorForStyle;
  64. - (UIColor*)backgroundColorForStyle;
  65. - (UIImage*)image:(UIImage*)image withTintColor:(UIColor*)color;
  66. @end
  67. @implementation SVProgressHUD {
  68. BOOL _isInitializing;
  69. }
  70. + (SVProgressHUD*)sharedView {
  71. static dispatch_once_t once;
  72. static SVProgressHUD *sharedView;
  73. #if !defined(SV_APP_EXTENSIONS)
  74. dispatch_once(&once, ^{ sharedView = [[self alloc] initWithFrame:[[[UIApplication sharedApplication] delegate] window].bounds]; });
  75. #else
  76. dispatch_once(&once, ^{ sharedView = [[self alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; });
  77. #endif
  78. return sharedView;
  79. }
  80. #pragma mark - Setters
  81. + (void)setStatus:(NSString*)status {
  82. [[self sharedView] setStatus:status];
  83. }
  84. + (void)setDefaultStyle:(SVProgressHUDStyle)style {
  85. [self sharedView].defaultStyle = style;
  86. }
  87. + (void)setDefaultMaskType:(SVProgressHUDMaskType)maskType {
  88. [self sharedView].defaultMaskType = maskType;
  89. }
  90. + (void)setDefaultAnimationType:(SVProgressHUDAnimationType)type {
  91. [self sharedView].defaultAnimationType = type;
  92. }
  93. + (void)setMinimumSize:(CGSize)minimumSize {
  94. [self sharedView].minimumSize = minimumSize;
  95. }
  96. + (void)setRingThickness:(CGFloat)ringThickness {
  97. [self sharedView].ringThickness = ringThickness;
  98. }
  99. + (void)setRingRadius:(CGFloat)radius {
  100. [self sharedView].ringRadius = radius;
  101. }
  102. + (void)setRingNoTextRadius:(CGFloat)radius {
  103. [self sharedView].ringNoTextRadius = radius;
  104. }
  105. + (void)setCornerRadius:(CGFloat)cornerRadius {
  106. [self sharedView].cornerRadius = cornerRadius;
  107. }
  108. + (void)setFont:(UIFont*)font {
  109. [self sharedView].font = font;
  110. }
  111. + (void)setForegroundColor:(UIColor*)color {
  112. [self sharedView].foregroundColor = color;
  113. }
  114. + (void)setBackgroundColor:(UIColor*)color {
  115. [self sharedView].backgroundColor = color;
  116. }
  117. + (void)setBackgroundLayerColor:(UIColor*)color {
  118. [self sharedView].backgroundLayerColor = color;
  119. }
  120. + (void)setInfoImage:(UIImage*)image {
  121. [self sharedView].infoImage = image;
  122. }
  123. + (void)setSuccessImage:(UIImage*)image {
  124. [self sharedView].successImage = image;
  125. }
  126. + (void)setErrorImage:(UIImage*)image {
  127. [self sharedView].errorImage = image;
  128. }
  129. + (void)setViewForExtension:(UIView*)view {
  130. [self sharedView].viewForExtension = view;
  131. }
  132. + (void)setMinimumDismissTimeInterval:(NSTimeInterval)interval {
  133. [self sharedView].minimumDismissTimeInterval = interval;
  134. }
  135. + (void)setFadeInAnimationDuration:(NSTimeInterval)duration {
  136. [self sharedView].fadeInAnimationDuration = duration;
  137. }
  138. + (void)setFadeOutAnimationDuration:(NSTimeInterval)duration {
  139. [self sharedView].fadeOutAnimationDuration = duration;
  140. }
  141. #pragma mark - Show Methods
  142. + (void)show {
  143. [self showWithStatus:nil];
  144. }
  145. + (void)showWithMaskType:(SVProgressHUDMaskType)maskType {
  146. SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType;
  147. [self setDefaultMaskType:maskType];
  148. [self show];
  149. [self setDefaultMaskType:existingMaskType];
  150. }
  151. + (void)showWithStatus:(NSString*)status {
  152. [self sharedView];
  153. [self showProgress:SVProgressHUDUndefinedProgress status:status];
  154. }
  155. + (void)showWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType {
  156. SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType;
  157. [self setDefaultMaskType:maskType];
  158. [self showWithStatus:status];
  159. [self setDefaultMaskType:existingMaskType];
  160. }
  161. + (void)showProgress:(float)progress {
  162. [self showProgress:progress status:nil];
  163. }
  164. + (void)showProgress:(float)progress maskType:(SVProgressHUDMaskType)maskType {
  165. SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType;
  166. [self setDefaultMaskType:maskType];
  167. [self showProgress:progress];
  168. [self setDefaultMaskType:existingMaskType];
  169. }
  170. + (void)showProgress:(float)progress status:(NSString*)status {
  171. [[self sharedView] showProgress:progress status:status];
  172. }
  173. + (void)showProgress:(float)progress status:(NSString*)status maskType:(SVProgressHUDMaskType)maskType {
  174. SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType;
  175. [self setDefaultMaskType:maskType];
  176. [self showProgress:progress status:status];
  177. [self setDefaultMaskType:existingMaskType];
  178. }
  179. #pragma mark - Show, then automatically dismiss methods
  180. + (void)showInfoWithStatus:(NSString*)status {
  181. [self showImage:[self sharedView].infoImage status:status];
  182. }
  183. + (void)showInfoWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType {
  184. SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType;
  185. [self setDefaultMaskType:maskType];
  186. [self showInfoWithStatus:status];
  187. [self setDefaultMaskType:existingMaskType];
  188. }
  189. + (void)showSuccessWithStatus:(NSString*)status {
  190. [self showImage:[self sharedView].successImage status:status];
  191. }
  192. + (void)showSuccessWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType {
  193. SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType;
  194. [self setDefaultMaskType:maskType];
  195. [self showSuccessWithStatus:status];
  196. [self setDefaultMaskType:existingMaskType];
  197. }
  198. + (void)showErrorWithStatus:(NSString*)status {
  199. [self showImage:[self sharedView].errorImage status:status];
  200. }
  201. + (void)showErrorWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType {
  202. SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType;
  203. [self setDefaultMaskType:maskType];
  204. [self showErrorWithStatus:status];
  205. [self setDefaultMaskType:existingMaskType];
  206. }
  207. + (void)showImage:(UIImage*)image status:(NSString*)status {
  208. NSTimeInterval displayInterval = [self displayDurationForString:status];
  209. [[self sharedView] showImage:image status:status duration:displayInterval];
  210. }
  211. + (void)showImage:(UIImage*)image status:(NSString*)status maskType:(SVProgressHUDMaskType)maskType {
  212. SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType;
  213. [self setDefaultMaskType:maskType];
  214. [self showImage:image status:status];
  215. [self setDefaultMaskType:existingMaskType];
  216. }
  217. #pragma mark - Dismiss Methods
  218. + (void)popActivity {
  219. if([self sharedView].activityCount > 0) {
  220. [self sharedView].activityCount--;
  221. }
  222. if([self sharedView].activityCount == 0) {
  223. [[self sharedView] dismiss];
  224. }
  225. }
  226. + (void)dismiss {
  227. [self dismissWithDelay:0.0];
  228. }
  229. + (void)dismissWithDelay:(NSTimeInterval)delay {
  230. [[self sharedView] dismissWithDelay:delay];
  231. }
  232. #pragma mark - Offset
  233. + (void)setOffsetFromCenter:(UIOffset)offset {
  234. [self sharedView].offsetFromCenter = offset;
  235. }
  236. + (void)resetOffsetFromCenter {
  237. [self setOffsetFromCenter:UIOffsetZero];
  238. }
  239. #pragma mark - Instance Methods
  240. - (instancetype)initWithFrame:(CGRect)frame {
  241. if((self = [super initWithFrame:frame])) {
  242. _isInitializing = YES;
  243. self.userInteractionEnabled = NO;
  244. _backgroundColor = [UIColor clearColor];
  245. _foregroundColor = [UIColor blackColor];
  246. _backgroundLayerColor = [UIColor colorWithWhite:0 alpha:0.4];
  247. self.alpha = 0.0f;
  248. self.activityCount = 0;
  249. // Set default values
  250. _defaultMaskType = SVProgressHUDMaskTypeNone;
  251. _defaultStyle = SVProgressHUDStyleLight;
  252. _defaultAnimationType = SVProgressHUDAnimationTypeFlat;
  253. if ([UIFont respondsToSelector:@selector(preferredFontForTextStyle:)]) {
  254. _font = [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
  255. } else {
  256. _font = [UIFont systemFontOfSize:14.0f];
  257. }
  258. NSBundle *bundle = [NSBundle bundleForClass:[SVProgressHUD class]];
  259. NSURL *url = [bundle URLForResource:@"SVProgressHUD" withExtension:@"bundle"];
  260. NSBundle *imageBundle = [NSBundle bundleWithURL:url];
  261. UIImage* infoImage = [UIImage imageWithContentsOfFile:[imageBundle pathForResource:@"info" ofType:@"png"]];
  262. UIImage* successImage = [UIImage imageWithContentsOfFile:[imageBundle pathForResource:@"success" ofType:@"png"]];
  263. UIImage* errorImage = [UIImage imageWithContentsOfFile:[imageBundle pathForResource:@"error" ofType:@"png"]];
  264. if ([[UIImage class] instancesRespondToSelector:@selector(imageWithRenderingMode:)]) {
  265. _infoImage = [infoImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
  266. _successImage = [successImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
  267. _errorImage = [errorImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
  268. } else {
  269. _infoImage = infoImage;
  270. _successImage = successImage;
  271. _errorImage = errorImage;
  272. }
  273. _ringThickness = 2.0f;
  274. _ringRadius = 18.0f;
  275. _ringNoTextRadius = 24.0f;
  276. _cornerRadius = 14.0f;
  277. _minimumDismissTimeInterval = 5.0;
  278. _fadeInAnimationDuration = SVProgressHUDDefaultAnimationDuration;
  279. _fadeOutAnimationDuration = SVProgressHUDDefaultAnimationDuration;
  280. // Accessibility support
  281. self.accessibilityIdentifier = @"SVProgressHUD";
  282. self.accessibilityLabel = @"SVProgressHUD";
  283. self.isAccessibilityElement = YES;
  284. _isInitializing = NO;
  285. }
  286. return self;
  287. }
  288. - (void)updateHUDFrame {
  289. // For the beginning use default values, these
  290. // might get update if string is too large etc.
  291. CGFloat hudWidth = 100.0f;
  292. CGFloat hudHeight = 100.0f;
  293. CGFloat stringHeightBuffer = 20.0f;
  294. CGFloat stringAndContentHeightBuffer = 80.0f;
  295. CGRect labelRect = CGRectZero;
  296. // Check if an image or progress ring is displayed
  297. BOOL imageUsed = (self.imageView.image) && !(self.imageView.hidden);
  298. BOOL progressUsed = self.imageView.hidden;
  299. // Calculate size of string and update HUD size
  300. NSString *string = self.statusLabel.text;
  301. if(string) {
  302. CGSize constraintSize = CGSizeMake(200.0f, 300.0f);
  303. CGRect stringRect;
  304. if([string respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) {
  305. stringRect = [string boundingRectWithSize:constraintSize
  306. options:(NSStringDrawingOptions)(NSStringDrawingUsesFontLeading|NSStringDrawingTruncatesLastVisibleLine|NSStringDrawingUsesLineFragmentOrigin)
  307. attributes:@{NSFontAttributeName: self.statusLabel.font}
  308. context:NULL];
  309. } else {
  310. CGSize stringSize;
  311. if([string respondsToSelector:@selector(sizeWithAttributes:)]) {
  312. stringSize = [string sizeWithAttributes:@{NSFontAttributeName:[UIFont fontWithName:self.statusLabel.font.fontName size:self.statusLabel.font.pointSize]}];
  313. } else {
  314. #if TARGET_OS_IOS
  315. #pragma clang diagnostic push
  316. #pragma clang diagnostic ignored "-Wdeprecated"
  317. stringSize = [string sizeWithFont:self.statusLabel.font constrainedToSize:CGSizeMake(200.0f, 300.0f)];
  318. #pragma clang diagnostic pop
  319. #endif
  320. }
  321. stringRect = CGRectMake(0.0f, 0.0f, stringSize.width, stringSize.height);
  322. }
  323. CGFloat stringWidth = stringRect.size.width;
  324. CGFloat stringHeight = ceilf(CGRectGetHeight(stringRect));
  325. if(imageUsed || progressUsed) {
  326. hudHeight = stringAndContentHeightBuffer + stringHeight;
  327. } else {
  328. hudHeight = stringHeightBuffer + stringHeight;
  329. }
  330. if(stringWidth > hudWidth) {
  331. hudWidth = ceilf(stringWidth/2)*2;
  332. }
  333. CGFloat labelRectY = (imageUsed || progressUsed) ? 68.0f : 9.0f;
  334. if(hudHeight > 100.0f) {
  335. labelRect = CGRectMake(12.0f, labelRectY, hudWidth, stringHeight);
  336. hudWidth += 24.0f;
  337. } else {
  338. hudWidth += 24.0f;
  339. labelRect = CGRectMake(0.0f, labelRectY, hudWidth, stringHeight);
  340. }
  341. }
  342. // Update values on subviews
  343. self.hudView.bounds = CGRectMake(0.0f, 0.0f, MAX(self.minimumSize.width, hudWidth), MAX(self.minimumSize.height, hudHeight));
  344. labelRect.size.width += MAX(0, self.minimumSize.width - hudWidth);
  345. [self updateBlurBounds];
  346. if(string) {
  347. self.imageView.center = CGPointMake(CGRectGetWidth(self.hudView.bounds)/2, 36.0f);
  348. } else {
  349. self.imageView.center = CGPointMake(CGRectGetWidth(self.hudView.bounds)/2, CGRectGetHeight(self.hudView.bounds)/2);
  350. }
  351. self.statusLabel.hidden = NO;
  352. self.statusLabel.frame = labelRect;
  353. // Animate value update
  354. [CATransaction begin];
  355. [CATransaction setDisableActions:YES];
  356. if(string) {
  357. if(self.defaultAnimationType == SVProgressHUDAnimationTypeFlat) {
  358. SVIndefiniteAnimatedView *indefiniteAnimationView = (SVIndefiniteAnimatedView*)self.indefiniteAnimatedView;
  359. indefiniteAnimationView.radius = self.ringRadius;
  360. [indefiniteAnimationView sizeToFit];
  361. }
  362. CGPoint center = CGPointMake((CGRectGetWidth(self.hudView.bounds)/2), 36.0f);
  363. self.indefiniteAnimatedView.center = center;
  364. if(self.progress != SVProgressHUDUndefinedProgress) {
  365. self.backgroundRingView.center = self.ringView.center = CGPointMake((CGRectGetWidth(self.hudView.bounds)/2), 36.0f);
  366. }
  367. } else {
  368. if(self.defaultAnimationType == SVProgressHUDAnimationTypeFlat) {
  369. SVIndefiniteAnimatedView *indefiniteAnimationView = (SVIndefiniteAnimatedView*)self.indefiniteAnimatedView;
  370. indefiniteAnimationView.radius = self.ringNoTextRadius;
  371. [indefiniteAnimationView sizeToFit];
  372. }
  373. CGPoint center = CGPointMake((CGRectGetWidth(self.hudView.bounds)/2), CGRectGetHeight(self.hudView.bounds)/2);
  374. self.indefiniteAnimatedView.center = center;
  375. if(self.progress != SVProgressHUDUndefinedProgress) {
  376. self.backgroundRingView.center = self.ringView.center = CGPointMake((CGRectGetWidth(self.hudView.bounds)/2), CGRectGetHeight(self.hudView.bounds)/2);
  377. }
  378. }
  379. [CATransaction commit];
  380. }
  381. - (void)updateMask {
  382. if(self.backgroundLayer) {
  383. [self.backgroundLayer removeFromSuperlayer];
  384. self.backgroundLayer = nil;
  385. }
  386. switch (self.defaultMaskType) {
  387. case SVProgressHUDMaskTypeCustom:
  388. case SVProgressHUDMaskTypeBlack:{
  389. self.backgroundLayer = [CALayer layer];
  390. self.backgroundLayer.frame = self.bounds;
  391. self.backgroundLayer.backgroundColor = self.defaultMaskType == SVProgressHUDMaskTypeCustom ? self.backgroundLayerColor.CGColor : [UIColor colorWithWhite:0 alpha:0.4].CGColor;
  392. [self.backgroundLayer setNeedsDisplay];
  393. [self.layer insertSublayer:self.backgroundLayer atIndex:0];
  394. break;
  395. }
  396. case SVProgressHUDMaskTypeGradient:{
  397. SVRadialGradientLayer *layer = [SVRadialGradientLayer layer];
  398. self.backgroundLayer = layer;
  399. self.backgroundLayer.frame = self.bounds;
  400. CGPoint gradientCenter = self.center;
  401. gradientCenter.y = (self.bounds.size.height - self.visibleKeyboardHeight)/2;
  402. layer.gradientCenter = gradientCenter;
  403. [self.backgroundLayer setNeedsDisplay];
  404. [self.layer insertSublayer:self.backgroundLayer atIndex:0];
  405. break;
  406. }
  407. default:
  408. break;
  409. }
  410. }
  411. - (void)updateBlurBounds {
  412. #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
  413. if(NSClassFromString(@"UIBlurEffect") && self.defaultStyle != SVProgressHUDStyleCustom) {
  414. // Remove background color, else the effect would not work
  415. self.hudView.backgroundColor = [UIColor clearColor];
  416. // Remove any old instances of UIVisualEffectViews
  417. for (UIView *subview in self.hudView.subviews) {
  418. if([subview isKindOfClass:[UIVisualEffectView class]]) {
  419. [subview removeFromSuperview];
  420. }
  421. }
  422. if(self.backgroundColor != [UIColor clearColor]) {
  423. // Create blur effect
  424. UIBlurEffectStyle blurEffectStyle = self.defaultStyle == SVProgressHUDStyleDark ? UIBlurEffectStyleDark : UIBlurEffectStyleLight;
  425. UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:blurEffectStyle];
  426. UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
  427. blurEffectView.autoresizingMask = self.hudView.autoresizingMask;
  428. blurEffectView.frame = self.hudView.bounds;
  429. // Add vibrancy to the blur effect to make it more vivid
  430. UIVibrancyEffect *vibrancyEffect = [UIVibrancyEffect effectForBlurEffect:blurEffect];
  431. UIVisualEffectView *vibrancyEffectView = [[UIVisualEffectView alloc] initWithEffect:vibrancyEffect];
  432. vibrancyEffectView.autoresizingMask = blurEffectView.autoresizingMask;
  433. vibrancyEffectView.bounds = blurEffectView.bounds;
  434. [blurEffectView.contentView addSubview:vibrancyEffectView];
  435. [self.hudView insertSubview:blurEffectView atIndex:0];
  436. }
  437. }
  438. #endif
  439. }
  440. #if TARGET_OS_IOS
  441. - (void)updateMotionEffectForOrientation:(UIInterfaceOrientation)orientation {
  442. UIInterpolatingMotionEffectType xMotionEffectType = UIInterfaceOrientationIsPortrait(orientation) ? UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis : UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis;
  443. UIInterpolatingMotionEffectType yMotionEffectType = UIInterfaceOrientationIsPortrait(orientation) ? UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis : UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis;
  444. [self updateMotionEffectForXMotionEffectType:xMotionEffectType yMotionEffectType:yMotionEffectType];
  445. }
  446. #endif
  447. - (void)updateMotionEffectForXMotionEffectType:(UIInterpolatingMotionEffectType)xMotionEffectType yMotionEffectType:(UIInterpolatingMotionEffectType)yMotionEffectType {
  448. if([self.hudView respondsToSelector:@selector(addMotionEffect:)]) {
  449. UIInterpolatingMotionEffect *effectX = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:xMotionEffectType];
  450. effectX.minimumRelativeValue = @(-SVProgressHUDParallaxDepthPoints);
  451. effectX.maximumRelativeValue = @(SVProgressHUDParallaxDepthPoints);
  452. UIInterpolatingMotionEffect *effectY = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" type:yMotionEffectType];
  453. effectY.minimumRelativeValue = @(-SVProgressHUDParallaxDepthPoints);
  454. effectY.maximumRelativeValue = @(SVProgressHUDParallaxDepthPoints);
  455. UIMotionEffectGroup *effectGroup = [[UIMotionEffectGroup alloc] init];
  456. effectGroup.motionEffects = @[effectX, effectY];
  457. // Clear old motion effect, then add new motion effects
  458. self.hudView.motionEffects = @[];
  459. [self.hudView addMotionEffect:effectGroup];
  460. }
  461. }
  462. - (void)updateViewHierachy {
  463. // Add the overlay (e.g. black, gradient) to the application window if necessary
  464. if(!self.overlayView.superview) {
  465. #if !defined(SV_APP_EXTENSIONS)
  466. // Default case: iterate over UIApplication windows
  467. NSEnumerator *frontToBackWindows = [UIApplication.sharedApplication.windows reverseObjectEnumerator];
  468. for (UIWindow *window in frontToBackWindows) {
  469. BOOL windowOnMainScreen = window.screen == UIScreen.mainScreen;
  470. BOOL windowIsVisible = !window.hidden && window.alpha > 0;
  471. BOOL windowLevelNormal = window.windowLevel == UIWindowLevelNormal;
  472. if(windowOnMainScreen && windowIsVisible && windowLevelNormal) {
  473. [window addSubview:self.overlayView];
  474. break;
  475. }
  476. }
  477. #else
  478. // If SVProgressHUD ist used inside an app extension add it to the given view
  479. if(self.viewForExtension) {
  480. [self.viewForExtension addSubview:self.overlayView];
  481. }
  482. #endif
  483. } else {
  484. // The HUD is already on screen, but maybot not in front. Therefore
  485. // ensure that overlay will be on top of rootViewController (which may
  486. // be changed during runtime).
  487. [self.overlayView.superview bringSubviewToFront:self.overlayView];
  488. }
  489. // Add self to the overlay view
  490. if(!self.superview){
  491. [self.overlayView addSubview:self];
  492. }
  493. if(!self.hudView.superview) {
  494. [self addSubview:self.hudView];
  495. }
  496. }
  497. - (void)setStatus:(NSString*)status {
  498. self.statusLabel.text = status;
  499. [self updateHUDFrame];
  500. }
  501. - (void)setFadeOutTimer:(NSTimer*)timer {
  502. if(_fadeOutTimer) {
  503. [_fadeOutTimer invalidate], _fadeOutTimer = nil;
  504. }
  505. if(timer) {
  506. _fadeOutTimer = timer;
  507. }
  508. }
  509. #pragma mark - Notifications and their handling
  510. - (void)registerNotifications {
  511. #if TARGET_OS_IOS
  512. [[NSNotificationCenter defaultCenter] addObserver:self
  513. selector:@selector(positionHUD:)
  514. name:UIApplicationDidChangeStatusBarOrientationNotification
  515. object:nil];
  516. [[NSNotificationCenter defaultCenter] addObserver:self
  517. selector:@selector(positionHUD:)
  518. name:UIKeyboardWillHideNotification
  519. object:nil];
  520. [[NSNotificationCenter defaultCenter] addObserver:self
  521. selector:@selector(positionHUD:)
  522. name:UIKeyboardDidHideNotification
  523. object:nil];
  524. [[NSNotificationCenter defaultCenter] addObserver:self
  525. selector:@selector(positionHUD:)
  526. name:UIKeyboardWillShowNotification
  527. object:nil];
  528. [[NSNotificationCenter defaultCenter] addObserver:self
  529. selector:@selector(positionHUD:)
  530. name:UIKeyboardDidShowNotification
  531. object:nil];
  532. #endif
  533. [[NSNotificationCenter defaultCenter] addObserver:self
  534. selector:@selector(positionHUD:)
  535. name:UIApplicationDidBecomeActiveNotification
  536. object:nil];
  537. }
  538. - (NSDictionary*)notificationUserInfo{
  539. return (self.statusLabel.text ? @{SVProgressHUDStatusUserInfoKey : self.statusLabel.text} : nil);
  540. }
  541. - (void)positionHUD:(NSNotification*)notification {
  542. CGFloat keyboardHeight = 0.0f;
  543. double animationDuration = 0.0;
  544. #if !defined(SV_APP_EXTENSIONS) && TARGET_OS_IOS
  545. self.frame = [[[UIApplication sharedApplication] delegate] window].bounds;
  546. UIInterfaceOrientation orientation = UIApplication.sharedApplication.statusBarOrientation;
  547. #elif !defined(SV_APP_EXTENSIONS)
  548. self.frame = [UIApplication sharedApplication].keyWindow.bounds;
  549. #else
  550. if (self.viewForExtension) {
  551. self.frame = self.viewForExtension.frame;
  552. } else {
  553. self.frame = UIScreen.mainScreen.bounds;
  554. }
  555. UIInterfaceOrientation orientation = CGRectGetWidth(self.frame) > CGRectGetHeight(self.frame) ? UIInterfaceOrientationLandscapeLeft : UIInterfaceOrientationPortrait;
  556. #endif
  557. // 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.
  558. BOOL ignoreOrientation = NO;
  559. #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
  560. if([[NSProcessInfo processInfo] respondsToSelector:@selector(operatingSystemVersion)]) {
  561. ignoreOrientation = YES;
  562. }
  563. #endif
  564. #if TARGET_OS_IOS
  565. // Get keyboardHeight in regards to current state
  566. if(notification) {
  567. NSDictionary* keyboardInfo = [notification userInfo];
  568. CGRect keyboardFrame = [keyboardInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
  569. animationDuration = [keyboardInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
  570. if(notification.name == UIKeyboardWillShowNotification || notification.name == UIKeyboardDidShowNotification) {
  571. keyboardHeight = CGRectGetWidth(keyboardFrame);
  572. if(ignoreOrientation || UIInterfaceOrientationIsPortrait(orientation)) {
  573. keyboardHeight = CGRectGetHeight(keyboardFrame);
  574. }
  575. }
  576. } else {
  577. keyboardHeight = self.visibleKeyboardHeight;
  578. }
  579. #endif
  580. // Get the currently active frame of the display (depends on orientation)
  581. CGRect orientationFrame = self.bounds;
  582. #if !defined(SV_APP_EXTENSIONS) && TARGET_OS_IOS
  583. CGRect statusBarFrame = UIApplication.sharedApplication.statusBarFrame;
  584. #else
  585. CGRect statusBarFrame = CGRectZero;
  586. #endif
  587. #if TARGET_OS_IOS
  588. if(!ignoreOrientation && UIInterfaceOrientationIsLandscape(orientation)) {
  589. float temp = CGRectGetWidth(orientationFrame);
  590. orientationFrame.size.width = CGRectGetHeight(orientationFrame);
  591. orientationFrame.size.height = temp;
  592. temp = CGRectGetWidth(statusBarFrame);
  593. statusBarFrame.size.width = CGRectGetHeight(statusBarFrame);
  594. statusBarFrame.size.height = temp;
  595. }
  596. // Update the motion effects in regards to orientation
  597. [self updateMotionEffectForOrientation:orientation];
  598. #else
  599. [self updateMotionEffectForXMotionEffectType:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis yMotionEffectType:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
  600. #endif
  601. // Calculate available height for display
  602. CGFloat activeHeight = CGRectGetHeight(orientationFrame);
  603. if(keyboardHeight > 0) {
  604. activeHeight += CGRectGetHeight(statusBarFrame)*2;
  605. }
  606. activeHeight -= keyboardHeight;
  607. CGFloat posX = CGRectGetWidth(orientationFrame)/2.0f;
  608. CGFloat posY = floorf(activeHeight*0.45f);
  609. CGFloat rotateAngle = 0.0;
  610. CGPoint newCenter = CGPointMake(posX, posY);
  611. // Update posX and posY in regards to orientation
  612. #if TARGET_OS_IOS
  613. if(!ignoreOrientation) {
  614. switch (orientation) {
  615. case UIInterfaceOrientationPortraitUpsideDown:
  616. rotateAngle = (CGFloat) M_PI;
  617. newCenter = CGPointMake(posX, CGRectGetHeight(orientationFrame)-posY);
  618. break;
  619. case UIInterfaceOrientationLandscapeLeft:
  620. rotateAngle = (CGFloat) (-M_PI/2.0f);
  621. newCenter = CGPointMake(posY, posX);
  622. break;
  623. case UIInterfaceOrientationLandscapeRight:
  624. rotateAngle = (CGFloat) (M_PI/2.0f);
  625. newCenter = CGPointMake(CGRectGetHeight(orientationFrame)-posY, posX);
  626. break;
  627. default: // Same as UIInterfaceOrientationPortrait
  628. rotateAngle = 0.0f;
  629. newCenter = CGPointMake(posX, posY);
  630. break;
  631. }
  632. }
  633. #endif
  634. if(notification) {
  635. // Animate update if notification was present
  636. __weak SVProgressHUD *weakSelf = self;
  637. [UIView animateWithDuration:animationDuration
  638. delay:0
  639. options:UIViewAnimationOptionAllowUserInteraction
  640. animations:^{
  641. __strong SVProgressHUD *strongSelf = weakSelf;
  642. if(strongSelf) {
  643. [strongSelf moveToPoint:newCenter rotateAngle:rotateAngle];
  644. [strongSelf.hudView setNeedsDisplay];
  645. }
  646. } completion:NULL];
  647. } else {
  648. [self moveToPoint:newCenter rotateAngle:rotateAngle];
  649. [self.hudView setNeedsDisplay];
  650. }
  651. [self updateMask];
  652. }
  653. - (void)moveToPoint:(CGPoint)newCenter rotateAngle:(CGFloat)angle {
  654. self.hudView.transform = CGAffineTransformMakeRotation(angle);
  655. self.hudView.center = CGPointMake(newCenter.x + self.offsetFromCenter.horizontal, newCenter.y + self.offsetFromCenter.vertical);
  656. }
  657. #pragma mark - Event handling
  658. - (void)overlayViewDidReceiveTouchEvent:(id)sender forEvent:(UIEvent*)event {
  659. [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidReceiveTouchEventNotification
  660. object:self
  661. userInfo:[self notificationUserInfo]];
  662. UITouch *touch = event.allTouches.anyObject;
  663. CGPoint touchLocation = [touch locationInView:self];
  664. if(CGRectContainsPoint(self.hudView.frame, touchLocation)) {
  665. [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidTouchDownInsideNotification
  666. object:self
  667. userInfo:[self notificationUserInfo]];
  668. }
  669. }
  670. #pragma mark - Master show/dismiss methods
  671. - (void)showProgress:(float)progress status:(NSString*)status {
  672. __weak SVProgressHUD *weakSelf = self;
  673. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  674. __strong SVProgressHUD *strongSelf = weakSelf;
  675. if(strongSelf){
  676. // Update / Check view hierachy to ensure the HUD is visible
  677. [strongSelf updateViewHierachy];
  678. // Reset imageView and fadeout timer if an image is currently displayed
  679. strongSelf.imageView.hidden = YES;
  680. strongSelf.imageView.image = nil;
  681. if(strongSelf.fadeOutTimer) {
  682. strongSelf.activityCount = 0;
  683. }
  684. strongSelf.fadeOutTimer = nil;
  685. // Update text and set progress to the given value
  686. strongSelf.statusLabel.text = status;
  687. strongSelf.progress = progress;
  688. // Choose the "right" indicator depending on the progress
  689. if(progress >= 0) {
  690. // Cancel the indefiniteAnimatedView, then show the ringLayer
  691. [strongSelf cancelIndefiniteAnimatedViewAnimation];
  692. // Add ring to HUD and set progress
  693. [strongSelf.hudView addSubview:strongSelf.ringView];
  694. [strongSelf.hudView addSubview:strongSelf.backgroundRingView];
  695. strongSelf.ringView.strokeEnd = progress;
  696. // Updat the activity count
  697. if(progress == 0) {
  698. strongSelf.activityCount++;
  699. }
  700. } else {
  701. // Cancel the ringLayer animation, then show the indefiniteAnimatedView
  702. [strongSelf cancelRingLayerAnimation];
  703. // Add indefiniteAnimatedView to HUD
  704. [strongSelf.hudView addSubview:strongSelf.indefiniteAnimatedView];
  705. if([strongSelf.indefiniteAnimatedView respondsToSelector:@selector(startAnimating)]) {
  706. [(id)strongSelf.indefiniteAnimatedView startAnimating];
  707. }
  708. // Update the activity count
  709. strongSelf.activityCount++;
  710. }
  711. // Show
  712. [strongSelf showStatus:status];
  713. }
  714. }];
  715. }
  716. - (void)showImage:(UIImage*)image status:(NSString*)status duration:(NSTimeInterval)duration {
  717. __weak SVProgressHUD *weakSelf = self;
  718. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  719. __strong SVProgressHUD *strongSelf = weakSelf;
  720. if(strongSelf){
  721. // Update / Check view hierachy to ensure the HUD is visible
  722. [strongSelf updateViewHierachy];
  723. // Reset progress and cancel any running animation
  724. strongSelf.progress = SVProgressHUDUndefinedProgress;
  725. [strongSelf cancelRingLayerAnimation];
  726. [strongSelf cancelIndefiniteAnimatedViewAnimation];
  727. // Update imageView
  728. UIColor *tintColor = strongSelf.foregroundColorForStyle;
  729. UIImage *tintedImage = image;
  730. if([strongSelf.imageView respondsToSelector:@selector(setTintColor:)]) {
  731. if (tintedImage.renderingMode != UIImageRenderingModeAlwaysTemplate) {
  732. tintedImage = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
  733. }
  734. strongSelf.imageView.tintColor = tintColor;
  735. } else {
  736. tintedImage = [strongSelf image:image withTintColor:tintColor];
  737. }
  738. strongSelf.imageView.image = tintedImage;
  739. strongSelf.imageView.hidden = NO;
  740. // Update text
  741. strongSelf.statusLabel.text = status;
  742. // Show
  743. [strongSelf showStatus:status];
  744. // An image will dismissed automatically. Therefore we start a timer
  745. // which then will call dismiss after the predefined duration
  746. strongSelf.fadeOutTimer = [NSTimer timerWithTimeInterval:duration target:strongSelf selector:@selector(dismiss) userInfo:nil repeats:NO];
  747. [[NSRunLoop mainRunLoop] addTimer:strongSelf.fadeOutTimer forMode:NSRunLoopCommonModes];
  748. }
  749. }];
  750. }
  751. - (void)showStatus:(NSString*)status {
  752. // Update the HUDs frame to the new content and position HUD
  753. [self updateHUDFrame];
  754. [self positionHUD:nil];
  755. // Update accesibilty as well as user interaction
  756. if(self.defaultMaskType != SVProgressHUDMaskTypeNone) {
  757. self.overlayView.userInteractionEnabled = YES;
  758. self.accessibilityLabel = status;
  759. self.isAccessibilityElement = YES;
  760. } else {
  761. self.overlayView.userInteractionEnabled = NO;
  762. self.hudView.accessibilityLabel = status;
  763. self.hudView.isAccessibilityElement = YES;
  764. }
  765. // Show overlay
  766. self.overlayView.backgroundColor = [UIColor clearColor];
  767. // Show if not already visible (depending on alpha)
  768. if(self.alpha != 1.0f || self.hudView.alpha != 1.0f) {
  769. // Post notification to inform user
  770. [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDWillAppearNotification
  771. object:self
  772. userInfo:[self notificationUserInfo]];
  773. // Zoom HUD a little to make a nice appear / pop up animation
  774. self.hudView.transform = CGAffineTransformScale(self.hudView.transform, 1.3, 1.3);
  775. // Set initial values to handle iOS 7 (and above) UIToolbar which not answers well to hierarchy opacity change
  776. self.alpha = 0.0f;
  777. self.hudView.alpha = 0.0f;
  778. // Define blocks
  779. __weak SVProgressHUD *weakSelf = self;
  780. __block void (^animationsBlock)(void) = ^{
  781. __strong SVProgressHUD *strongSelf = weakSelf;
  782. if(strongSelf) {
  783. // Shrink HUD to finish pop up animation
  784. strongSelf.hudView.transform = CGAffineTransformScale(strongSelf.hudView.transform, 1/1.3f, 1/1.3f);
  785. strongSelf.alpha = 1.0f;
  786. strongSelf.hudView.alpha = 1.0f;
  787. }
  788. };
  789. __block void (^completionBlock)(void) = ^{
  790. __strong SVProgressHUD *strongSelf = weakSelf;
  791. if(strongSelf) {
  792. /// Register observer <=> we now have to handle orientation changes etc.
  793. [strongSelf registerNotifications];
  794. // Post notification to inform user
  795. [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidAppearNotification
  796. object:strongSelf
  797. userInfo:[strongSelf notificationUserInfo]];
  798. }
  799. // Update accesibilty
  800. UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);
  801. UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, status);
  802. };
  803. if (self.fadeInAnimationDuration > 0) {
  804. // Animate appearance
  805. [UIView animateWithDuration:self.fadeInAnimationDuration
  806. delay:0
  807. options:(UIViewAnimationOptions) (UIViewAnimationOptionAllowUserInteraction | UIViewAnimationCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState)
  808. animations:^{
  809. animationsBlock();
  810. } completion:^(BOOL finished) {
  811. completionBlock();
  812. }];
  813. } else {
  814. animationsBlock();
  815. completionBlock();
  816. }
  817. // Inform iOS to redraw the view hierachy
  818. [self setNeedsDisplay];
  819. }
  820. }
  821. - (void)dismiss {
  822. [self dismissWithDelay:0];
  823. }
  824. - (void)dismissWithDelay:(NSTimeInterval)delay {
  825. __weak SVProgressHUD *weakSelf = self;
  826. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  827. __strong SVProgressHUD *strongSelf = weakSelf;
  828. if(strongSelf){
  829. // Dismiss if visible (depending on alpha)
  830. if(strongSelf.alpha != 0.0f || strongSelf.hudView.alpha != 0.0f){
  831. // Post notification to inform user
  832. [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDWillDisappearNotification
  833. object:nil
  834. userInfo:[strongSelf notificationUserInfo]];
  835. // Reset activitiy count
  836. strongSelf.activityCount = 0;
  837. // Define blocks
  838. __block void (^animationsBlock)(void) = ^{
  839. strongSelf.hudView.transform = CGAffineTransformScale(strongSelf.hudView.transform, 0.8f, 0.8f);
  840. strongSelf.alpha = 0.0f;
  841. strongSelf.hudView.alpha = 0.0f;
  842. };
  843. __block void (^completionBlock)(void) = ^{
  844. // Clean up view hierachy (overlays)
  845. [strongSelf.overlayView removeFromSuperview];
  846. [strongSelf.hudView removeFromSuperview];
  847. [strongSelf removeFromSuperview];
  848. // Reset progress and cancel any running animation
  849. strongSelf.progress = SVProgressHUDUndefinedProgress;
  850. [strongSelf cancelRingLayerAnimation];
  851. [strongSelf cancelIndefiniteAnimatedViewAnimation];
  852. // Remove observer <=> we do not have to handle orientation changes etc.
  853. [[NSNotificationCenter defaultCenter] removeObserver:strongSelf];
  854. // Post notification to inform user
  855. [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidDisappearNotification
  856. object:strongSelf
  857. userInfo:[strongSelf notificationUserInfo]];
  858. // Tell the rootViewController to update the StatusBar appearance
  859. #if !defined(SV_APP_EXTENSIONS) && TARGET_OS_IOS
  860. UIViewController *rootController = [[UIApplication sharedApplication] keyWindow].rootViewController;
  861. if([rootController respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) {
  862. [rootController setNeedsStatusBarAppearanceUpdate];
  863. }
  864. #endif
  865. // Update accesibilty
  866. UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);
  867. };
  868. if (strongSelf.fadeOutAnimationDuration > 0) {
  869. // Animate appearance
  870. [UIView animateWithDuration:strongSelf.fadeOutAnimationDuration
  871. delay:delay
  872. options:(UIViewAnimationOptions) (UIViewAnimationOptionAllowUserInteraction | UIViewAnimationCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState)
  873. animations:^{
  874. animationsBlock();
  875. } completion:^(BOOL finished) {
  876. completionBlock();
  877. }];
  878. } else {
  879. animationsBlock();
  880. completionBlock();
  881. }
  882. // Inform iOS to redraw the view hierachy
  883. [strongSelf setNeedsDisplay];
  884. }
  885. }
  886. }];
  887. }
  888. #pragma mark - Ring progress animation
  889. - (UIView*)indefiniteAnimatedView {
  890. // Get the correct spinner for defaultAnimationType
  891. if(self.defaultAnimationType == SVProgressHUDAnimationTypeFlat){
  892. // Check if spinner exists and is an object of different class
  893. if(_indefiniteAnimatedView && ![_indefiniteAnimatedView isKindOfClass:[SVIndefiniteAnimatedView class]]){
  894. [_indefiniteAnimatedView removeFromSuperview];
  895. _indefiniteAnimatedView = nil;
  896. }
  897. if(!_indefiniteAnimatedView){
  898. _indefiniteAnimatedView = [[SVIndefiniteAnimatedView alloc] initWithFrame:CGRectZero];
  899. }
  900. // Update styling
  901. SVIndefiniteAnimatedView *indefiniteAnimatedView = (SVIndefiniteAnimatedView*)_indefiniteAnimatedView;
  902. indefiniteAnimatedView.strokeColor = self.foregroundColorForStyle;
  903. indefiniteAnimatedView.strokeThickness = self.ringThickness;
  904. indefiniteAnimatedView.radius = self.statusLabel.text ? self.ringRadius : self.ringNoTextRadius;
  905. } else {
  906. // Check if spinner exists and is an object of different class
  907. if(_indefiniteAnimatedView && ![_indefiniteAnimatedView isKindOfClass:[UIActivityIndicatorView class]]){
  908. [_indefiniteAnimatedView removeFromSuperview];
  909. _indefiniteAnimatedView = nil;
  910. }
  911. if(!_indefiniteAnimatedView){
  912. _indefiniteAnimatedView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
  913. }
  914. // Update styling
  915. UIActivityIndicatorView *activityIndicatorView = (UIActivityIndicatorView*)_indefiniteAnimatedView;
  916. activityIndicatorView.color = self.foregroundColorForStyle;
  917. }
  918. [_indefiniteAnimatedView sizeToFit];
  919. return _indefiniteAnimatedView;
  920. }
  921. - (SVProgressAnimatedView*)ringView {
  922. if(!_ringView) {
  923. _ringView = [[SVProgressAnimatedView alloc] initWithFrame:CGRectZero];
  924. }
  925. // Update styling
  926. _ringView.strokeColor = self.foregroundColorForStyle;
  927. _ringView.strokeThickness = self.ringThickness;
  928. _ringView.radius = self.statusLabel.text ? self.ringRadius : self.ringNoTextRadius;
  929. return _ringView;
  930. }
  931. - (SVProgressAnimatedView*)backgroundRingView {
  932. if(!_backgroundRingView) {
  933. _backgroundRingView = [[SVProgressAnimatedView alloc] initWithFrame:CGRectZero];
  934. _backgroundRingView.strokeEnd = 1.0f;
  935. }
  936. // Update styling
  937. _backgroundRingView.strokeColor = [self.foregroundColorForStyle colorWithAlphaComponent:0.1f];
  938. _backgroundRingView.strokeThickness = self.ringThickness;
  939. _backgroundRingView.radius = self.statusLabel.text ? self.ringRadius : self.ringNoTextRadius;
  940. return _backgroundRingView;
  941. }
  942. - (void)cancelRingLayerAnimation {
  943. // Animate value update, stop animation
  944. [CATransaction begin];
  945. [CATransaction setDisableActions:YES];
  946. [self.hudView.layer removeAllAnimations];
  947. self.ringView.strokeEnd = 0.0f;
  948. [CATransaction commit];
  949. // Remove from view
  950. [self.ringView removeFromSuperview];
  951. [self.backgroundRingView removeFromSuperview];
  952. }
  953. - (void)cancelIndefiniteAnimatedViewAnimation {
  954. // Stop animation
  955. if([self.indefiniteAnimatedView respondsToSelector:@selector(stopAnimating)]) {
  956. [(id)self.indefiniteAnimatedView stopAnimating];
  957. }
  958. // Remove from view
  959. [self.indefiniteAnimatedView removeFromSuperview];
  960. }
  961. #pragma mark - Utilities
  962. + (BOOL)isVisible {
  963. return ([self sharedView].alpha > 0);
  964. }
  965. #pragma mark - Getters
  966. + (NSTimeInterval)displayDurationForString:(NSString*)string {
  967. return MAX((float)string.length * 0.06 + 0.5, [self sharedView].minimumDismissTimeInterval);
  968. }
  969. - (UIColor*)foregroundColorForStyle {
  970. if(self.defaultStyle == SVProgressHUDStyleLight) {
  971. return [UIColor blackColor];
  972. } else if(self.defaultStyle == SVProgressHUDStyleDark) {
  973. return [UIColor whiteColor];
  974. } else {
  975. return self.foregroundColor;
  976. }
  977. }
  978. - (UIColor*)backgroundColorForStyle {
  979. if(self.defaultStyle == SVProgressHUDStyleLight) {
  980. return [UIColor whiteColor];
  981. } else if(self.defaultStyle == SVProgressHUDStyleDark) {
  982. return [UIColor blackColor];
  983. } else {
  984. return self.backgroundColor;
  985. }
  986. }
  987. - (UIImage*)image:(UIImage*)image withTintColor:(UIColor*)color {
  988. CGRect rect = CGRectMake(0.0f, 0.0f, image.size.width, image.size.height);
  989. // 传入的View.frame.size是0的话,直接返回nil,防止 UIGraphicsBeginImageContext() 传入0,导致崩溃
  990. if (CGSizeEqualToSize(rect.size, CGSizeZero)) {
  991. return nil;
  992. }
  993. UIGraphicsBeginImageContextWithOptions(rect.size, NO, image.scale);
  994. CGContextRef c = UIGraphicsGetCurrentContext();
  995. [image drawInRect:rect];
  996. CGContextSetFillColorWithColor(c, [color CGColor]);
  997. CGContextSetBlendMode(c, kCGBlendModeSourceAtop);
  998. CGContextFillRect(c, rect);
  999. UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
  1000. UIGraphicsEndImageContext();
  1001. return tintedImage;
  1002. }
  1003. - (BOOL)isClear { // used for iOS 7 and above
  1004. return (self.defaultMaskType == SVProgressHUDMaskTypeClear || self.defaultMaskType == SVProgressHUDMaskTypeNone);
  1005. }
  1006. - (UIControl*)overlayView {
  1007. if(!_overlayView) {
  1008. #if !defined(SV_APP_EXTENSIONS)
  1009. CGRect windowBounds = [[[UIApplication sharedApplication] delegate] window].bounds;
  1010. _overlayView = [[UIControl alloc] initWithFrame:windowBounds];
  1011. #else
  1012. _overlayView = [[UIControl alloc] initWithFrame:[UIScreen mainScreen].bounds];
  1013. #endif
  1014. _overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  1015. _overlayView.backgroundColor = [UIColor clearColor];
  1016. [_overlayView addTarget:self action:@selector(overlayViewDidReceiveTouchEvent:forEvent:) forControlEvents:UIControlEventTouchDown];
  1017. }
  1018. return _overlayView;
  1019. }
  1020. - (UIView*)hudView {
  1021. if(!_hudView) {
  1022. _hudView = [[UIView alloc] initWithFrame:CGRectZero];
  1023. _hudView.layer.masksToBounds = YES;
  1024. _hudView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin;
  1025. }
  1026. // Update styling
  1027. _hudView.layer.cornerRadius = self.cornerRadius;
  1028. _hudView.backgroundColor = self.backgroundColorForStyle;
  1029. return _hudView;
  1030. }
  1031. - (UILabel*)statusLabel {
  1032. if(!_statusLabel) {
  1033. _statusLabel = [[UILabel alloc] initWithFrame:CGRectZero];
  1034. _statusLabel.backgroundColor = [UIColor clearColor];
  1035. _statusLabel.adjustsFontSizeToFitWidth = YES;
  1036. _statusLabel.textAlignment = NSTextAlignmentCenter;
  1037. _statusLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
  1038. _statusLabel.numberOfLines = 0;
  1039. }
  1040. if(!_statusLabel.superview) {
  1041. [self.hudView addSubview:_statusLabel];
  1042. }
  1043. // Update styling
  1044. _statusLabel.textColor = self.foregroundColorForStyle;
  1045. _statusLabel.font = self.font;
  1046. return _statusLabel;
  1047. }
  1048. - (UIImageView*)imageView {
  1049. if(!_imageView) {
  1050. _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 28.0f, 28.0f)];
  1051. }
  1052. if(!_imageView.superview) {
  1053. [self.hudView addSubview:_imageView];
  1054. }
  1055. return _imageView;
  1056. }
  1057. - (CGFloat)visibleKeyboardHeight {
  1058. #if !defined(SV_APP_EXTENSIONS)
  1059. UIWindow *keyboardWindow = nil;
  1060. for (UIWindow *testWindow in [[UIApplication sharedApplication] windows]) {
  1061. if(![[testWindow class] isEqual:[UIWindow class]]) {
  1062. keyboardWindow = testWindow;
  1063. break;
  1064. }
  1065. }
  1066. for (__strong UIView *possibleKeyboard in [keyboardWindow subviews]) {
  1067. if([possibleKeyboard isKindOfClass:NSClassFromString(@"UIPeripheralHostView")] || [possibleKeyboard isKindOfClass:NSClassFromString(@"UIKeyboard")]) {
  1068. return CGRectGetHeight(possibleKeyboard.bounds);
  1069. } else if([possibleKeyboard isKindOfClass:NSClassFromString(@"UIInputSetContainerView")]) {
  1070. for (__strong UIView *possibleKeyboardSubview in [possibleKeyboard subviews]) {
  1071. if([possibleKeyboardSubview isKindOfClass:NSClassFromString(@"UIInputSetHostView")]) {
  1072. return CGRectGetHeight(possibleKeyboardSubview.bounds);
  1073. }
  1074. }
  1075. }
  1076. }
  1077. #endif
  1078. return 0;
  1079. }
  1080. #pragma mark - UIAppearance Setters
  1081. - (void)setDefaultStyle:(SVProgressHUDStyle)style {
  1082. if (!_isInitializing) _defaultStyle = style;
  1083. }
  1084. - (void)setDefaultMaskType:(SVProgressHUDMaskType)maskType {
  1085. if (!_isInitializing) _defaultMaskType = maskType;
  1086. }
  1087. - (void)setDefaultAnimationType:(SVProgressHUDAnimationType)animationType {
  1088. if (!_isInitializing) _defaultAnimationType = animationType;
  1089. }
  1090. - (void)setMinimumSize:(CGSize)minimumSize {
  1091. if (!_isInitializing) _minimumSize = minimumSize;
  1092. }
  1093. - (void)setRingThickness:(CGFloat)ringThickness {
  1094. if (!_isInitializing) _ringThickness = ringThickness;
  1095. }
  1096. - (void)setRingRadius:(CGFloat)ringRadius {
  1097. if (!_isInitializing) _ringRadius = ringRadius;
  1098. }
  1099. - (void)setRingNoTextRadius:(CGFloat)ringNoTextRadius {
  1100. if (!_isInitializing) _ringNoTextRadius = ringNoTextRadius;
  1101. }
  1102. - (void)setCornerRadius:(CGFloat)cornerRadius {
  1103. if (!_isInitializing) _cornerRadius = cornerRadius;
  1104. }
  1105. - (void)setFont:(UIFont*)font {
  1106. if (!_isInitializing) _font = font;
  1107. }
  1108. - (void)setForegroundColor:(UIColor*)color {
  1109. if (!_isInitializing) _foregroundColor = color;
  1110. }
  1111. - (void)setBackgroundColor:(UIColor*)color {
  1112. if (!_isInitializing) _backgroundColor = color;
  1113. }
  1114. - (void)setBackgroundLayerColor:(UIColor*)color {
  1115. if (!_isInitializing) _backgroundLayerColor = color;
  1116. }
  1117. - (void)setInfoImage:(UIImage*)image {
  1118. if (!_isInitializing) _infoImage = image;
  1119. }
  1120. - (void)setSuccessImage:(UIImage*)image {
  1121. if (!_isInitializing) _successImage = image;
  1122. }
  1123. - (void)setErrorImage:(UIImage*)image {
  1124. if (!_isInitializing) _errorImage = image;
  1125. }
  1126. - (void)setViewForExtension:(UIView*)view {
  1127. if (!_isInitializing) _viewForExtension = view;
  1128. }
  1129. - (void)setOffsetFromCenter:(UIOffset)offset {
  1130. if (!_isInitializing) _offsetFromCenter = offset;
  1131. }
  1132. - (void)setMinimumDismissTimeInterval:(NSTimeInterval)minimumDismissTimeInterval {
  1133. if (!_isInitializing) _minimumDismissTimeInterval = minimumDismissTimeInterval;
  1134. }
  1135. - (void)setFadeInAnimationDuration:(NSTimeInterval)duration {
  1136. if (!_isInitializing) _fadeInAnimationDuration = duration;
  1137. }
  1138. - (void)setFadeOutAnimationDuration:(NSTimeInterval)duration {
  1139. if (!_isInitializing) _fadeOutAnimationDuration = duration;
  1140. }
  1141. @end