PageRenderTime 63ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/WebServiceDemo/Required_Libs/MBPProgressHUD/MBProgressHUD.m

https://gitlab.com/praveenvelanati/ios-demo
Objective C | 756 lines | 615 code | 98 blank | 43 comment | 100 complexity | fbd28085352d536d5509dcdd7f37abda MD5 | raw file
  1. //
  2. // MBProgressHUD.m
  3. // Version 0.5
  4. // Created by Matej Bukovinski on 2.4.09.
  5. //
  6. #import "MBProgressHUD.h"
  7. #if __has_feature(objc_arc)
  8. #define MB_AUTORELEASE(exp) exp
  9. #define MB_RELEASE(exp) exp
  10. #define MB_RETAIN(exp) exp
  11. #else
  12. #define MB_AUTORELEASE(exp) [exp autorelease]
  13. #define MB_RELEASE(exp) [exp release]
  14. #define MB_RETAIN(exp) [exp retain]
  15. #endif
  16. static const CGFloat kPadding = 4.f;
  17. static const CGFloat kLabelFontSize = 16.f;
  18. static const CGFloat kDetailsLabelFontSize = 12.f;
  19. @interface MBProgressHUD ()
  20. - (void)setupLabels;
  21. - (void)registerForKVO;
  22. - (void)unregisterFromKVO;
  23. - (NSArray *)observableKeypaths;
  24. - (void)registerForNotifications;
  25. - (void)unregisterFromNotifications;
  26. - (void)updateUIForKeypath:(NSString *)keyPath;
  27. - (void)hideUsingAnimation:(BOOL)animated;
  28. - (void)showUsingAnimation:(BOOL)animated;
  29. - (void)done;
  30. - (void)updateIndicators;
  31. - (void)handleGraceTimer:(NSTimer *)theTimer;
  32. - (void)handleMinShowTimer:(NSTimer *)theTimer;
  33. - (void)setTransformForCurrentOrientation:(BOOL)animated;
  34. - (void)cleanUp;
  35. - (void)launchExecution;
  36. - (void)deviceOrientationDidChange:(NSNotification *)notification;
  37. - (void)hideDelayed:(NSNumber *)animated;
  38. @property (MB_STRONG) UIView *indicator;
  39. @property (MB_STRONG) NSTimer *graceTimer;
  40. @property (MB_STRONG) NSTimer *minShowTimer;
  41. @property (MB_STRONG) NSDate *showStarted;
  42. @property (assign) CGSize size;
  43. @end
  44. @implementation MBProgressHUD {
  45. BOOL useAnimation;
  46. SEL methodForExecution;
  47. id targetForExecution;
  48. id objectForExecution;
  49. UILabel *label;
  50. UILabel *detailsLabel;
  51. BOOL isFinished;
  52. CGAffineTransform rotationTransform;
  53. }
  54. #pragma mark - Properties
  55. @synthesize animationType;
  56. @synthesize delegate;
  57. @synthesize opacity;
  58. @synthesize labelFont;
  59. @synthesize detailsLabelFont;
  60. @synthesize indicator;
  61. @synthesize xOffset;
  62. @synthesize yOffset;
  63. @synthesize minSize;
  64. @synthesize square;
  65. @synthesize margin;
  66. @synthesize dimBackground;
  67. @synthesize graceTime;
  68. @synthesize minShowTime;
  69. @synthesize graceTimer;
  70. @synthesize minShowTimer;
  71. @synthesize taskInProgress;
  72. @synthesize removeFromSuperViewOnHide;
  73. @synthesize customView;
  74. @synthesize showStarted;
  75. @synthesize mode;
  76. @synthesize labelText;
  77. @synthesize detailsLabelText;
  78. @synthesize progress;
  79. @synthesize size;
  80. #pragma mark - Class methods
  81. + (MBProgressHUD *)showHUDAddedTo:(UIView *)view animated:(BOOL)animated {
  82. MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView:view];
  83. [view addSubview:hud];
  84. [hud show:animated];
  85. return MB_AUTORELEASE(hud);
  86. }
  87. + (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated {
  88. MBProgressHUD *hud = [MBProgressHUD HUDForView:view];
  89. if (hud != nil) {
  90. hud.removeFromSuperViewOnHide = YES;
  91. [hud hide:animated];
  92. return YES;
  93. }
  94. return NO;
  95. }
  96. + (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated {
  97. NSArray *huds = [self allHUDsForView:view];
  98. for (MBProgressHUD *hud in huds) {
  99. hud.removeFromSuperViewOnHide = YES;
  100. [hud hide:animated];
  101. }
  102. return [huds count];
  103. }
  104. + (MBProgressHUD *)HUDForView:(UIView *)view {
  105. MBProgressHUD *hud = nil;
  106. NSArray *subviews = view.subviews;
  107. Class hudClass = [MBProgressHUD class];
  108. for (UIView *aView in subviews) {
  109. if ([aView isKindOfClass:hudClass]) {
  110. hud = (MBProgressHUD *)aView;
  111. }
  112. }
  113. return hud;
  114. }
  115. + (NSArray *)allHUDsForView:(UIView *)view {
  116. NSMutableArray *huds = [NSMutableArray array];
  117. NSArray *subviews = view.subviews;
  118. Class hudClass = [MBProgressHUD class];
  119. for (UIView *aView in subviews) {
  120. if ([aView isKindOfClass:hudClass]) {
  121. [huds addObject:aView];
  122. }
  123. }
  124. return [NSArray arrayWithArray:huds];
  125. }
  126. #pragma mark - Lifecycle
  127. - (id)initWithFrame:(CGRect)frame {
  128. self = [super initWithFrame:frame];
  129. if (self) {
  130. // Set default values for properties
  131. self.animationType = MBProgressHUDAnimationFade;
  132. self.mode = MBProgressHUDModeIndeterminate;
  133. self.labelText = nil;
  134. self.detailsLabelText = nil;
  135. self.opacity = 0.8f;
  136. self.labelFont = [UIFont boldSystemFontOfSize:kLabelFontSize];
  137. self.detailsLabelFont = [UIFont boldSystemFontOfSize:kDetailsLabelFontSize];
  138. self.xOffset = 0.0f;
  139. self.yOffset = 0.0f;
  140. self.dimBackground = NO;
  141. self.margin = 20.0f;
  142. self.graceTime = 0.0f;
  143. self.minShowTime = 0.0f;
  144. self.removeFromSuperViewOnHide = NO;
  145. self.minSize = CGSizeZero;
  146. self.square = NO;
  147. self.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin
  148. | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
  149. // Transparent background
  150. self.opaque = NO;
  151. self.backgroundColor = [UIColor clearColor];
  152. // Make it invisible for now
  153. self.alpha = 0.0f;
  154. taskInProgress = NO;
  155. rotationTransform = CGAffineTransformIdentity;
  156. [self setupLabels];
  157. [self updateIndicators];
  158. [self registerForKVO];
  159. [self registerForNotifications];
  160. }
  161. return self;
  162. }
  163. - (id)initWithView:(UIView *)view {
  164. NSAssert(view, @"View must not be nil.");
  165. id me = [self initWithFrame:view.bounds];
  166. // We need to take care of rotation ourselfs if we're adding the HUD to a window
  167. if ([view isKindOfClass:[UIWindow class]]) {
  168. [self setTransformForCurrentOrientation:NO];
  169. }
  170. return me;
  171. }
  172. - (id)initWithWindow:(UIWindow *)window {
  173. return [self initWithView:window];
  174. }
  175. - (void)dealloc {
  176. [self unregisterFromNotifications];
  177. [self unregisterFromKVO];
  178. #if !__has_feature(objc_arc)
  179. [indicator release];
  180. [label release];
  181. [detailsLabel release];
  182. [labelText release];
  183. [detailsLabelText release];
  184. [graceTimer release];
  185. [minShowTimer release];
  186. [showStarted release];
  187. [customView release];
  188. [super dealloc];
  189. #endif
  190. }
  191. #pragma mark - Show & hide
  192. - (void)show:(BOOL)animated {
  193. useAnimation = animated;
  194. // If the grace time is set postpone the HUD display
  195. if (self.graceTime > 0.0) {
  196. self.graceTimer = [NSTimer scheduledTimerWithTimeInterval:self.graceTime target:self
  197. selector:@selector(handleGraceTimer:) userInfo:nil repeats:NO];
  198. }
  199. // ... otherwise show the HUD imediately
  200. else {
  201. [self setNeedsDisplay];
  202. [self showUsingAnimation:useAnimation];
  203. }
  204. }
  205. - (void)hide:(BOOL)animated {
  206. useAnimation = animated;
  207. // If the minShow time is set, calculate how long the hud was shown,
  208. // and pospone the hiding operation if necessary
  209. if (self.minShowTime > 0.0 && showStarted) {
  210. NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:showStarted];
  211. if (interv < self.minShowTime) {
  212. self.minShowTimer = [NSTimer scheduledTimerWithTimeInterval:(self.minShowTime - interv) target:self
  213. selector:@selector(handleMinShowTimer:) userInfo:nil repeats:NO];
  214. return;
  215. }
  216. }
  217. // ... otherwise hide the HUD immediately
  218. [self hideUsingAnimation:useAnimation];
  219. }
  220. - (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay {
  221. [self performSelector:@selector(hideDelayed:) withObject:[NSNumber numberWithBool:animated] afterDelay:delay];
  222. }
  223. - (void)hideDelayed:(NSNumber *)animated {
  224. [self hide:[animated boolValue]];
  225. }
  226. #pragma mark - Timer callbacks
  227. - (void)handleGraceTimer:(NSTimer *)theTimer {
  228. // Show the HUD only if the task is still running
  229. if (taskInProgress) {
  230. [self setNeedsDisplay];
  231. [self showUsingAnimation:useAnimation];
  232. }
  233. }
  234. - (void)handleMinShowTimer:(NSTimer *)theTimer {
  235. [self hideUsingAnimation:useAnimation];
  236. }
  237. #pragma mark - Internal show & hide operations
  238. - (void)showUsingAnimation:(BOOL)animated {
  239. self.alpha = 0.0f;
  240. if (animated && animationType == MBProgressHUDAnimationZoom) {
  241. self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(1.5f, 1.5f));
  242. }
  243. self.showStarted = [NSDate date];
  244. // Fade in
  245. if (animated) {
  246. [UIView beginAnimations:nil context:NULL];
  247. [UIView setAnimationDuration:0.30];
  248. self.alpha = 1.0f;
  249. if (animationType == MBProgressHUDAnimationZoom) {
  250. self.transform = rotationTransform;
  251. }
  252. [UIView commitAnimations];
  253. }
  254. else {
  255. self.alpha = 1.0f;
  256. }
  257. }
  258. - (void)hideUsingAnimation:(BOOL)animated {
  259. // Fade out
  260. if (animated && showStarted) {
  261. [UIView beginAnimations:nil context:NULL];
  262. [UIView setAnimationDuration:0.30];
  263. [UIView setAnimationDelegate:self];
  264. [UIView setAnimationDidStopSelector:@selector(animationFinished:finished:context:)];
  265. // 0.02 prevents the hud from passing through touches during the animation the hud will get completely hidden
  266. // in the done method
  267. if (animationType == MBProgressHUDAnimationZoom) {
  268. self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(0.5f, 0.5f));
  269. }
  270. self.alpha = 0.02f;
  271. [UIView commitAnimations];
  272. }
  273. else {
  274. self.alpha = 0.0f;
  275. [self done];
  276. }
  277. self.showStarted = nil;
  278. }
  279. - (void)animationFinished:(NSString *)animationID finished:(BOOL)finished context:(void*)context {
  280. [self done];
  281. }
  282. - (void)done {
  283. isFinished = YES;
  284. self.alpha = 0.0f;
  285. if ([delegate respondsToSelector:@selector(hudWasHidden:)]) {
  286. [delegate performSelector:@selector(hudWasHidden:) withObject:self];
  287. }
  288. if (removeFromSuperViewOnHide) {
  289. [self removeFromSuperview];
  290. }
  291. }
  292. #pragma mark - Threading
  293. - (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated {
  294. methodForExecution = method;
  295. targetForExecution = MB_RETAIN(target);
  296. objectForExecution = MB_RETAIN(object);
  297. // Launch execution in new thread
  298. self.taskInProgress = YES;
  299. [NSThread detachNewThreadSelector:@selector(launchExecution) toTarget:self withObject:nil];
  300. // Show HUD view
  301. [self show:animated];
  302. }
  303. - (void)launchExecution {
  304. @autoreleasepool {
  305. #pragma clang diagnostic push
  306. #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
  307. // Start executing the requested task
  308. [targetForExecution performSelector:methodForExecution withObject:objectForExecution];
  309. #pragma clang diagnostic pop
  310. // Task completed, update view in main thread (note: view operations should
  311. // be done only in the main thread)
  312. [self performSelectorOnMainThread:@selector(cleanUp) withObject:nil waitUntilDone:NO];
  313. }
  314. }
  315. - (void)cleanUp {
  316. taskInProgress = NO;
  317. self.indicator = nil;
  318. #if !__has_feature(objc_arc)
  319. [targetForExecution release];
  320. [objectForExecution release];
  321. #endif
  322. [self hide:useAnimation];
  323. }
  324. #pragma mark - UI
  325. - (void)setupLabels {
  326. label = [[UILabel alloc] initWithFrame:self.bounds];
  327. label.adjustsFontSizeToFitWidth = NO;
  328. label.textAlignment = UITextAlignmentCenter;
  329. label.opaque = NO;
  330. label.backgroundColor = [UIColor clearColor];
  331. label.textColor = [UIColor whiteColor];
  332. label.font = self.labelFont;
  333. label.text = self.labelText;
  334. [self addSubview:label];
  335. detailsLabel = [[UILabel alloc] initWithFrame:self.bounds];
  336. detailsLabel.font = self.detailsLabelFont;
  337. detailsLabel.adjustsFontSizeToFitWidth = NO;
  338. detailsLabel.textAlignment = UITextAlignmentCenter;
  339. detailsLabel.opaque = NO;
  340. detailsLabel.backgroundColor = [UIColor clearColor];
  341. detailsLabel.textColor = [UIColor whiteColor];
  342. detailsLabel.numberOfLines = 0;
  343. detailsLabel.font = self.detailsLabelFont;
  344. detailsLabel.text = self.detailsLabelText;
  345. [self addSubview:detailsLabel];
  346. }
  347. - (void)updateIndicators {
  348. BOOL isActivityIndicator = [indicator isKindOfClass:[UIActivityIndicatorView class]];
  349. BOOL isRoundIndicator = [indicator isKindOfClass:[MBRoundProgressView class]];
  350. if (mode == MBProgressHUDModeIndeterminate && !isActivityIndicator) {
  351. // Update to indeterminate indicator
  352. [indicator removeFromSuperview];
  353. self.indicator = MB_AUTORELEASE([[UIActivityIndicatorView alloc]
  354. initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]);
  355. [(UIActivityIndicatorView *)indicator startAnimating];
  356. [self addSubview:indicator];
  357. }
  358. else if (mode == MBProgressHUDModeDeterminate || mode == MBProgressHUDModeAnnularDeterminate) {
  359. if (!isRoundIndicator) {
  360. // Update to determinante indicator
  361. [indicator removeFromSuperview];
  362. self.indicator = MB_AUTORELEASE([[MBRoundProgressView alloc] init]);
  363. [self addSubview:indicator];
  364. }
  365. if (mode == MBProgressHUDModeAnnularDeterminate) {
  366. [(MBRoundProgressView *)indicator setAnnular:YES];
  367. }
  368. }
  369. else if (mode == MBProgressHUDModeCustomView && customView != indicator) {
  370. // Update custom view indicator
  371. [indicator removeFromSuperview];
  372. self.indicator = customView;
  373. [self addSubview:indicator];
  374. } else if (mode == MBProgressHUDModeText) {
  375. [indicator removeFromSuperview];
  376. self.indicator = nil;
  377. }
  378. }
  379. #pragma mark - Layout
  380. - (void)layoutSubviews {
  381. // Entirely cover the parent view
  382. UIView *parent = self.superview;
  383. if (parent) {
  384. self.frame = parent.bounds;
  385. }
  386. CGRect bounds = self.bounds;
  387. // Determine the total widt and height needed
  388. CGFloat maxWidth = bounds.size.width - 4 * margin;
  389. CGSize totalSize = CGSizeZero;
  390. CGRect indicatorF = indicator.bounds;
  391. indicatorF.size.width = MIN(indicatorF.size.width, maxWidth);
  392. totalSize.width = MAX(totalSize.width, indicatorF.size.width);
  393. totalSize.height += indicatorF.size.height;
  394. CGSize labelSize = [label.text sizeWithFont:label.font];
  395. labelSize.width = MIN(labelSize.width, maxWidth);
  396. totalSize.width = MAX(totalSize.width, labelSize.width);
  397. totalSize.height += labelSize.height;
  398. if (labelSize.height > 0.f && indicatorF.size.height > 0.f) {
  399. totalSize.height += kPadding;
  400. }
  401. CGFloat remainingHeight = bounds.size.height - totalSize.height - kPadding - 4 * margin;
  402. CGSize maxSize = CGSizeMake(maxWidth, remainingHeight);
  403. CGSize detailsLabelSize = [detailsLabel.text sizeWithFont:detailsLabel.font
  404. constrainedToSize:maxSize lineBreakMode:detailsLabel.lineBreakMode];
  405. totalSize.width = MAX(totalSize.width, detailsLabelSize.width);
  406. totalSize.height += detailsLabelSize.height;
  407. if (detailsLabelSize.height > 0.f && (indicatorF.size.height > 0.f || labelSize.height > 0.f)) {
  408. totalSize.height += kPadding;
  409. }
  410. totalSize.width += 2 * margin;
  411. totalSize.height += 2 * margin;
  412. // Position elements
  413. CGFloat yPos = roundf(((bounds.size.height - totalSize.height) / 2)) + margin + yOffset;
  414. CGFloat xPos = xOffset;
  415. indicatorF.origin.y = yPos;
  416. indicatorF.origin.x = roundf((bounds.size.width - indicatorF.size.width) / 2) + xPos;
  417. indicator.frame = indicatorF;
  418. yPos += indicatorF.size.height;
  419. if (labelSize.height > 0.f && indicatorF.size.height > 0.f) {
  420. yPos += kPadding;
  421. }
  422. CGRect labelF;
  423. labelF.origin.y = yPos;
  424. labelF.origin.x = roundf((bounds.size.width - labelSize.width) / 2) + xPos;
  425. labelF.size = labelSize;
  426. label.frame = labelF;
  427. yPos += labelF.size.height;
  428. if (detailsLabelSize.height > 0.f && (indicatorF.size.height > 0.f || labelSize.height > 0.f)) {
  429. yPos += kPadding;
  430. }
  431. CGRect detailsLabelF;
  432. detailsLabelF.origin.y = yPos;
  433. detailsLabelF.origin.x = roundf((bounds.size.width - detailsLabelSize.width) / 2) + xPos;
  434. detailsLabelF.size = detailsLabelSize;
  435. detailsLabel.frame = detailsLabelF;
  436. // Enforce minsize and quare rules
  437. if (square) {
  438. CGFloat max = MAX(totalSize.width, totalSize.height);
  439. if (max <= bounds.size.width - 2 * margin) {
  440. totalSize.width = max;
  441. }
  442. if (max <= bounds.size.height - 2 * margin) {
  443. totalSize.height = max;
  444. }
  445. }
  446. if (totalSize.width < minSize.width) {
  447. totalSize.width = minSize.width;
  448. }
  449. if (totalSize.height < minSize.height) {
  450. totalSize.height = minSize.height;
  451. }
  452. self.size = totalSize;
  453. }
  454. #pragma mark BG Drawing
  455. - (void)drawRect:(CGRect)rect {
  456. CGContextRef context = UIGraphicsGetCurrentContext();
  457. if (dimBackground) {
  458. //Gradient colours
  459. size_t gradLocationsNum = 2;
  460. CGFloat gradLocations[2] = {0.0f, 1.0f};
  461. CGFloat gradColors[8] = {0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.75f};
  462. CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  463. CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradColors, gradLocations, gradLocationsNum);
  464. CGColorSpaceRelease(colorSpace);
  465. //Gradient center
  466. CGPoint gradCenter= CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
  467. //Gradient radius
  468. float gradRadius = MIN(self.bounds.size.width , self.bounds.size.height) ;
  469. //Gradient draw
  470. CGContextDrawRadialGradient (context, gradient, gradCenter,
  471. 0, gradCenter, gradRadius,
  472. kCGGradientDrawsAfterEndLocation);
  473. CGGradientRelease(gradient);
  474. }
  475. // Center HUD
  476. CGRect allRect = self.bounds;
  477. // Draw rounded HUD bacgroud rect
  478. CGRect boxRect = CGRectMake(roundf((allRect.size.width - size.width) / 2) + self.xOffset,
  479. roundf((allRect.size.height - size.height) / 2) + self.yOffset, size.width, size.height);
  480. float radius = 10.0f;
  481. CGContextBeginPath(context);
  482. CGContextSetGrayFillColor(context, 0.0f, self.opacity);
  483. CGContextMoveToPoint(context, CGRectGetMinX(boxRect) + radius, CGRectGetMinY(boxRect));
  484. CGContextAddArc(context, CGRectGetMaxX(boxRect) - radius, CGRectGetMinY(boxRect) + radius, radius, 3 * (float)M_PI / 2, 0, 0);
  485. CGContextAddArc(context, CGRectGetMaxX(boxRect) - radius, CGRectGetMaxY(boxRect) - radius, radius, 0, (float)M_PI / 2, 0);
  486. CGContextAddArc(context, CGRectGetMinX(boxRect) + radius, CGRectGetMaxY(boxRect) - radius, radius, (float)M_PI / 2, (float)M_PI, 0);
  487. CGContextAddArc(context, CGRectGetMinX(boxRect) + radius, CGRectGetMinY(boxRect) + radius, radius, (float)M_PI, 3 * (float)M_PI / 2, 0);
  488. CGContextClosePath(context);
  489. CGContextFillPath(context);
  490. }
  491. #pragma mark - KVO
  492. - (void)registerForKVO {
  493. for (NSString *keyPath in [self observableKeypaths]) {
  494. [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL];
  495. }
  496. }
  497. - (void)unregisterFromKVO {
  498. for (NSString *keyPath in [self observableKeypaths]) {
  499. [self removeObserver:self forKeyPath:keyPath];
  500. }
  501. }
  502. - (NSArray *)observableKeypaths {
  503. return [NSArray arrayWithObjects:@"mode", @"customView", @"labelText", @"labelFont",
  504. @"detailsLabelText", @"detailsLabelFont", @"progress", nil];
  505. }
  506. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
  507. if (![NSThread isMainThread]) {
  508. [self performSelectorOnMainThread:@selector(updateUIForKeypath:) withObject:keyPath waitUntilDone:NO];
  509. } else {
  510. [self updateUIForKeypath:keyPath];
  511. }
  512. }
  513. - (void)updateUIForKeypath:(NSString *)keyPath {
  514. if ([keyPath isEqualToString:@"mode"] || [keyPath isEqualToString:@"customView"]) {
  515. [self updateIndicators];
  516. } else if ([keyPath isEqualToString:@"labelText"]) {
  517. label.text = self.labelText;
  518. } else if ([keyPath isEqualToString:@"labelFont"]) {
  519. label.font = self.labelFont;
  520. } else if ([keyPath isEqualToString:@"detailsLabelText"]) {
  521. detailsLabel.text = self.detailsLabelText;
  522. } else if ([keyPath isEqualToString:@"detailsLabelFont"]) {
  523. detailsLabel.font = self.detailsLabelFont;
  524. } else if ([keyPath isEqualToString:@"progress"]) {
  525. if ([indicator respondsToSelector:@selector(setProgress:)]) {
  526. [(id)indicator setProgress:progress];
  527. }
  528. return;
  529. }
  530. [self setNeedsLayout];
  531. [self setNeedsDisplay];
  532. }
  533. #pragma mark - Notifications
  534. - (void)registerForNotifications {
  535. NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
  536. [nc addObserver:self selector:@selector(deviceOrientationDidChange:)
  537. name:UIDeviceOrientationDidChangeNotification object:nil];
  538. }
  539. - (void)unregisterFromNotifications {
  540. [[NSNotificationCenter defaultCenter] removeObserver:self];
  541. }
  542. - (void)deviceOrientationDidChange:(NSNotification *)notification {
  543. UIView *superview = self.superview;
  544. if (!superview) {
  545. return;
  546. } else if ([superview isKindOfClass:[UIWindow class]]) {
  547. [self setTransformForCurrentOrientation:YES];
  548. } else {
  549. self.bounds = self.superview.bounds;
  550. [self setNeedsDisplay];
  551. }
  552. }
  553. - (void)setTransformForCurrentOrientation:(BOOL)animated {
  554. // Stay in sync with the superview
  555. if (self.superview) {
  556. self.bounds = self.superview.bounds;
  557. [self setNeedsDisplay];
  558. }
  559. UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
  560. float radians = 0;
  561. if (UIInterfaceOrientationIsLandscape(orientation)) {
  562. if (orientation == UIInterfaceOrientationLandscapeLeft) { radians = -M_PI_2; }
  563. else { radians = M_PI_2; }
  564. // Window coordinates differ!
  565. self.bounds = CGRectMake(0, 0, self.bounds.size.height, self.bounds.size.width);
  566. } else {
  567. if (orientation == UIInterfaceOrientationPortraitUpsideDown) { radians = M_PI; }
  568. else { radians = 0; }
  569. }
  570. rotationTransform = CGAffineTransformMakeRotation(radians);
  571. if (animated) {
  572. [UIView beginAnimations:nil context:nil];
  573. }
  574. [self setTransform:rotationTransform];
  575. if (animated) {
  576. [UIView commitAnimations];
  577. }
  578. }
  579. @end
  580. @implementation MBRoundProgressView {
  581. float _progress;
  582. BOOL _annular;
  583. }
  584. #pragma mark - Accessors
  585. - (float)progress {
  586. return _progress;
  587. }
  588. - (void)setProgress:(float)progress {
  589. _progress = progress;
  590. [self setNeedsDisplay];
  591. }
  592. - (BOOL)isAnnular {
  593. return _annular;
  594. }
  595. - (void)setAnnular:(BOOL)annular {
  596. _annular = annular;
  597. [self setNeedsDisplay];
  598. }
  599. #pragma mark - Lifecycle
  600. - (id)init {
  601. return [self initWithFrame:CGRectMake(0.f, 0.f, 37.f, 37.f)];
  602. }
  603. - (id)initWithFrame:(CGRect)frame {
  604. self = [super initWithFrame:frame];
  605. if (self) {
  606. self.backgroundColor = [UIColor clearColor];
  607. self.opaque = NO;
  608. _progress = 0.f;
  609. _annular = NO;
  610. }
  611. return self;
  612. }
  613. #pragma mark - Drawing
  614. - (void)drawRect:(CGRect)rect {
  615. CGRect allRect = self.bounds;
  616. CGRect circleRect = CGRectInset(allRect, 2.0f, 2.0f);
  617. CGContextRef context = UIGraphicsGetCurrentContext();
  618. if (_annular) {
  619. // Draw background
  620. CGFloat lineWidth = 5.f;
  621. UIBezierPath *processBackgroundPath = [UIBezierPath bezierPath];
  622. processBackgroundPath.lineWidth = lineWidth;
  623. processBackgroundPath.lineCapStyle = kCGLineCapRound;
  624. CGPoint center = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
  625. CGFloat radius = (self.bounds.size.width - lineWidth)/2;
  626. CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees
  627. CGFloat endAngle = (2 * (float)M_PI) + startAngle;
  628. [processBackgroundPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
  629. [[UIColor colorWithRed:1 green:1 blue:1 alpha:0.1] set];
  630. [processBackgroundPath stroke];
  631. // Draw progress
  632. UIBezierPath *processPath = [UIBezierPath bezierPath];
  633. processPath.lineCapStyle = kCGLineCapRound;
  634. processPath.lineWidth = lineWidth;
  635. endAngle = (self.progress * 2 * (float)M_PI) + startAngle;
  636. [processPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
  637. [[UIColor whiteColor] set];
  638. [processPath stroke];
  639. } else {
  640. // Draw background
  641. CGContextSetRGBStrokeColor(context, 1.0f, 1.0f, 1.0f, 1.0f); // white
  642. CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 0.1f); // translucent white
  643. CGContextSetLineWidth(context, 2.0f);
  644. CGContextFillEllipseInRect(context, circleRect);
  645. CGContextStrokeEllipseInRect(context, circleRect);
  646. // Draw progress
  647. CGPoint center = CGPointMake(allRect.size.width / 2, allRect.size.height / 2);
  648. CGFloat radius = (allRect.size.width - 4) / 2;
  649. CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees
  650. CGFloat endAngle = (self.progress * 2 * (float)M_PI) + startAngle;
  651. CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 1.0f); // white
  652. CGContextMoveToPoint(context, center.x, center.y);
  653. CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, 0);
  654. CGContextClosePath(context);
  655. CGContextFillPath(context);
  656. }
  657. }
  658. @end