4 // Created by Matej Bukovinski on 2.4.09.
7 #import "MBProgressHUD.h"
11 #ifndef kCFCoreFoundationVersionNumber_iOS_7_0
12 #define kCFCoreFoundationVersionNumber_iOS_7_0 847.20
15 #ifndef kCFCoreFoundationVersionNumber_iOS_8_0
16 #define kCFCoreFoundationVersionNumber_iOS_8_0 1129.15
19 #define MBMainThreadAssert() NSAssert([NSThread isMainThread], @"MBProgressHUD needs to be accessed on the main thread.");
21 CGFloat const MBProgressMaxOffset = 1000000.f;
23 static const CGFloat MBDefaultPadding = 4.f;
24 static const CGFloat MBDefaultLabelFontSize = 16.f;
25 static const CGFloat MBDefaultDetailsLabelFontSize = 12.f;
28 @interface MBProgressHUD () {
30 UIColor *_activityIndicatorColor;
34 @property (nonatomic, assign) BOOL useAnimation;
35 @property (nonatomic, assign, getter=hasFinished) BOOL finished;
36 @property (nonatomic, strong) UIView *indicator;
37 @property (nonatomic, strong) NSDate *showStarted;
38 @property (nonatomic, strong) NSArray *paddingConstraints;
39 @property (nonatomic, strong) NSArray *bezelConstraints;
40 @property (nonatomic, strong) UIView *topSpacer;
41 @property (nonatomic, strong) UIView *bottomSpacer;
42 @property (nonatomic, weak) NSTimer *graceTimer;
43 @property (nonatomic, weak) NSTimer *minShowTimer;
44 @property (nonatomic, weak) NSTimer *hideDelayTimer;
45 @property (nonatomic, weak) CADisplayLink *progressObjectDisplayLink;
48 @property (assign) BOOL taskInProgress;
53 @interface MBProgressHUDRoundedButton : UIButton
57 @implementation MBProgressHUD
59 #pragma mark - Class methods
61 + (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated {
62 MBProgressHUD *hud = [[self alloc] initWithView:view];
63 hud.removeFromSuperViewOnHide = YES;
64 [view addSubview:hud];
65 [hud showAnimated:animated];
69 + (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated {
70 MBProgressHUD *hud = [self HUDForView:view];
72 hud.removeFromSuperViewOnHide = YES;
73 [hud hideAnimated:animated];
79 + (MBProgressHUD *)HUDForView:(UIView *)view {
80 NSEnumerator *subviewsEnum = [view.subviews reverseObjectEnumerator];
81 for (UIView *subview in subviewsEnum) {
82 if ([subview isKindOfClass:self]) {
83 MBProgressHUD *hud = (MBProgressHUD *)subview;
84 if (hud.hasFinished == NO) {
92 #pragma mark - Lifecycle
95 // Set default values for properties
96 _animationType = MBProgressHUDAnimationFade;
97 _mode = MBProgressHUDModeIndeterminate;
100 _defaultMotionEffectsEnabled = YES;
102 // Default color, depending on the current iOS version
103 BOOL isLegacy = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0;
104 _contentColor = isLegacy ? [UIColor whiteColor] : [UIColor colorWithWhite:0.f alpha:0.7f];
105 // Transparent background
107 self.backgroundColor = [UIColor clearColor];
108 // Make it invisible for now
110 self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
111 self.layer.allowsGroupOpacity = NO;
114 [self updateIndicators];
115 [self registerForNotifications];
118 - (instancetype)initWithFrame:(CGRect)frame {
119 if ((self = [super initWithFrame:frame])) {
125 - (instancetype)initWithCoder:(NSCoder *)aDecoder {
126 if ((self = [super initWithCoder:aDecoder])) {
132 - (id)initWithView:(UIView *)view {
133 NSAssert(view, @"View must not be nil.");
134 return [self initWithFrame:view.bounds];
138 [self unregisterFromNotifications];
141 #pragma mark - Show & hide
143 - (void)showAnimated:(BOOL)animated {
144 MBMainThreadAssert();
145 [self.minShowTimer invalidate];
146 self.useAnimation = animated;
148 // If the grace time is set, postpone the HUD display
149 if (self.graceTime > 0.0) {
150 NSTimer *timer = [NSTimer timerWithTimeInterval:self.graceTime target:self selector:@selector(handleGraceTimer:) userInfo:nil repeats:NO];
151 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
152 self.graceTimer = timer;
154 // ... otherwise show the HUD immediately
156 [self showUsingAnimation:self.useAnimation];
160 - (void)hideAnimated:(BOOL)animated {
161 MBMainThreadAssert();
162 [self.graceTimer invalidate];
163 self.useAnimation = animated;
165 // If the minShow time is set, calculate how long the HUD was shown,
166 // and postpone the hiding operation if necessary
167 if (self.minShowTime > 0.0 && self.showStarted) {
168 NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:self.showStarted];
169 if (interv < self.minShowTime) {
170 NSTimer *timer = [NSTimer timerWithTimeInterval:(self.minShowTime - interv) target:self selector:@selector(handleMinShowTimer:) userInfo:nil repeats:NO];
171 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
172 self.minShowTimer = timer;
176 // ... otherwise hide the HUD immediately
177 [self hideUsingAnimation:self.useAnimation];
180 - (void)hideAnimated:(BOOL)animated afterDelay:(NSTimeInterval)delay {
181 // Cancel any scheduled hideDelayed: calls
182 [self.hideDelayTimer invalidate];
184 NSTimer *timer = [NSTimer timerWithTimeInterval:delay target:self selector:@selector(handleHideTimer:) userInfo:@(animated) repeats:NO];
185 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
186 self.hideDelayTimer = timer;
189 #pragma mark - Timer callbacks
191 - (void)handleGraceTimer:(NSTimer *)theTimer {
192 // Show the HUD only if the task is still running
193 if (!self.hasFinished) {
194 [self showUsingAnimation:self.useAnimation];
198 - (void)handleMinShowTimer:(NSTimer *)theTimer {
199 [self hideUsingAnimation:self.useAnimation];
202 - (void)handleHideTimer:(NSTimer *)timer {
203 [self hideAnimated:[timer.userInfo boolValue]];
206 #pragma mark - View Hierrarchy
208 - (void)didMoveToSuperview {
209 [self updateForCurrentOrientationAnimated:NO];
212 #pragma mark - Internal show & hide operations
214 - (void)showUsingAnimation:(BOOL)animated {
215 // Cancel any previous animations
216 [self.bezelView.layer removeAllAnimations];
217 [self.backgroundView.layer removeAllAnimations];
219 // Cancel any scheduled hideDelayed: calls
220 [self.hideDelayTimer invalidate];
222 self.showStarted = [NSDate date];
225 // Needed in case we hide and re-show with the same NSProgress object attached.
226 [self setNSProgressDisplayLinkEnabled:YES];
229 [self animateIn:YES withType:self.animationType completion:NULL];
231 #pragma clang diagnostic push
232 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
233 self.bezelView.alpha = self.opacity;
234 #pragma clang diagnostic pop
235 self.backgroundView.alpha = 1.f;
239 - (void)hideUsingAnimation:(BOOL)animated {
240 if (animated && self.showStarted) {
241 self.showStarted = nil;
242 [self animateIn:NO withType:self.animationType completion:^(BOOL finished) {
246 self.showStarted = nil;
247 self.bezelView.alpha = 0.f;
248 self.backgroundView.alpha = 1.f;
253 - (void)animateIn:(BOOL)animatingIn withType:(MBProgressHUDAnimation)type completion:(void(^)(BOOL finished))completion {
254 // Automatically determine the correct zoom animation type
255 if (type == MBProgressHUDAnimationZoom) {
256 type = animatingIn ? MBProgressHUDAnimationZoomIn : MBProgressHUDAnimationZoomOut;
259 CGAffineTransform small = CGAffineTransformMakeScale(0.5f, 0.5f);
260 CGAffineTransform large = CGAffineTransformMakeScale(1.5f, 1.5f);
262 // Set starting state
263 UIView *bezelView = self.bezelView;
264 if (animatingIn && bezelView.alpha == 0.f && type == MBProgressHUDAnimationZoomIn) {
265 bezelView.transform = small;
266 } else if (animatingIn && bezelView.alpha == 0.f && type == MBProgressHUDAnimationZoomOut) {
267 bezelView.transform = large;
270 // Perform animations
271 dispatch_block_t animations = ^{
273 bezelView.transform = CGAffineTransformIdentity;
274 } else if (!animatingIn && type == MBProgressHUDAnimationZoomIn) {
275 bezelView.transform = large;
276 } else if (!animatingIn && type == MBProgressHUDAnimationZoomOut) {
277 bezelView.transform = small;
279 #pragma clang diagnostic push
280 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
281 bezelView.alpha = animatingIn ? self.opacity : 0.f;
282 #pragma clang diagnostic pop
283 self.backgroundView.alpha = animatingIn ? 1.f : 0.f;
286 // Spring animations are nicer, but only available on iOS 7+
287 #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 || TARGET_OS_TV
288 if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_7_0) {
289 [UIView animateWithDuration:0.3 delay:0. usingSpringWithDamping:1.f initialSpringVelocity:0.f options:UIViewAnimationOptionBeginFromCurrentState animations:animations completion:completion];
293 [UIView animateWithDuration:0.3 delay:0. options:UIViewAnimationOptionBeginFromCurrentState animations:animations completion:completion];
297 // Cancel any scheduled hideDelayed: calls
298 [self.hideDelayTimer invalidate];
299 [self setNSProgressDisplayLinkEnabled:NO];
301 if (self.hasFinished) {
303 if (self.removeFromSuperViewOnHide) {
304 [self removeFromSuperview];
307 MBProgressHUDCompletionBlock completionBlock = self.completionBlock;
308 if (completionBlock) {
311 id<MBProgressHUDDelegate> delegate = self.delegate;
312 if ([delegate respondsToSelector:@selector(hudWasHidden:)]) {
313 [delegate performSelector:@selector(hudWasHidden:) withObject:self];
320 UIColor *defaultColor = self.contentColor;
322 MBBackgroundView *backgroundView = [[MBBackgroundView alloc] initWithFrame:self.bounds];
323 backgroundView.style = MBProgressHUDBackgroundStyleSolidColor;
324 backgroundView.backgroundColor = [UIColor clearColor];
325 backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
326 backgroundView.alpha = 0.f;
327 [self addSubview:backgroundView];
328 _backgroundView = backgroundView;
330 MBBackgroundView *bezelView = [MBBackgroundView new];
331 bezelView.translatesAutoresizingMaskIntoConstraints = NO;
332 bezelView.layer.cornerRadius = 5.f;
333 bezelView.alpha = 0.f;
334 [self addSubview:bezelView];
335 _bezelView = bezelView;
336 [self updateBezelMotionEffects];
338 UILabel *label = [UILabel new];
339 label.adjustsFontSizeToFitWidth = NO;
340 label.textAlignment = NSTextAlignmentCenter;
341 label.textColor = defaultColor;
342 label.font = [UIFont boldSystemFontOfSize:MBDefaultLabelFontSize];
344 label.backgroundColor = [UIColor clearColor];
347 UILabel *detailsLabel = [UILabel new];
348 detailsLabel.adjustsFontSizeToFitWidth = NO;
349 detailsLabel.textAlignment = NSTextAlignmentCenter;
350 detailsLabel.textColor = defaultColor;
351 detailsLabel.numberOfLines = 0;
352 detailsLabel.font = [UIFont boldSystemFontOfSize:MBDefaultDetailsLabelFontSize];
353 detailsLabel.opaque = NO;
354 detailsLabel.backgroundColor = [UIColor clearColor];
355 _detailsLabel = detailsLabel;
357 UIButton *button = [MBProgressHUDRoundedButton buttonWithType:UIButtonTypeCustom];
358 button.titleLabel.textAlignment = NSTextAlignmentCenter;
359 button.titleLabel.font = [UIFont boldSystemFontOfSize:MBDefaultDetailsLabelFontSize];
360 [button setTitleColor:defaultColor forState:UIControlStateNormal];
363 for (UIView *view in @[label, detailsLabel, button]) {
364 view.translatesAutoresizingMaskIntoConstraints = NO;
365 [view setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisHorizontal];
366 [view setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisVertical];
367 [bezelView addSubview:view];
370 UIView *topSpacer = [UIView new];
371 topSpacer.translatesAutoresizingMaskIntoConstraints = NO;
372 topSpacer.hidden = YES;
373 [bezelView addSubview:topSpacer];
374 _topSpacer = topSpacer;
376 UIView *bottomSpacer = [UIView new];
377 bottomSpacer.translatesAutoresizingMaskIntoConstraints = NO;
378 bottomSpacer.hidden = YES;
379 [bezelView addSubview:bottomSpacer];
380 _bottomSpacer = bottomSpacer;
383 - (void)updateIndicators {
384 UIView *indicator = self.indicator;
385 BOOL isActivityIndicator = [indicator isKindOfClass:[UIActivityIndicatorView class]];
386 BOOL isRoundIndicator = [indicator isKindOfClass:[MBRoundProgressView class]];
388 MBProgressHUDMode mode = self.mode;
389 if (mode == MBProgressHUDModeIndeterminate) {
390 if (!isActivityIndicator) {
391 // Update to indeterminate indicator
392 [indicator removeFromSuperview];
393 indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
394 [(UIActivityIndicatorView *)indicator startAnimating];
395 [self.bezelView addSubview:indicator];
398 else if (mode == MBProgressHUDModeDeterminateHorizontalBar) {
399 // Update to bar determinate indicator
400 [indicator removeFromSuperview];
401 indicator = [[MBBarProgressView alloc] init];
402 [self.bezelView addSubview:indicator];
404 else if (mode == MBProgressHUDModeDeterminate || mode == MBProgressHUDModeAnnularDeterminate) {
405 if (!isRoundIndicator) {
406 // Update to determinante indicator
407 [indicator removeFromSuperview];
408 indicator = [[MBRoundProgressView alloc] init];
409 [self.bezelView addSubview:indicator];
411 if (mode == MBProgressHUDModeAnnularDeterminate) {
412 [(MBRoundProgressView *)indicator setAnnular:YES];
415 else if (mode == MBProgressHUDModeCustomView && self.customView != indicator) {
416 // Update custom view indicator
417 [indicator removeFromSuperview];
418 indicator = self.customView;
419 [self.bezelView addSubview:indicator];
421 else if (mode == MBProgressHUDModeText) {
422 [indicator removeFromSuperview];
425 indicator.translatesAutoresizingMaskIntoConstraints = NO;
426 self.indicator = indicator;
428 if ([indicator respondsToSelector:@selector(setProgress:)]) {
429 [(id)indicator setValue:@(self.progress) forKey:@"progress"];
432 [indicator setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisHorizontal];
433 [indicator setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisVertical];
435 [self updateViewsForColor:self.contentColor];
436 [self setNeedsUpdateConstraints];
439 - (void)updateViewsForColor:(UIColor *)color {
442 self.label.textColor = color;
443 self.detailsLabel.textColor = color;
444 [self.button setTitleColor:color forState:UIControlStateNormal];
446 #pragma clang diagnostic push
447 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
448 if (self.activityIndicatorColor) {
449 color = self.activityIndicatorColor;
451 #pragma clang diagnostic pop
453 // UIAppearance settings are prioritized. If they are preset the set color is ignored.
455 UIView *indicator = self.indicator;
456 if ([indicator isKindOfClass:[UIActivityIndicatorView class]]) {
457 UIActivityIndicatorView *appearance = nil;
458 #if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000
459 appearance = [UIActivityIndicatorView appearanceWhenContainedIn:[MBProgressHUD class], nil];
462 appearance = [UIActivityIndicatorView appearanceWhenContainedInInstancesOfClasses:@[[MBProgressHUD class]]];
465 if (appearance.color == nil) {
466 ((UIActivityIndicatorView *)indicator).color = color;
468 } else if ([indicator isKindOfClass:[MBRoundProgressView class]]) {
469 MBRoundProgressView *appearance = nil;
470 #if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000
471 appearance = [MBRoundProgressView appearanceWhenContainedIn:[MBProgressHUD class], nil];
473 appearance = [MBRoundProgressView appearanceWhenContainedInInstancesOfClasses:@[[MBProgressHUD class]]];
475 if (appearance.progressTintColor == nil) {
476 ((MBRoundProgressView *)indicator).progressTintColor = color;
478 if (appearance.backgroundTintColor == nil) {
479 ((MBRoundProgressView *)indicator).backgroundTintColor = [color colorWithAlphaComponent:0.1];
481 } else if ([indicator isKindOfClass:[MBBarProgressView class]]) {
482 MBBarProgressView *appearance = nil;
483 #if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000
484 appearance = [MBBarProgressView appearanceWhenContainedIn:[MBProgressHUD class], nil];
486 appearance = [MBBarProgressView appearanceWhenContainedInInstancesOfClasses:@[[MBProgressHUD class]]];
488 if (appearance.progressColor == nil) {
489 ((MBBarProgressView *)indicator).progressColor = color;
491 if (appearance.lineColor == nil) {
492 ((MBBarProgressView *)indicator).lineColor = color;
495 #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 || TARGET_OS_TV
496 if ([indicator respondsToSelector:@selector(setTintColor:)]) {
497 [indicator setTintColor:color];
503 - (void)updateBezelMotionEffects {
504 #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 || TARGET_OS_TV
505 MBBackgroundView *bezelView = self.bezelView;
506 if (![bezelView respondsToSelector:@selector(addMotionEffect:)]) return;
508 if (self.defaultMotionEffectsEnabled) {
509 CGFloat effectOffset = 10.f;
510 UIInterpolatingMotionEffect *effectX = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
511 effectX.maximumRelativeValue = @(effectOffset);
512 effectX.minimumRelativeValue = @(-effectOffset);
514 UIInterpolatingMotionEffect *effectY = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
515 effectY.maximumRelativeValue = @(effectOffset);
516 effectY.minimumRelativeValue = @(-effectOffset);
518 UIMotionEffectGroup *group = [[UIMotionEffectGroup alloc] init];
519 group.motionEffects = @[effectX, effectY];
521 [bezelView addMotionEffect:group];
523 NSArray *effects = [bezelView motionEffects];
524 for (UIMotionEffect *effect in effects) {
525 [bezelView removeMotionEffect:effect];
531 #pragma mark - Layout
533 - (void)updateConstraints {
534 UIView *bezel = self.bezelView;
535 UIView *topSpacer = self.topSpacer;
536 UIView *bottomSpacer = self.bottomSpacer;
537 CGFloat margin = self.margin;
538 NSMutableArray *bezelConstraints = [NSMutableArray array];
539 NSDictionary *metrics = @{@"margin": @(margin)};
541 NSMutableArray *subviews = [NSMutableArray arrayWithObjects:self.topSpacer, self.label, self.detailsLabel, self.button, self.bottomSpacer, nil];
542 if (self.indicator) [subviews insertObject:self.indicator atIndex:1];
544 // Remove existing constraints
545 [self removeConstraints:self.constraints];
546 [topSpacer removeConstraints:topSpacer.constraints];
547 [bottomSpacer removeConstraints:bottomSpacer.constraints];
548 if (self.bezelConstraints) {
549 [bezel removeConstraints:self.bezelConstraints];
550 self.bezelConstraints = nil;
553 // Center bezel in container (self), applying the offset if set
554 CGPoint offset = self.offset;
555 NSMutableArray *centeringConstraints = [NSMutableArray array];
556 [centeringConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.f constant:offset.x]];
557 [centeringConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.f constant:offset.y]];
558 [self applyPriority:998.f toConstraints:centeringConstraints];
559 [self addConstraints:centeringConstraints];
561 // Ensure minimum side margin is kept
562 NSMutableArray *sideConstraints = [NSMutableArray array];
563 [sideConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=margin)-[bezel]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(bezel)]];
564 [sideConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=margin)-[bezel]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(bezel)]];
565 [self applyPriority:999.f toConstraints:sideConstraints];
566 [self addConstraints:sideConstraints];
568 // Minimum bezel size, if set
569 CGSize minimumSize = self.minSize;
570 if (!CGSizeEqualToSize(minimumSize, CGSizeZero)) {
571 NSMutableArray *minSizeConstraints = [NSMutableArray array];
572 [minSizeConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:minimumSize.width]];
573 [minSizeConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:minimumSize.height]];
574 [self applyPriority:997.f toConstraints:minSizeConstraints];
575 [bezelConstraints addObjectsFromArray:minSizeConstraints];
578 // Square aspect ratio, if set
580 NSLayoutConstraint *square = [NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeWidth multiplier:1.f constant:0];
581 square.priority = 997.f;
582 [bezelConstraints addObject:square];
585 // Top and bottom spacing
586 [topSpacer addConstraint:[NSLayoutConstraint constraintWithItem:topSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:margin]];
587 [bottomSpacer addConstraint:[NSLayoutConstraint constraintWithItem:bottomSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:margin]];
588 // Top and bottom spaces should be equal
589 [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:topSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:bottomSpacer attribute:NSLayoutAttributeHeight multiplier:1.f constant:0.f]];
591 // Layout subviews in bezel
592 NSMutableArray *paddingConstraints = [NSMutableArray new];
593 [subviews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) {
595 [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeCenterX multiplier:1.f constant:0.f]];
596 // Ensure the minimum edge margin is kept
597 [bezelConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=margin)-[view]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(view)]];
600 // First, ensure spacing to bezel edge
601 [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeTop multiplier:1.f constant:0.f]];
602 } else if (idx == subviews.count - 1) {
603 // Last, ensure spacing to bezel edge
604 [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeBottom multiplier:1.f constant:0.f]];
608 NSLayoutConstraint *padding = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:subviews[idx - 1] attribute:NSLayoutAttributeBottom multiplier:1.f constant:0.f];
609 [bezelConstraints addObject:padding];
610 [paddingConstraints addObject:padding];
614 [bezel addConstraints:bezelConstraints];
615 self.bezelConstraints = bezelConstraints;
617 self.paddingConstraints = [paddingConstraints copy];
618 [self updatePaddingConstraints];
620 [super updateConstraints];
623 - (void)layoutSubviews {
624 // There is no need to update constraints if they are going to
625 // be recreated in [super layoutSubviews] due to needsUpdateConstraints being set.
626 // This also avoids an issue on iOS 8, where updatePaddingConstraints
627 // would trigger a zombie object access.
628 if (!self.needsUpdateConstraints) {
629 [self updatePaddingConstraints];
631 [super layoutSubviews];
634 - (void)updatePaddingConstraints {
635 // Set padding dynamically, depending on whether the view is visible or not
636 __block BOOL hasVisibleAncestors = NO;
637 [self.paddingConstraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *padding, NSUInteger idx, BOOL *stop) {
638 UIView *firstView = (UIView *)padding.firstItem;
639 UIView *secondView = (UIView *)padding.secondItem;
640 BOOL firstVisible = !firstView.hidden && !CGSizeEqualToSize(firstView.intrinsicContentSize, CGSizeZero);
641 BOOL secondVisible = !secondView.hidden && !CGSizeEqualToSize(secondView.intrinsicContentSize, CGSizeZero);
642 // Set if both views are visible or if there's a visible view on top that doesn't have padding
643 // added relative to the current view yet
644 padding.constant = (firstVisible && (secondVisible || hasVisibleAncestors)) ? MBDefaultPadding : 0.f;
645 hasVisibleAncestors |= secondVisible;
649 - (void)applyPriority:(UILayoutPriority)priority toConstraints:(NSArray *)constraints {
650 for (NSLayoutConstraint *constraint in constraints) {
651 constraint.priority = priority;
655 #pragma mark - Properties
657 - (void)setMode:(MBProgressHUDMode)mode {
660 [self updateIndicators];
664 - (void)setCustomView:(UIView *)customView {
665 if (customView != _customView) {
666 _customView = customView;
667 if (self.mode == MBProgressHUDModeCustomView) {
668 [self updateIndicators];
673 - (void)setOffset:(CGPoint)offset {
674 if (!CGPointEqualToPoint(offset, _offset)) {
676 [self setNeedsUpdateConstraints];
680 - (void)setMargin:(CGFloat)margin {
681 if (margin != _margin) {
683 [self setNeedsUpdateConstraints];
687 - (void)setMinSize:(CGSize)minSize {
688 if (!CGSizeEqualToSize(minSize, _minSize)) {
690 [self setNeedsUpdateConstraints];
694 - (void)setSquare:(BOOL)square {
695 if (square != _square) {
697 [self setNeedsUpdateConstraints];
701 - (void)setProgressObjectDisplayLink:(CADisplayLink *)progressObjectDisplayLink {
702 if (progressObjectDisplayLink != _progressObjectDisplayLink) {
703 [_progressObjectDisplayLink invalidate];
705 _progressObjectDisplayLink = progressObjectDisplayLink;
707 [_progressObjectDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
711 - (void)setProgressObject:(NSProgress *)progressObject {
712 if (progressObject != _progressObject) {
713 _progressObject = progressObject;
714 [self setNSProgressDisplayLinkEnabled:YES];
718 - (void)setProgress:(float)progress {
719 if (progress != _progress) {
720 _progress = progress;
721 UIView *indicator = self.indicator;
722 if ([indicator respondsToSelector:@selector(setProgress:)]) {
723 [(id)indicator setValue:@(self.progress) forKey:@"progress"];
728 - (void)setContentColor:(UIColor *)contentColor {
729 if (contentColor != _contentColor && ![contentColor isEqual:_contentColor]) {
730 _contentColor = contentColor;
731 [self updateViewsForColor:contentColor];
735 - (void)setDefaultMotionEffectsEnabled:(BOOL)defaultMotionEffectsEnabled {
736 if (defaultMotionEffectsEnabled != _defaultMotionEffectsEnabled) {
737 _defaultMotionEffectsEnabled = defaultMotionEffectsEnabled;
738 [self updateBezelMotionEffects];
742 #pragma mark - NSProgress
744 - (void)setNSProgressDisplayLinkEnabled:(BOOL)enabled {
745 // We're using CADisplayLink, because NSProgress can change very quickly and observing it may starve the main thread,
746 // so we're refreshing the progress only every frame draw
747 if (enabled && self.progressObject) {
748 // Only create if not already active.
749 if (!self.progressObjectDisplayLink) {
750 self.progressObjectDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateProgressFromProgressObject)];
753 self.progressObjectDisplayLink = nil;
757 - (void)updateProgressFromProgressObject {
758 self.progress = self.progressObject.fractionCompleted;
761 #pragma mark - Notifications
763 - (void)registerForNotifications {
765 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
767 [nc addObserver:self selector:@selector(statusBarOrientationDidChange:)
768 name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
772 - (void)unregisterFromNotifications {
774 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
775 [nc removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
780 - (void)statusBarOrientationDidChange:(NSNotification *)notification {
781 UIView *superview = self.superview;
785 [self updateForCurrentOrientationAnimated:YES];
790 - (void)updateForCurrentOrientationAnimated:(BOOL)animated {
791 // Stay in sync with the superview in any case
792 if (self.superview) {
793 self.frame = self.superview.bounds;
796 // Not needed on iOS 8+, compile out when the deployment target allows,
797 // to avoid sharedApplication problems on extension targets
798 #if __IPHONE_OS_VERSION_MIN_REQUIRED < 80000
799 // Only needed pre iOS 8 when added to a window
800 BOOL iOS8OrLater = kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0;
801 if (iOS8OrLater || ![self.superview isKindOfClass:[UIWindow class]]) return;
803 // Make extension friendly. Will not get called on extensions (iOS 8+) due to the above check.
804 // This just ensures we don't get a warning about extension-unsafe API.
805 Class UIApplicationClass = NSClassFromString(@"UIApplication");
806 if (!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) return;
808 UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)];
809 UIInterfaceOrientation orientation = application.statusBarOrientation;
812 if (UIInterfaceOrientationIsLandscape(orientation)) {
813 radians = orientation == UIInterfaceOrientationLandscapeLeft ? -(CGFloat)M_PI_2 : (CGFloat)M_PI_2;
814 // Window coordinates differ!
815 self.bounds = CGRectMake(0, 0, self.bounds.size.height, self.bounds.size.width);
817 radians = orientation == UIInterfaceOrientationPortraitUpsideDown ? (CGFloat)M_PI : 0.f;
821 [UIView animateWithDuration:0.3 animations:^{
822 self.transform = CGAffineTransformMakeRotation(radians);
825 self.transform = CGAffineTransformMakeRotation(radians);
833 @implementation MBRoundProgressView
835 #pragma mark - Lifecycle
838 return [self initWithFrame:CGRectMake(0.f, 0.f, 37.f, 37.f)];
841 - (id)initWithFrame:(CGRect)frame {
842 self = [super initWithFrame:frame];
844 self.backgroundColor = [UIColor clearColor];
848 _progressTintColor = [[UIColor alloc] initWithWhite:1.f alpha:1.f];
849 _backgroundTintColor = [[UIColor alloc] initWithWhite:1.f alpha:.1f];
854 #pragma mark - Layout
856 - (CGSize)intrinsicContentSize {
857 return CGSizeMake(37.f, 37.f);
860 #pragma mark - Properties
862 - (void)setProgress:(float)progress {
863 if (progress != _progress) {
864 _progress = progress;
865 [self setNeedsDisplay];
869 - (void)setProgressTintColor:(UIColor *)progressTintColor {
870 NSAssert(progressTintColor, @"The color should not be nil.");
871 if (progressTintColor != _progressTintColor && ![progressTintColor isEqual:_progressTintColor]) {
872 _progressTintColor = progressTintColor;
873 [self setNeedsDisplay];
877 - (void)setBackgroundTintColor:(UIColor *)backgroundTintColor {
878 NSAssert(backgroundTintColor, @"The color should not be nil.");
879 if (backgroundTintColor != _backgroundTintColor && ![backgroundTintColor isEqual:_backgroundTintColor]) {
880 _backgroundTintColor = backgroundTintColor;
881 [self setNeedsDisplay];
885 #pragma mark - Drawing
887 - (void)drawRect:(CGRect)rect {
888 CGContextRef context = UIGraphicsGetCurrentContext();
889 BOOL isPreiOS7 = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0;
893 CGFloat lineWidth = isPreiOS7 ? 5.f : 2.f;
894 UIBezierPath *processBackgroundPath = [UIBezierPath bezierPath];
895 processBackgroundPath.lineWidth = lineWidth;
896 processBackgroundPath.lineCapStyle = kCGLineCapButt;
897 CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
898 CGFloat radius = (self.bounds.size.width - lineWidth)/2;
899 CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees
900 CGFloat endAngle = (2 * (float)M_PI) + startAngle;
901 [processBackgroundPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
902 [_backgroundTintColor set];
903 [processBackgroundPath stroke];
905 UIBezierPath *processPath = [UIBezierPath bezierPath];
906 processPath.lineCapStyle = isPreiOS7 ? kCGLineCapRound : kCGLineCapSquare;
907 processPath.lineWidth = lineWidth;
908 endAngle = (self.progress * 2 * (float)M_PI) + startAngle;
909 [processPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
910 [_progressTintColor set];
911 [processPath stroke];
914 CGFloat lineWidth = 2.f;
915 CGRect allRect = self.bounds;
916 CGRect circleRect = CGRectInset(allRect, lineWidth/2.f, lineWidth/2.f);
917 CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
918 [_progressTintColor setStroke];
919 [_backgroundTintColor setFill];
920 CGContextSetLineWidth(context, lineWidth);
922 CGContextFillEllipseInRect(context, circleRect);
924 CGContextStrokeEllipseInRect(context, circleRect);
926 CGFloat startAngle = - ((float)M_PI / 2.f);
929 CGFloat radius = (CGRectGetWidth(self.bounds) / 2.f) - lineWidth;
930 CGFloat endAngle = (self.progress * 2.f * (float)M_PI) + startAngle;
931 [_progressTintColor setFill];
932 CGContextMoveToPoint(context, center.x, center.y);
933 CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, 0);
934 CGContextClosePath(context);
935 CGContextFillPath(context);
937 UIBezierPath *processPath = [UIBezierPath bezierPath];
938 processPath.lineCapStyle = kCGLineCapButt;
939 processPath.lineWidth = lineWidth * 2.f;
940 CGFloat radius = (CGRectGetWidth(self.bounds) / 2.f) - (processPath.lineWidth / 2.f);
941 CGFloat endAngle = (self.progress * 2.f * (float)M_PI) + startAngle;
942 [processPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
943 // Ensure that we don't get color overlapping when _progressTintColor alpha < 1.f.
944 CGContextSetBlendMode(context, kCGBlendModeCopy);
945 [_progressTintColor set];
946 [processPath stroke];
954 @implementation MBBarProgressView
956 #pragma mark - Lifecycle
959 return [self initWithFrame:CGRectMake(.0f, .0f, 120.0f, 20.0f)];
962 - (id)initWithFrame:(CGRect)frame {
963 self = [super initWithFrame:frame];
966 _lineColor = [UIColor whiteColor];
967 _progressColor = [UIColor whiteColor];
968 _progressRemainingColor = [UIColor clearColor];
969 self.backgroundColor = [UIColor clearColor];
975 #pragma mark - Layout
977 - (CGSize)intrinsicContentSize {
978 BOOL isPreiOS7 = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0;
979 return CGSizeMake(120.f, isPreiOS7 ? 20.f : 10.f);
982 #pragma mark - Properties
984 - (void)setProgress:(float)progress {
985 if (progress != _progress) {
986 _progress = progress;
987 [self setNeedsDisplay];
991 - (void)setProgressColor:(UIColor *)progressColor {
992 NSAssert(progressColor, @"The color should not be nil.");
993 if (progressColor != _progressColor && ![progressColor isEqual:_progressColor]) {
994 _progressColor = progressColor;
995 [self setNeedsDisplay];
999 - (void)setProgressRemainingColor:(UIColor *)progressRemainingColor {
1000 NSAssert(progressRemainingColor, @"The color should not be nil.");
1001 if (progressRemainingColor != _progressRemainingColor && ![progressRemainingColor isEqual:_progressRemainingColor]) {
1002 _progressRemainingColor = progressRemainingColor;
1003 [self setNeedsDisplay];
1007 #pragma mark - Drawing
1009 - (void)drawRect:(CGRect)rect {
1010 CGContextRef context = UIGraphicsGetCurrentContext();
1012 CGContextSetLineWidth(context, 2);
1013 CGContextSetStrokeColorWithColor(context,[_lineColor CGColor]);
1014 CGContextSetFillColorWithColor(context, [_progressRemainingColor CGColor]);
1016 // Draw background and Border
1017 CGFloat radius = (rect.size.height / 2) - 2;
1018 CGContextMoveToPoint(context, 2, rect.size.height/2);
1019 CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius);
1020 CGContextAddArcToPoint(context, rect.size.width - 2, 2, rect.size.width - 2, rect.size.height / 2, radius);
1021 CGContextAddArcToPoint(context, rect.size.width - 2, rect.size.height - 2, rect.size.width - radius - 2, rect.size.height - 2, radius);
1022 CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius);
1023 CGContextDrawPath(context, kCGPathFillStroke);
1025 CGContextSetFillColorWithColor(context, [_progressColor CGColor]);
1026 radius = radius - 2;
1027 CGFloat amount = self.progress * rect.size.width;
1029 // Progress in the middle area
1030 if (amount >= radius + 4 && amount <= (rect.size.width - radius - 4)) {
1031 CGContextMoveToPoint(context, 4, rect.size.height/2);
1032 CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
1033 CGContextAddLineToPoint(context, amount, 4);
1034 CGContextAddLineToPoint(context, amount, radius + 4);
1036 CGContextMoveToPoint(context, 4, rect.size.height/2);
1037 CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
1038 CGContextAddLineToPoint(context, amount, rect.size.height - 4);
1039 CGContextAddLineToPoint(context, amount, radius + 4);
1041 CGContextFillPath(context);
1044 // Progress in the right arc
1045 else if (amount > radius + 4) {
1046 CGFloat x = amount - (rect.size.width - radius - 4);
1048 CGContextMoveToPoint(context, 4, rect.size.height/2);
1049 CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
1050 CGContextAddLineToPoint(context, rect.size.width - radius - 4, 4);
1051 CGFloat angle = -acos(x/radius);
1052 if (isnan(angle)) angle = 0;
1053 CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, M_PI, angle, 0);
1054 CGContextAddLineToPoint(context, amount, rect.size.height/2);
1056 CGContextMoveToPoint(context, 4, rect.size.height/2);
1057 CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
1058 CGContextAddLineToPoint(context, rect.size.width - radius - 4, rect.size.height - 4);
1059 angle = acos(x/radius);
1060 if (isnan(angle)) angle = 0;
1061 CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, -M_PI, angle, 1);
1062 CGContextAddLineToPoint(context, amount, rect.size.height/2);
1064 CGContextFillPath(context);
1067 // Progress is in the left arc
1068 else if (amount < radius + 4 && amount > 0) {
1069 CGContextMoveToPoint(context, 4, rect.size.height/2);
1070 CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
1071 CGContextAddLineToPoint(context, radius + 4, rect.size.height/2);
1073 CGContextMoveToPoint(context, 4, rect.size.height/2);
1074 CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
1075 CGContextAddLineToPoint(context, radius + 4, rect.size.height/2);
1077 CGContextFillPath(context);
1084 @interface MBBackgroundView ()
1086 #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV
1087 @property UIVisualEffectView *effectView;
1090 @property UIToolbar *toolbar;
1096 @implementation MBBackgroundView
1098 #pragma mark - Lifecycle
1100 - (instancetype)initWithFrame:(CGRect)frame {
1101 if ((self = [super initWithFrame:frame])) {
1102 if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_7_0) {
1103 _style = MBProgressHUDBackgroundStyleBlur;
1104 #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV
1105 _blurEffectStyle = UIBlurEffectStyleLight;
1107 if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) {
1108 _color = [UIColor colorWithWhite:0.8f alpha:0.6f];
1110 _color = [UIColor colorWithWhite:0.95f alpha:0.6f];
1113 _style = MBProgressHUDBackgroundStyleSolidColor;
1114 _color = [[UIColor blackColor] colorWithAlphaComponent:0.8];
1117 self.clipsToBounds = YES;
1119 [self updateForBackgroundStyle];
1124 #pragma mark - Layout
1126 - (CGSize)intrinsicContentSize {
1127 // Smallest size possible. Content pushes against this.
1131 #pragma mark - Appearance
1133 - (void)setStyle:(MBProgressHUDBackgroundStyle)style {
1134 if (style == MBProgressHUDBackgroundStyleBlur && kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0) {
1135 style = MBProgressHUDBackgroundStyleSolidColor;
1137 if (_style != style) {
1139 [self updateForBackgroundStyle];
1143 - (void)setColor:(UIColor *)color {
1144 NSAssert(color, @"The color should not be nil.");
1145 if (color != _color && ![color isEqual:_color]) {
1147 [self updateViewsForColor:color];
1151 #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV
1153 - (void)setBlurEffectStyle:(UIBlurEffectStyle)blurEffectStyle {
1154 if (_blurEffectStyle == blurEffectStyle) {
1158 _blurEffectStyle = blurEffectStyle;
1160 [self updateForBackgroundStyle];
1165 ///////////////////////////////////////////////////////////////////////////////////////////
1166 #pragma mark - Views
1168 - (void)updateForBackgroundStyle {
1169 MBProgressHUDBackgroundStyle style = self.style;
1170 if (style == MBProgressHUDBackgroundStyleBlur) {
1171 #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV
1172 if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) {
1173 UIBlurEffect *effect = [UIBlurEffect effectWithStyle:self.blurEffectStyle];
1174 UIVisualEffectView *effectView = [[UIVisualEffectView alloc] initWithEffect:effect];
1175 [self addSubview:effectView];
1176 effectView.frame = self.bounds;
1177 effectView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
1178 self.backgroundColor = self.color;
1179 self.layer.allowsGroupOpacity = NO;
1180 self.effectView = effectView;
1184 UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectInset(self.bounds, -100.f, -100.f)];
1185 toolbar.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
1186 toolbar.barTintColor = self.color;
1187 toolbar.translucent = YES;
1188 [self addSubview:toolbar];
1189 self.toolbar = toolbar;
1191 #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV
1195 #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV
1196 if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) {
1197 [self.effectView removeFromSuperview];
1198 self.effectView = nil;
1202 [self.toolbar removeFromSuperview];
1205 #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV
1208 self.backgroundColor = self.color;
1212 - (void)updateViewsForColor:(UIColor *)color {
1213 if (self.style == MBProgressHUDBackgroundStyleBlur) {
1214 if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) {
1215 self.backgroundColor = self.color;
1218 self.toolbar.barTintColor = color;
1222 self.backgroundColor = self.color;
1229 @implementation MBProgressHUD (Deprecated)
1231 #pragma mark - Class
1233 + (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated {
1234 NSArray *huds = [MBProgressHUD allHUDsForView:view];
1235 for (MBProgressHUD *hud in huds) {
1236 hud.removeFromSuperViewOnHide = YES;
1237 [hud hideAnimated:animated];
1239 return [huds count];
1242 + (NSArray *)allHUDsForView:(UIView *)view {
1243 NSMutableArray *huds = [NSMutableArray array];
1244 NSArray *subviews = view.subviews;
1245 for (UIView *aView in subviews) {
1246 if ([aView isKindOfClass:self]) {
1247 [huds addObject:aView];
1250 return [NSArray arrayWithArray:huds];
1253 #pragma mark - Lifecycle
1255 - (id)initWithWindow:(UIWindow *)window {
1256 return [self initWithView:window];
1259 #pragma mark - Show & hide
1261 - (void)show:(BOOL)animated {
1262 [self showAnimated:animated];
1265 - (void)hide:(BOOL)animated {
1266 [self hideAnimated:animated];
1269 - (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay {
1270 [self hideAnimated:animated afterDelay:delay];
1273 #pragma mark - Threading
1275 - (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated {
1276 [self showAnimated:animated whileExecutingBlock:^{
1277 #pragma clang diagnostic push
1278 #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
1279 // Start executing the requested task
1280 [target performSelector:method withObject:object];
1281 #pragma clang diagnostic pop
1285 - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block {
1286 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1287 [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:NULL];
1290 - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(void (^)(void))completion {
1291 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1292 [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:completion];
1295 - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue {
1296 [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:NULL];
1299 - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue completionBlock:(nullable MBProgressHUDCompletionBlock)completion {
1300 self.taskInProgress = YES;
1301 self.completionBlock = completion;
1302 dispatch_async(queue, ^(void) {
1304 dispatch_async(dispatch_get_main_queue(), ^(void) {
1308 [self showAnimated:animated];
1312 self.taskInProgress = NO;
1313 [self hideAnimated:self.useAnimation];
1316 #pragma mark - Labels
1318 - (NSString *)labelText {
1319 return self.label.text;
1322 - (void)setLabelText:(NSString *)labelText {
1323 MBMainThreadAssert();
1324 self.label.text = labelText;
1327 - (UIFont *)labelFont {
1328 return self.label.font;
1331 - (void)setLabelFont:(UIFont *)labelFont {
1332 MBMainThreadAssert();
1333 self.label.font = labelFont;
1336 - (UIColor *)labelColor {
1337 return self.label.textColor;
1340 - (void)setLabelColor:(UIColor *)labelColor {
1341 MBMainThreadAssert();
1342 self.label.textColor = labelColor;
1345 - (NSString *)detailsLabelText {
1346 return self.detailsLabel.text;
1349 - (void)setDetailsLabelText:(NSString *)detailsLabelText {
1350 MBMainThreadAssert();
1351 self.detailsLabel.text = detailsLabelText;
1354 - (UIFont *)detailsLabelFont {
1355 return self.detailsLabel.font;
1358 - (void)setDetailsLabelFont:(UIFont *)detailsLabelFont {
1359 MBMainThreadAssert();
1360 self.detailsLabel.font = detailsLabelFont;
1363 - (UIColor *)detailsLabelColor {
1364 return self.detailsLabel.textColor;
1367 - (void)setDetailsLabelColor:(UIColor *)detailsLabelColor {
1368 MBMainThreadAssert();
1369 self.detailsLabel.textColor = detailsLabelColor;
1372 - (CGFloat)opacity {
1376 - (void)setOpacity:(CGFloat)opacity {
1377 MBMainThreadAssert();
1381 - (UIColor *)color {
1382 return self.bezelView.color;
1385 - (void)setColor:(UIColor *)color {
1386 MBMainThreadAssert();
1387 self.bezelView.color = color;
1390 - (CGFloat)yOffset {
1391 return self.offset.y;
1394 - (void)setYOffset:(CGFloat)yOffset {
1395 MBMainThreadAssert();
1396 self.offset = CGPointMake(self.offset.x, yOffset);
1399 - (CGFloat)xOffset {
1400 return self.offset.x;
1403 - (void)setXOffset:(CGFloat)xOffset {
1404 MBMainThreadAssert();
1405 self.offset = CGPointMake(xOffset, self.offset.y);
1408 - (CGFloat)cornerRadius {
1409 return self.bezelView.layer.cornerRadius;
1412 - (void)setCornerRadius:(CGFloat)cornerRadius {
1413 MBMainThreadAssert();
1414 self.bezelView.layer.cornerRadius = cornerRadius;
1417 - (BOOL)dimBackground {
1418 MBBackgroundView *backgroundView = self.backgroundView;
1419 UIColor *dimmedColor = [UIColor colorWithWhite:0.f alpha:.2f];
1420 return backgroundView.style == MBProgressHUDBackgroundStyleSolidColor && [backgroundView.color isEqual:dimmedColor];
1423 - (void)setDimBackground:(BOOL)dimBackground {
1424 MBMainThreadAssert();
1425 self.backgroundView.style = MBProgressHUDBackgroundStyleSolidColor;
1426 self.backgroundView.color = dimBackground ? [UIColor colorWithWhite:0.f alpha:.2f] : [UIColor clearColor];
1430 return self.bezelView.frame.size;
1433 - (UIColor *)activityIndicatorColor {
1434 return _activityIndicatorColor;
1437 - (void)setActivityIndicatorColor:(UIColor *)activityIndicatorColor {
1438 if (activityIndicatorColor != _activityIndicatorColor) {
1439 _activityIndicatorColor = activityIndicatorColor;
1440 UIActivityIndicatorView *indicator = (UIActivityIndicatorView *)self.indicator;
1441 if ([indicator isKindOfClass:[UIActivityIndicatorView class]]) {
1442 [indicator setColor:activityIndicatorColor];
1449 @implementation MBProgressHUDRoundedButton
1451 #pragma mark - Lifecycle
1453 - (instancetype)initWithFrame:(CGRect)frame {
1454 self = [super initWithFrame:frame];
1456 CALayer *layer = self.layer;
1457 layer.borderWidth = 1.f;
1462 #pragma mark - Layout
1464 - (void)layoutSubviews {
1465 [super layoutSubviews];
1466 // Fully rounded corners
1467 CGFloat height = CGRectGetHeight(self.bounds);
1468 self.layer.cornerRadius = ceil(height / 2.f);
1471 - (CGSize)intrinsicContentSize {
1472 // Only show if we have associated control events
1473 if (self.allControlEvents == 0) return CGSizeZero;
1474 CGSize size = [super intrinsicContentSize];
1475 // Add some side padding
1480 #pragma mark - Color
1482 - (void)setTitleColor:(UIColor *)color forState:(UIControlState)state {
1483 [super setTitleColor:color forState:state];
1484 // Update related colors
1485 [self setHighlighted:self.highlighted];
1486 self.layer.borderColor = color.CGColor;
1489 - (void)setHighlighted:(BOOL)highlighted {
1490 [super setHighlighted:highlighted];
1491 UIColor *baseColor = [self titleColorForState:UIControlStateSelected];
1492 self.backgroundColor = highlighted ? [baseColor colorWithAlphaComponent:0.1f] : [UIColor clearColor];