ProgressHUD.m 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. //
  2. // Copyright (c) 2016 Related Code - http://relatedcode.com
  3. //
  4. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  5. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  6. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  7. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  8. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  9. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  10. // THE SOFTWARE.
  11. #import "ProgressHUD.h"
  12. //-------------------------------------------------------------------------------------------------------------------------------------------------
  13. @interface ProgressHUD()
  14. {
  15. UIWindow *window;
  16. UIView *viewBackground;
  17. UIToolbar *toolbarHUD;
  18. UIActivityIndicatorView *spinner;
  19. UIImageView *imageView;
  20. UILabel *labelStatus;
  21. }
  22. @end
  23. //-------------------------------------------------------------------------------------------------------------------------------------------------
  24. @implementation ProgressHUD
  25. //-------------------------------------------------------------------------------------------------------------------------------------------------
  26. + (ProgressHUD *)shared
  27. //-------------------------------------------------------------------------------------------------------------------------------------------------
  28. {
  29. static dispatch_once_t once;
  30. static ProgressHUD *progressHUD;
  31. //---------------------------------------------------------------------------------------------------------------------------------------------
  32. dispatch_once(&once, ^{ progressHUD = [[ProgressHUD alloc] init]; });
  33. //---------------------------------------------------------------------------------------------------------------------------------------------
  34. return progressHUD;
  35. }
  36. #pragma mark - Display methods
  37. //-------------------------------------------------------------------------------------------------------------------------------------------------
  38. + (void)dismiss
  39. //-------------------------------------------------------------------------------------------------------------------------------------------------
  40. {
  41. dispatch_async(dispatch_get_main_queue(), ^{
  42. [[self shared] hudHide];
  43. });
  44. }
  45. //-------------------------------------------------------------------------------------------------------------------------------------------------
  46. + (void)show
  47. //-------------------------------------------------------------------------------------------------------------------------------------------------
  48. {
  49. dispatch_async(dispatch_get_main_queue(), ^{
  50. [[self shared] hudCreate:nil image:nil spin:YES hide:NO interaction:YES];
  51. });
  52. }
  53. //-------------------------------------------------------------------------------------------------------------------------------------------------
  54. + (void)show:(NSString *)status
  55. //-------------------------------------------------------------------------------------------------------------------------------------------------
  56. {
  57. dispatch_async(dispatch_get_main_queue(), ^{
  58. [[self shared] hudCreate:status image:nil spin:YES hide:NO interaction:YES];
  59. });
  60. }
  61. //-------------------------------------------------------------------------------------------------------------------------------------------------
  62. + (void)show:(NSString *)status Interaction:(BOOL)interaction
  63. //-------------------------------------------------------------------------------------------------------------------------------------------------
  64. {
  65. dispatch_async(dispatch_get_main_queue(), ^{
  66. [[self shared] hudCreate:status image:nil spin:YES hide:NO interaction:interaction];
  67. });
  68. }
  69. //-------------------------------------------------------------------------------------------------------------------------------------------------
  70. + (void)showSuccess
  71. //-------------------------------------------------------------------------------------------------------------------------------------------------
  72. {
  73. dispatch_async(dispatch_get_main_queue(), ^{
  74. [[self shared] hudCreate:nil image:[self shared].imageSuccess spin:NO hide:YES interaction:YES];
  75. });
  76. }
  77. //-------------------------------------------------------------------------------------------------------------------------------------------------
  78. + (void)showSuccess:(NSString *)status
  79. //-------------------------------------------------------------------------------------------------------------------------------------------------
  80. {
  81. dispatch_async(dispatch_get_main_queue(), ^{
  82. [[self shared] hudCreate:status image:[self shared].imageSuccess spin:NO hide:YES interaction:YES];
  83. });
  84. }
  85. //-------------------------------------------------------------------------------------------------------------------------------------------------
  86. + (void)showSuccess:(NSString *)status Interaction:(BOOL)interaction
  87. //-------------------------------------------------------------------------------------------------------------------------------------------------
  88. {
  89. dispatch_async(dispatch_get_main_queue(), ^{
  90. [[self shared] hudCreate:status image:[self shared].imageSuccess spin:NO hide:YES interaction:interaction];
  91. });
  92. }
  93. //-------------------------------------------------------------------------------------------------------------------------------------------------
  94. + (void)showError
  95. //-------------------------------------------------------------------------------------------------------------------------------------------------
  96. {
  97. dispatch_async(dispatch_get_main_queue(), ^{
  98. [[self shared] hudCreate:nil image:[self shared].imageError spin:NO hide:YES interaction:YES];
  99. });
  100. }
  101. //-------------------------------------------------------------------------------------------------------------------------------------------------
  102. + (void)showError:(NSString *)status
  103. //-------------------------------------------------------------------------------------------------------------------------------------------------
  104. {
  105. dispatch_async(dispatch_get_main_queue(), ^{
  106. [[self shared] hudCreate:status image:[self shared].imageError spin:NO hide:YES interaction:YES];
  107. });
  108. }
  109. //-------------------------------------------------------------------------------------------------------------------------------------------------
  110. + (void)showError:(NSString *)status Interaction:(BOOL)interaction
  111. //-------------------------------------------------------------------------------------------------------------------------------------------------
  112. {
  113. dispatch_async(dispatch_get_main_queue(), ^{
  114. [[self shared] hudCreate:status image:[self shared].imageError spin:NO hide:YES interaction:interaction];
  115. });
  116. }
  117. #pragma mark - Property methods
  118. //-------------------------------------------------------------------------------------------------------------------------------------------------
  119. + (void)statusFont:(UIFont *)font { [self shared].statusFont = font; }
  120. + (void)statusColor:(UIColor *)color { [self shared].statusColor = color; }
  121. + (void)spinnerColor:(UIColor *)color { [self shared].spinnerColor = color; }
  122. + (void)hudColor:(UIColor *)color { [self shared].hudColor = color; }
  123. + (void)backgroundColor:(UIColor *)color { [self shared].backgroundColor = color; }
  124. + (void)imageSuccess:(UIImage *)image { [self shared].imageSuccess = image; }
  125. + (void)imageError:(UIImage *)image { [self shared].imageError = image; }
  126. //-------------------------------------------------------------------------------------------------------------------------------------------------
  127. #pragma mark -
  128. //-------------------------------------------------------------------------------------------------------------------------------------------------
  129. - (id)init
  130. //-------------------------------------------------------------------------------------------------------------------------------------------------
  131. {
  132. self = [super initWithFrame:[[UIScreen mainScreen] bounds]];
  133. //---------------------------------------------------------------------------------------------------------------------------------------------
  134. self.statusFont = [UIFont boldSystemFontOfSize:16];
  135. self.statusColor = [UIColor blackColor];
  136. self.spinnerColor = [UIColor colorWithRed:251.0/255.0 green:71.0/255.0 blue:71.0/255.0 alpha:1.0];
  137. self.hudColor = [UIColor colorWithWhite:0.0 alpha:0.1];
  138. self.backgroundColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.2];
  139. NSBundle *bundle = [NSBundle bundleForClass:[self class]];
  140. self.imageSuccess = [UIImage imageNamed:@"ProgressHUD.bundle/progresshud-success" inBundle:bundle compatibleWithTraitCollection:nil];
  141. self.imageError = [UIImage imageNamed:@"ProgressHUD.bundle/progresshud-error" inBundle:bundle compatibleWithTraitCollection:nil];
  142. //---------------------------------------------------------------------------------------------------------------------------------------------
  143. id<UIApplicationDelegate> delegate = [[UIApplication sharedApplication] delegate];
  144. //---------------------------------------------------------------------------------------------------------------------------------------------
  145. if ([delegate respondsToSelector:@selector(window)])
  146. window = [delegate performSelector:@selector(window)];
  147. else window = [[UIApplication sharedApplication] keyWindow];
  148. //---------------------------------------------------------------------------------------------------------------------------------------------
  149. viewBackground = nil; toolbarHUD = nil; spinner = nil; imageView = nil; labelStatus = nil;
  150. //---------------------------------------------------------------------------------------------------------------------------------------------
  151. self.alpha = 0;
  152. //---------------------------------------------------------------------------------------------------------------------------------------------
  153. return self;
  154. }
  155. #pragma mark -
  156. //-------------------------------------------------------------------------------------------------------------------------------------------------
  157. - (void)hudCreate:(NSString *)status image:(UIImage *)image spin:(BOOL)spin hide:(BOOL)hide interaction:(BOOL)interaction
  158. //-------------------------------------------------------------------------------------------------------------------------------------------------
  159. {
  160. if (toolbarHUD == nil)
  161. {
  162. toolbarHUD = [[UIToolbar alloc] initWithFrame:CGRectZero];
  163. toolbarHUD.translucent = YES;
  164. toolbarHUD.backgroundColor = self.hudColor;
  165. toolbarHUD.layer.cornerRadius = 10;
  166. toolbarHUD.layer.masksToBounds = YES;
  167. [self registerNotifications];
  168. }
  169. //---------------------------------------------------------------------------------------------------------------------------------------------
  170. if (toolbarHUD.superview == nil)
  171. {
  172. if (interaction == NO)
  173. {
  174. viewBackground = [[UIView alloc] initWithFrame:window.frame];
  175. viewBackground.backgroundColor = self.backgroundColor;
  176. [window addSubview:viewBackground];
  177. [viewBackground addSubview:toolbarHUD];
  178. }
  179. else [window addSubview:toolbarHUD];
  180. }
  181. //---------------------------------------------------------------------------------------------------------------------------------------------
  182. if (spinner == nil)
  183. {
  184. spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
  185. spinner.color = self.spinnerColor;
  186. spinner.hidesWhenStopped = YES;
  187. }
  188. if (spinner.superview == nil) [toolbarHUD addSubview:spinner];
  189. //---------------------------------------------------------------------------------------------------------------------------------------------
  190. if (imageView == nil)
  191. {
  192. imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 28, 28)];
  193. }
  194. if (imageView.superview == nil) [toolbarHUD addSubview:imageView];
  195. //---------------------------------------------------------------------------------------------------------------------------------------------
  196. if (labelStatus == nil)
  197. {
  198. labelStatus = [[UILabel alloc] initWithFrame:CGRectZero];
  199. labelStatus.font = self.statusFont;
  200. labelStatus.textColor = self.statusColor;
  201. labelStatus.backgroundColor = [UIColor clearColor];
  202. labelStatus.textAlignment = NSTextAlignmentCenter;
  203. labelStatus.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
  204. labelStatus.numberOfLines = 0;
  205. }
  206. if (labelStatus.superview == nil) [toolbarHUD addSubview:labelStatus];
  207. //---------------------------------------------------------------------------------------------------------------------------------------------
  208. //---------------------------------------------------------------------------------------------------------------------------------------------
  209. labelStatus.text = status;
  210. labelStatus.hidden = (status == nil) ? YES : NO;
  211. //---------------------------------------------------------------------------------------------------------------------------------------------
  212. imageView.image = image;
  213. imageView.hidden = (image == nil) ? YES : NO;
  214. //---------------------------------------------------------------------------------------------------------------------------------------------
  215. if (spin) [spinner startAnimating]; else [spinner stopAnimating];
  216. //---------------------------------------------------------------------------------------------------------------------------------------------
  217. //---------------------------------------------------------------------------------------------------------------------------------------------
  218. [self hudSize];
  219. [self hudPosition:nil];
  220. [self hudShow];
  221. //---------------------------------------------------------------------------------------------------------------------------------------------
  222. if (hide) [self timedHide];
  223. }
  224. //-------------------------------------------------------------------------------------------------------------------------------------------------
  225. - (void)registerNotifications
  226. //-------------------------------------------------------------------------------------------------------------------------------------------------
  227. {
  228. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hudPosition:)
  229. name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
  230. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hudPosition:) name:UIKeyboardWillHideNotification object:nil];
  231. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hudPosition:) name:UIKeyboardDidHideNotification object:nil];
  232. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hudPosition:) name:UIKeyboardWillShowNotification object:nil];
  233. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hudPosition:) name:UIKeyboardDidShowNotification object:nil];
  234. }
  235. //-------------------------------------------------------------------------------------------------------------------------------------------------
  236. - (void)hudDestroy
  237. //-------------------------------------------------------------------------------------------------------------------------------------------------
  238. {
  239. [[NSNotificationCenter defaultCenter] removeObserver:self];
  240. //---------------------------------------------------------------------------------------------------------------------------------------------
  241. [labelStatus removeFromSuperview]; labelStatus = nil;
  242. [imageView removeFromSuperview]; imageView = nil;
  243. [spinner removeFromSuperview]; spinner = nil;
  244. [toolbarHUD removeFromSuperview]; toolbarHUD = nil;
  245. [viewBackground removeFromSuperview]; viewBackground = nil;
  246. }
  247. #pragma mark -
  248. //-------------------------------------------------------------------------------------------------------------------------------------------------
  249. - (void)hudSize
  250. //-------------------------------------------------------------------------------------------------------------------------------------------------
  251. {
  252. CGRect rectLabel = CGRectZero;
  253. CGFloat widthHUD = 100, heightHUD = 100;
  254. //---------------------------------------------------------------------------------------------------------------------------------------------
  255. if (labelStatus.text != nil)
  256. {
  257. NSDictionary *attributes = @{NSFontAttributeName:labelStatus.font};
  258. NSInteger options = NSStringDrawingUsesFontLeading | NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin;
  259. rectLabel = [labelStatus.text boundingRectWithSize:CGSizeMake(200, 300) options:options attributes:attributes context:NULL];
  260. widthHUD = rectLabel.size.width + 50;
  261. heightHUD = rectLabel.size.height + 75;
  262. if (widthHUD < 100) widthHUD = 100;
  263. if (heightHUD < 100) heightHUD = 100;
  264. rectLabel.origin.x = (widthHUD - rectLabel.size.width) / 2;
  265. rectLabel.origin.y = (heightHUD - rectLabel.size.height) / 2 + 25;
  266. }
  267. //---------------------------------------------------------------------------------------------------------------------------------------------
  268. toolbarHUD.bounds = CGRectMake(0, 0, widthHUD, heightHUD);
  269. //---------------------------------------------------------------------------------------------------------------------------------------------
  270. CGFloat imageX = widthHUD/2;
  271. CGFloat imageY = (labelStatus.text == nil) ? heightHUD/2 : 36;
  272. imageView.center = spinner.center = CGPointMake(imageX, imageY);
  273. //---------------------------------------------------------------------------------------------------------------------------------------------
  274. labelStatus.frame = rectLabel;
  275. }
  276. //-------------------------------------------------------------------------------------------------------------------------------------------------
  277. - (void)hudPosition:(NSNotification *)notification
  278. //-------------------------------------------------------------------------------------------------------------------------------------------------
  279. {
  280. CGFloat heightKeyboard = 0;
  281. NSTimeInterval duration = 0;
  282. //---------------------------------------------------------------------------------------------------------------------------------------------
  283. if (notification != nil)
  284. {
  285. NSDictionary *info = [notification userInfo];
  286. CGRect keyboard = [[info valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
  287. duration = [[info valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
  288. if ((notification.name == UIKeyboardWillShowNotification) || (notification.name == UIKeyboardDidShowNotification))
  289. {
  290. heightKeyboard = keyboard.size.height;
  291. }
  292. }
  293. else heightKeyboard = [self keyboardHeight];
  294. //---------------------------------------------------------------------------------------------------------------------------------------------
  295. CGRect screen = [UIScreen mainScreen].bounds;
  296. CGPoint center = CGPointMake(screen.size.width/2, (screen.size.height-heightKeyboard)/2);
  297. //---------------------------------------------------------------------------------------------------------------------------------------------
  298. [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
  299. toolbarHUD.center = CGPointMake(center.x, center.y);
  300. } completion:nil];
  301. //---------------------------------------------------------------------------------------------------------------------------------------------
  302. if (viewBackground != nil) viewBackground.frame = window.frame;
  303. }
  304. //-------------------------------------------------------------------------------------------------------------------------------------------------
  305. - (CGFloat)keyboardHeight
  306. //-------------------------------------------------------------------------------------------------------------------------------------------------
  307. {
  308. for (UIWindow *testWindow in [[UIApplication sharedApplication] windows])
  309. {
  310. if ([[testWindow class] isEqual:[UIWindow class]] == NO)
  311. {
  312. for (UIView *possibleKeyboard in [testWindow subviews])
  313. {
  314. if ([[possibleKeyboard description] hasPrefix:@"<UIPeripheralHostView"])
  315. {
  316. return possibleKeyboard.bounds.size.height;
  317. }
  318. else if ([[possibleKeyboard description] hasPrefix:@"<UIInputSetContainerView"])
  319. {
  320. for (UIView *hostKeyboard in [possibleKeyboard subviews])
  321. {
  322. if ([[hostKeyboard description] hasPrefix:@"<UIInputSetHost"])
  323. {
  324. return hostKeyboard.frame.size.height;
  325. }
  326. }
  327. }
  328. }
  329. }
  330. }
  331. return 0;
  332. }
  333. #pragma mark -
  334. //-------------------------------------------------------------------------------------------------------------------------------------------------
  335. - (void)hudShow
  336. //-------------------------------------------------------------------------------------------------------------------------------------------------
  337. {
  338. if (self.alpha == 0)
  339. {
  340. self.alpha = 1;
  341. toolbarHUD.alpha = 0;
  342. toolbarHUD.transform = CGAffineTransformScale(toolbarHUD.transform, 1.4, 1.4);
  343. UIViewAnimationOptions options = UIViewAnimationOptionAllowUserInteraction | UIViewAnimationCurveEaseOut;
  344. [UIView animateWithDuration:0.15 delay:0 options:options animations:^{
  345. toolbarHUD.transform = CGAffineTransformScale(toolbarHUD.transform, 1/1.4, 1/1.4);
  346. toolbarHUD.alpha = 1;
  347. } completion:nil];
  348. }
  349. }
  350. //-------------------------------------------------------------------------------------------------------------------------------------------------
  351. - (void)hudHide
  352. //-------------------------------------------------------------------------------------------------------------------------------------------------
  353. {
  354. if (self.alpha == 1)
  355. {
  356. UIViewAnimationOptions options = UIViewAnimationOptionAllowUserInteraction | UIViewAnimationCurveEaseIn;
  357. [UIView animateWithDuration:0.15 delay:0 options:options animations:^{
  358. toolbarHUD.transform = CGAffineTransformScale(toolbarHUD.transform, 0.7, 0.7);
  359. toolbarHUD.alpha = 0;
  360. }
  361. completion:^(BOOL finished) {
  362. [self hudDestroy];
  363. self.alpha = 0;
  364. }];
  365. }
  366. }
  367. //-------------------------------------------------------------------------------------------------------------------------------------------------
  368. - (void)timedHide
  369. //-------------------------------------------------------------------------------------------------------------------------------------------------
  370. {
  371. NSTimeInterval delay = labelStatus.text.length * 0.04 + 0.5;
  372. dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC);
  373. dispatch_after(time, dispatch_get_main_queue(), ^(void){ [self hudHide]; });
  374. }
  375. @end