--- /dev/null
+//
+// MBProgressHUD.m
+// Version 1.1.0
+// Created by Matej Bukovinski on 2.4.09.
+//
+
+#import "MBProgressHUD.h"
+#import <tgmath.h>
+
+
+#ifndef kCFCoreFoundationVersionNumber_iOS_7_0
+ #define kCFCoreFoundationVersionNumber_iOS_7_0 847.20
+#endif
+
+#ifndef kCFCoreFoundationVersionNumber_iOS_8_0
+ #define kCFCoreFoundationVersionNumber_iOS_8_0 1129.15
+#endif
+
+#define MBMainThreadAssert() NSAssert([NSThread isMainThread], @"MBProgressHUD needs to be accessed on the main thread.");
+
+CGFloat const MBProgressMaxOffset = 1000000.f;
+
+static const CGFloat MBDefaultPadding = 4.f;
+static const CGFloat MBDefaultLabelFontSize = 16.f;
+static const CGFloat MBDefaultDetailsLabelFontSize = 12.f;
+
+
+@interface MBProgressHUD () {
+ // Deprecated
+ UIColor *_activityIndicatorColor;
+ CGFloat _opacity;
+}
+
+@property (nonatomic, assign) BOOL useAnimation;
+@property (nonatomic, assign, getter=hasFinished) BOOL finished;
+@property (nonatomic, strong) UIView *indicator;
+@property (nonatomic, strong) NSDate *showStarted;
+@property (nonatomic, strong) NSArray *paddingConstraints;
+@property (nonatomic, strong) NSArray *bezelConstraints;
+@property (nonatomic, strong) UIView *topSpacer;
+@property (nonatomic, strong) UIView *bottomSpacer;
+@property (nonatomic, weak) NSTimer *graceTimer;
+@property (nonatomic, weak) NSTimer *minShowTimer;
+@property (nonatomic, weak) NSTimer *hideDelayTimer;
+@property (nonatomic, weak) CADisplayLink *progressObjectDisplayLink;
+
+// Deprecated
+@property (assign) BOOL taskInProgress;
+
+@end
+
+
+@interface MBProgressHUDRoundedButton : UIButton
+@end
+
+
+@implementation MBProgressHUD
+
+#pragma mark - Class methods
+
++ (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated {
+ MBProgressHUD *hud = [[self alloc] initWithView:view];
+ hud.removeFromSuperViewOnHide = YES;
+ [view addSubview:hud];
+ [hud showAnimated:animated];
+ return hud;
+}
+
++ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated {
+ MBProgressHUD *hud = [self HUDForView:view];
+ if (hud != nil) {
+ hud.removeFromSuperViewOnHide = YES;
+ [hud hideAnimated:animated];
+ return YES;
+ }
+ return NO;
+}
+
++ (MBProgressHUD *)HUDForView:(UIView *)view {
+ NSEnumerator *subviewsEnum = [view.subviews reverseObjectEnumerator];
+ for (UIView *subview in subviewsEnum) {
+ if ([subview isKindOfClass:self]) {
+ MBProgressHUD *hud = (MBProgressHUD *)subview;
+ if (hud.hasFinished == NO) {
+ return hud;
+ }
+ }
+ }
+ return nil;
+}
+
+#pragma mark - Lifecycle
+
+- (void)commonInit {
+ // Set default values for properties
+ _animationType = MBProgressHUDAnimationFade;
+ _mode = MBProgressHUDModeIndeterminate;
+ _margin = 20.0f;
+ _opacity = 1.f;
+ _defaultMotionEffectsEnabled = YES;
+
+ // Default color, depending on the current iOS version
+ BOOL isLegacy = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0;
+ _contentColor = isLegacy ? [UIColor whiteColor] : [UIColor colorWithWhite:0.f alpha:0.7f];
+ // Transparent background
+ self.opaque = NO;
+ self.backgroundColor = [UIColor clearColor];
+ // Make it invisible for now
+ self.alpha = 0.0f;
+ self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ self.layer.allowsGroupOpacity = NO;
+
+ [self setupViews];
+ [self updateIndicators];
+ [self registerForNotifications];
+}
+
+- (instancetype)initWithFrame:(CGRect)frame {
+ if ((self = [super initWithFrame:frame])) {
+ [self commonInit];
+ }
+ return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)aDecoder {
+ if ((self = [super initWithCoder:aDecoder])) {
+ [self commonInit];
+ }
+ return self;
+}
+
+- (id)initWithView:(UIView *)view {
+ NSAssert(view, @"View must not be nil.");
+ return [self initWithFrame:view.bounds];
+}
+
+- (void)dealloc {
+ [self unregisterFromNotifications];
+}
+
+#pragma mark - Show & hide
+
+- (void)showAnimated:(BOOL)animated {
+ MBMainThreadAssert();
+ [self.minShowTimer invalidate];
+ self.useAnimation = animated;
+ self.finished = NO;
+ // If the grace time is set, postpone the HUD display
+ if (self.graceTime > 0.0) {
+ NSTimer *timer = [NSTimer timerWithTimeInterval:self.graceTime target:self selector:@selector(handleGraceTimer:) userInfo:nil repeats:NO];
+ [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
+ self.graceTimer = timer;
+ }
+ // ... otherwise show the HUD immediately
+ else {
+ [self showUsingAnimation:self.useAnimation];
+ }
+}
+
+- (void)hideAnimated:(BOOL)animated {
+ MBMainThreadAssert();
+ [self.graceTimer invalidate];
+ self.useAnimation = animated;
+ self.finished = YES;
+ // If the minShow time is set, calculate how long the HUD was shown,
+ // and postpone the hiding operation if necessary
+ if (self.minShowTime > 0.0 && self.showStarted) {
+ NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:self.showStarted];
+ if (interv < self.minShowTime) {
+ NSTimer *timer = [NSTimer timerWithTimeInterval:(self.minShowTime - interv) target:self selector:@selector(handleMinShowTimer:) userInfo:nil repeats:NO];
+ [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
+ self.minShowTimer = timer;
+ return;
+ }
+ }
+ // ... otherwise hide the HUD immediately
+ [self hideUsingAnimation:self.useAnimation];
+}
+
+- (void)hideAnimated:(BOOL)animated afterDelay:(NSTimeInterval)delay {
+ // Cancel any scheduled hideDelayed: calls
+ [self.hideDelayTimer invalidate];
+
+ NSTimer *timer = [NSTimer timerWithTimeInterval:delay target:self selector:@selector(handleHideTimer:) userInfo:@(animated) repeats:NO];
+ [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
+ self.hideDelayTimer = timer;
+}
+
+#pragma mark - Timer callbacks
+
+- (void)handleGraceTimer:(NSTimer *)theTimer {
+ // Show the HUD only if the task is still running
+ if (!self.hasFinished) {
+ [self showUsingAnimation:self.useAnimation];
+ }
+}
+
+- (void)handleMinShowTimer:(NSTimer *)theTimer {
+ [self hideUsingAnimation:self.useAnimation];
+}
+
+- (void)handleHideTimer:(NSTimer *)timer {
+ [self hideAnimated:[timer.userInfo boolValue]];
+}
+
+#pragma mark - View Hierrarchy
+
+- (void)didMoveToSuperview {
+ [self updateForCurrentOrientationAnimated:NO];
+}
+
+#pragma mark - Internal show & hide operations
+
+- (void)showUsingAnimation:(BOOL)animated {
+ // Cancel any previous animations
+ [self.bezelView.layer removeAllAnimations];
+ [self.backgroundView.layer removeAllAnimations];
+
+ // Cancel any scheduled hideDelayed: calls
+ [self.hideDelayTimer invalidate];
+
+ self.showStarted = [NSDate date];
+ self.alpha = 1.f;
+
+ // Needed in case we hide and re-show with the same NSProgress object attached.
+ [self setNSProgressDisplayLinkEnabled:YES];
+
+ if (animated) {
+ [self animateIn:YES withType:self.animationType completion:NULL];
+ } else {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ self.bezelView.alpha = self.opacity;
+#pragma clang diagnostic pop
+ self.backgroundView.alpha = 1.f;
+ }
+}
+
+- (void)hideUsingAnimation:(BOOL)animated {
+ if (animated && self.showStarted) {
+ self.showStarted = nil;
+ [self animateIn:NO withType:self.animationType completion:^(BOOL finished) {
+ [self done];
+ }];
+ } else {
+ self.showStarted = nil;
+ self.bezelView.alpha = 0.f;
+ self.backgroundView.alpha = 1.f;
+ [self done];
+ }
+}
+
+- (void)animateIn:(BOOL)animatingIn withType:(MBProgressHUDAnimation)type completion:(void(^)(BOOL finished))completion {
+ // Automatically determine the correct zoom animation type
+ if (type == MBProgressHUDAnimationZoom) {
+ type = animatingIn ? MBProgressHUDAnimationZoomIn : MBProgressHUDAnimationZoomOut;
+ }
+
+ CGAffineTransform small = CGAffineTransformMakeScale(0.5f, 0.5f);
+ CGAffineTransform large = CGAffineTransformMakeScale(1.5f, 1.5f);
+
+ // Set starting state
+ UIView *bezelView = self.bezelView;
+ if (animatingIn && bezelView.alpha == 0.f && type == MBProgressHUDAnimationZoomIn) {
+ bezelView.transform = small;
+ } else if (animatingIn && bezelView.alpha == 0.f && type == MBProgressHUDAnimationZoomOut) {
+ bezelView.transform = large;
+ }
+
+ // Perform animations
+ dispatch_block_t animations = ^{
+ if (animatingIn) {
+ bezelView.transform = CGAffineTransformIdentity;
+ } else if (!animatingIn && type == MBProgressHUDAnimationZoomIn) {
+ bezelView.transform = large;
+ } else if (!animatingIn && type == MBProgressHUDAnimationZoomOut) {
+ bezelView.transform = small;
+ }
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ bezelView.alpha = animatingIn ? self.opacity : 0.f;
+#pragma clang diagnostic pop
+ self.backgroundView.alpha = animatingIn ? 1.f : 0.f;
+ };
+
+ // Spring animations are nicer, but only available on iOS 7+
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 || TARGET_OS_TV
+ if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_7_0) {
+ [UIView animateWithDuration:0.3 delay:0. usingSpringWithDamping:1.f initialSpringVelocity:0.f options:UIViewAnimationOptionBeginFromCurrentState animations:animations completion:completion];
+ return;
+ }
+#endif
+ [UIView animateWithDuration:0.3 delay:0. options:UIViewAnimationOptionBeginFromCurrentState animations:animations completion:completion];
+}
+
+- (void)done {
+ // Cancel any scheduled hideDelayed: calls
+ [self.hideDelayTimer invalidate];
+ [self setNSProgressDisplayLinkEnabled:NO];
+
+ if (self.hasFinished) {
+ self.alpha = 0.0f;
+ if (self.removeFromSuperViewOnHide) {
+ [self removeFromSuperview];
+ }
+ }
+ MBProgressHUDCompletionBlock completionBlock = self.completionBlock;
+ if (completionBlock) {
+ completionBlock();
+ }
+ id<MBProgressHUDDelegate> delegate = self.delegate;
+ if ([delegate respondsToSelector:@selector(hudWasHidden:)]) {
+ [delegate performSelector:@selector(hudWasHidden:) withObject:self];
+ }
+}
+
+#pragma mark - UI
+
+- (void)setupViews {
+ UIColor *defaultColor = self.contentColor;
+
+ MBBackgroundView *backgroundView = [[MBBackgroundView alloc] initWithFrame:self.bounds];
+ backgroundView.style = MBProgressHUDBackgroundStyleSolidColor;
+ backgroundView.backgroundColor = [UIColor clearColor];
+ backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ backgroundView.alpha = 0.f;
+ [self addSubview:backgroundView];
+ _backgroundView = backgroundView;
+
+ MBBackgroundView *bezelView = [MBBackgroundView new];
+ bezelView.translatesAutoresizingMaskIntoConstraints = NO;
+ bezelView.layer.cornerRadius = 5.f;
+ bezelView.alpha = 0.f;
+ [self addSubview:bezelView];
+ _bezelView = bezelView;
+ [self updateBezelMotionEffects];
+
+ UILabel *label = [UILabel new];
+ label.adjustsFontSizeToFitWidth = NO;
+ label.textAlignment = NSTextAlignmentCenter;
+ label.textColor = defaultColor;
+ label.font = [UIFont boldSystemFontOfSize:MBDefaultLabelFontSize];
+ label.opaque = NO;
+ label.backgroundColor = [UIColor clearColor];
+ _label = label;
+
+ UILabel *detailsLabel = [UILabel new];
+ detailsLabel.adjustsFontSizeToFitWidth = NO;
+ detailsLabel.textAlignment = NSTextAlignmentCenter;
+ detailsLabel.textColor = defaultColor;
+ detailsLabel.numberOfLines = 0;
+ detailsLabel.font = [UIFont boldSystemFontOfSize:MBDefaultDetailsLabelFontSize];
+ detailsLabel.opaque = NO;
+ detailsLabel.backgroundColor = [UIColor clearColor];
+ _detailsLabel = detailsLabel;
+
+ UIButton *button = [MBProgressHUDRoundedButton buttonWithType:UIButtonTypeCustom];
+ button.titleLabel.textAlignment = NSTextAlignmentCenter;
+ button.titleLabel.font = [UIFont boldSystemFontOfSize:MBDefaultDetailsLabelFontSize];
+ [button setTitleColor:defaultColor forState:UIControlStateNormal];
+ _button = button;
+
+ for (UIView *view in @[label, detailsLabel, button]) {
+ view.translatesAutoresizingMaskIntoConstraints = NO;
+ [view setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisHorizontal];
+ [view setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisVertical];
+ [bezelView addSubview:view];
+ }
+
+ UIView *topSpacer = [UIView new];
+ topSpacer.translatesAutoresizingMaskIntoConstraints = NO;
+ topSpacer.hidden = YES;
+ [bezelView addSubview:topSpacer];
+ _topSpacer = topSpacer;
+
+ UIView *bottomSpacer = [UIView new];
+ bottomSpacer.translatesAutoresizingMaskIntoConstraints = NO;
+ bottomSpacer.hidden = YES;
+ [bezelView addSubview:bottomSpacer];
+ _bottomSpacer = bottomSpacer;
+}
+
+- (void)updateIndicators {
+ UIView *indicator = self.indicator;
+ BOOL isActivityIndicator = [indicator isKindOfClass:[UIActivityIndicatorView class]];
+ BOOL isRoundIndicator = [indicator isKindOfClass:[MBRoundProgressView class]];
+
+ MBProgressHUDMode mode = self.mode;
+ if (mode == MBProgressHUDModeIndeterminate) {
+ if (!isActivityIndicator) {
+ // Update to indeterminate indicator
+ [indicator removeFromSuperview];
+ indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
+ [(UIActivityIndicatorView *)indicator startAnimating];
+ [self.bezelView addSubview:indicator];
+ }
+ }
+ else if (mode == MBProgressHUDModeDeterminateHorizontalBar) {
+ // Update to bar determinate indicator
+ [indicator removeFromSuperview];
+ indicator = [[MBBarProgressView alloc] init];
+ [self.bezelView addSubview:indicator];
+ }
+ else if (mode == MBProgressHUDModeDeterminate || mode == MBProgressHUDModeAnnularDeterminate) {
+ if (!isRoundIndicator) {
+ // Update to determinante indicator
+ [indicator removeFromSuperview];
+ indicator = [[MBRoundProgressView alloc] init];
+ [self.bezelView addSubview:indicator];
+ }
+ if (mode == MBProgressHUDModeAnnularDeterminate) {
+ [(MBRoundProgressView *)indicator setAnnular:YES];
+ }
+ }
+ else if (mode == MBProgressHUDModeCustomView && self.customView != indicator) {
+ // Update custom view indicator
+ [indicator removeFromSuperview];
+ indicator = self.customView;
+ [self.bezelView addSubview:indicator];
+ }
+ else if (mode == MBProgressHUDModeText) {
+ [indicator removeFromSuperview];
+ indicator = nil;
+ }
+ indicator.translatesAutoresizingMaskIntoConstraints = NO;
+ self.indicator = indicator;
+
+ if ([indicator respondsToSelector:@selector(setProgress:)]) {
+ [(id)indicator setValue:@(self.progress) forKey:@"progress"];
+ }
+
+ [indicator setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisHorizontal];
+ [indicator setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisVertical];
+
+ [self updateViewsForColor:self.contentColor];
+ [self setNeedsUpdateConstraints];
+}
+
+- (void)updateViewsForColor:(UIColor *)color {
+ if (!color) return;
+
+ self.label.textColor = color;
+ self.detailsLabel.textColor = color;
+ [self.button setTitleColor:color forState:UIControlStateNormal];
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ if (self.activityIndicatorColor) {
+ color = self.activityIndicatorColor;
+ }
+#pragma clang diagnostic pop
+
+ // UIAppearance settings are prioritized. If they are preset the set color is ignored.
+
+ UIView *indicator = self.indicator;
+ if ([indicator isKindOfClass:[UIActivityIndicatorView class]]) {
+ UIActivityIndicatorView *appearance = nil;
+#if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000
+ appearance = [UIActivityIndicatorView appearanceWhenContainedIn:[MBProgressHUD class], nil];
+#else
+ // For iOS 9+
+ appearance = [UIActivityIndicatorView appearanceWhenContainedInInstancesOfClasses:@[[MBProgressHUD class]]];
+#endif
+
+ if (appearance.color == nil) {
+ ((UIActivityIndicatorView *)indicator).color = color;
+ }
+ } else if ([indicator isKindOfClass:[MBRoundProgressView class]]) {
+ MBRoundProgressView *appearance = nil;
+#if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000
+ appearance = [MBRoundProgressView appearanceWhenContainedIn:[MBProgressHUD class], nil];
+#else
+ appearance = [MBRoundProgressView appearanceWhenContainedInInstancesOfClasses:@[[MBProgressHUD class]]];
+#endif
+ if (appearance.progressTintColor == nil) {
+ ((MBRoundProgressView *)indicator).progressTintColor = color;
+ }
+ if (appearance.backgroundTintColor == nil) {
+ ((MBRoundProgressView *)indicator).backgroundTintColor = [color colorWithAlphaComponent:0.1];
+ }
+ } else if ([indicator isKindOfClass:[MBBarProgressView class]]) {
+ MBBarProgressView *appearance = nil;
+#if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000
+ appearance = [MBBarProgressView appearanceWhenContainedIn:[MBProgressHUD class], nil];
+#else
+ appearance = [MBBarProgressView appearanceWhenContainedInInstancesOfClasses:@[[MBProgressHUD class]]];
+#endif
+ if (appearance.progressColor == nil) {
+ ((MBBarProgressView *)indicator).progressColor = color;
+ }
+ if (appearance.lineColor == nil) {
+ ((MBBarProgressView *)indicator).lineColor = color;
+ }
+ } else {
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 || TARGET_OS_TV
+ if ([indicator respondsToSelector:@selector(setTintColor:)]) {
+ [indicator setTintColor:color];
+ }
+#endif
+ }
+}
+
+- (void)updateBezelMotionEffects {
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 || TARGET_OS_TV
+ MBBackgroundView *bezelView = self.bezelView;
+ if (![bezelView respondsToSelector:@selector(addMotionEffect:)]) return;
+
+ if (self.defaultMotionEffectsEnabled) {
+ CGFloat effectOffset = 10.f;
+ UIInterpolatingMotionEffect *effectX = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
+ effectX.maximumRelativeValue = @(effectOffset);
+ effectX.minimumRelativeValue = @(-effectOffset);
+
+ UIInterpolatingMotionEffect *effectY = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
+ effectY.maximumRelativeValue = @(effectOffset);
+ effectY.minimumRelativeValue = @(-effectOffset);
+
+ UIMotionEffectGroup *group = [[UIMotionEffectGroup alloc] init];
+ group.motionEffects = @[effectX, effectY];
+
+ [bezelView addMotionEffect:group];
+ } else {
+ NSArray *effects = [bezelView motionEffects];
+ for (UIMotionEffect *effect in effects) {
+ [bezelView removeMotionEffect:effect];
+ }
+ }
+#endif
+}
+
+#pragma mark - Layout
+
+- (void)updateConstraints {
+ UIView *bezel = self.bezelView;
+ UIView *topSpacer = self.topSpacer;
+ UIView *bottomSpacer = self.bottomSpacer;
+ CGFloat margin = self.margin;
+ NSMutableArray *bezelConstraints = [NSMutableArray array];
+ NSDictionary *metrics = @{@"margin": @(margin)};
+
+ NSMutableArray *subviews = [NSMutableArray arrayWithObjects:self.topSpacer, self.label, self.detailsLabel, self.button, self.bottomSpacer, nil];
+ if (self.indicator) [subviews insertObject:self.indicator atIndex:1];
+
+ // Remove existing constraints
+ [self removeConstraints:self.constraints];
+ [topSpacer removeConstraints:topSpacer.constraints];
+ [bottomSpacer removeConstraints:bottomSpacer.constraints];
+ if (self.bezelConstraints) {
+ [bezel removeConstraints:self.bezelConstraints];
+ self.bezelConstraints = nil;
+ }
+
+ // Center bezel in container (self), applying the offset if set
+ CGPoint offset = self.offset;
+ NSMutableArray *centeringConstraints = [NSMutableArray array];
+ [centeringConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.f constant:offset.x]];
+ [centeringConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.f constant:offset.y]];
+ [self applyPriority:998.f toConstraints:centeringConstraints];
+ [self addConstraints:centeringConstraints];
+
+ // Ensure minimum side margin is kept
+ NSMutableArray *sideConstraints = [NSMutableArray array];
+ [sideConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=margin)-[bezel]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(bezel)]];
+ [sideConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=margin)-[bezel]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(bezel)]];
+ [self applyPriority:999.f toConstraints:sideConstraints];
+ [self addConstraints:sideConstraints];
+
+ // Minimum bezel size, if set
+ CGSize minimumSize = self.minSize;
+ if (!CGSizeEqualToSize(minimumSize, CGSizeZero)) {
+ NSMutableArray *minSizeConstraints = [NSMutableArray array];
+ [minSizeConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:minimumSize.width]];
+ [minSizeConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:minimumSize.height]];
+ [self applyPriority:997.f toConstraints:minSizeConstraints];
+ [bezelConstraints addObjectsFromArray:minSizeConstraints];
+ }
+
+ // Square aspect ratio, if set
+ if (self.square) {
+ NSLayoutConstraint *square = [NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeWidth multiplier:1.f constant:0];
+ square.priority = 997.f;
+ [bezelConstraints addObject:square];
+ }
+
+ // Top and bottom spacing
+ [topSpacer addConstraint:[NSLayoutConstraint constraintWithItem:topSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:margin]];
+ [bottomSpacer addConstraint:[NSLayoutConstraint constraintWithItem:bottomSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:margin]];
+ // Top and bottom spaces should be equal
+ [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:topSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:bottomSpacer attribute:NSLayoutAttributeHeight multiplier:1.f constant:0.f]];
+
+ // Layout subviews in bezel
+ NSMutableArray *paddingConstraints = [NSMutableArray new];
+ [subviews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) {
+ // Center in bezel
+ [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeCenterX multiplier:1.f constant:0.f]];
+ // Ensure the minimum edge margin is kept
+ [bezelConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=margin)-[view]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(view)]];
+ // Element spacing
+ if (idx == 0) {
+ // First, ensure spacing to bezel edge
+ [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeTop multiplier:1.f constant:0.f]];
+ } else if (idx == subviews.count - 1) {
+ // Last, ensure spacing to bezel edge
+ [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeBottom multiplier:1.f constant:0.f]];
+ }
+ if (idx > 0) {
+ // Has previous
+ NSLayoutConstraint *padding = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:subviews[idx - 1] attribute:NSLayoutAttributeBottom multiplier:1.f constant:0.f];
+ [bezelConstraints addObject:padding];
+ [paddingConstraints addObject:padding];
+ }
+ }];
+
+ [bezel addConstraints:bezelConstraints];
+ self.bezelConstraints = bezelConstraints;
+
+ self.paddingConstraints = [paddingConstraints copy];
+ [self updatePaddingConstraints];
+
+ [super updateConstraints];
+}
+
+- (void)layoutSubviews {
+ // There is no need to update constraints if they are going to
+ // be recreated in [super layoutSubviews] due to needsUpdateConstraints being set.
+ // This also avoids an issue on iOS 8, where updatePaddingConstraints
+ // would trigger a zombie object access.
+ if (!self.needsUpdateConstraints) {
+ [self updatePaddingConstraints];
+ }
+ [super layoutSubviews];
+}
+
+- (void)updatePaddingConstraints {
+ // Set padding dynamically, depending on whether the view is visible or not
+ __block BOOL hasVisibleAncestors = NO;
+ [self.paddingConstraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *padding, NSUInteger idx, BOOL *stop) {
+ UIView *firstView = (UIView *)padding.firstItem;
+ UIView *secondView = (UIView *)padding.secondItem;
+ BOOL firstVisible = !firstView.hidden && !CGSizeEqualToSize(firstView.intrinsicContentSize, CGSizeZero);
+ BOOL secondVisible = !secondView.hidden && !CGSizeEqualToSize(secondView.intrinsicContentSize, CGSizeZero);
+ // Set if both views are visible or if there's a visible view on top that doesn't have padding
+ // added relative to the current view yet
+ padding.constant = (firstVisible && (secondVisible || hasVisibleAncestors)) ? MBDefaultPadding : 0.f;
+ hasVisibleAncestors |= secondVisible;
+ }];
+}
+
+- (void)applyPriority:(UILayoutPriority)priority toConstraints:(NSArray *)constraints {
+ for (NSLayoutConstraint *constraint in constraints) {
+ constraint.priority = priority;
+ }
+}
+
+#pragma mark - Properties
+
+- (void)setMode:(MBProgressHUDMode)mode {
+ if (mode != _mode) {
+ _mode = mode;
+ [self updateIndicators];
+ }
+}
+
+- (void)setCustomView:(UIView *)customView {
+ if (customView != _customView) {
+ _customView = customView;
+ if (self.mode == MBProgressHUDModeCustomView) {
+ [self updateIndicators];
+ }
+ }
+}
+
+- (void)setOffset:(CGPoint)offset {
+ if (!CGPointEqualToPoint(offset, _offset)) {
+ _offset = offset;
+ [self setNeedsUpdateConstraints];
+ }
+}
+
+- (void)setMargin:(CGFloat)margin {
+ if (margin != _margin) {
+ _margin = margin;
+ [self setNeedsUpdateConstraints];
+ }
+}
+
+- (void)setMinSize:(CGSize)minSize {
+ if (!CGSizeEqualToSize(minSize, _minSize)) {
+ _minSize = minSize;
+ [self setNeedsUpdateConstraints];
+ }
+}
+
+- (void)setSquare:(BOOL)square {
+ if (square != _square) {
+ _square = square;
+ [self setNeedsUpdateConstraints];
+ }
+}
+
+- (void)setProgressObjectDisplayLink:(CADisplayLink *)progressObjectDisplayLink {
+ if (progressObjectDisplayLink != _progressObjectDisplayLink) {
+ [_progressObjectDisplayLink invalidate];
+
+ _progressObjectDisplayLink = progressObjectDisplayLink;
+
+ [_progressObjectDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
+ }
+}
+
+- (void)setProgressObject:(NSProgress *)progressObject {
+ if (progressObject != _progressObject) {
+ _progressObject = progressObject;
+ [self setNSProgressDisplayLinkEnabled:YES];
+ }
+}
+
+- (void)setProgress:(float)progress {
+ if (progress != _progress) {
+ _progress = progress;
+ UIView *indicator = self.indicator;
+ if ([indicator respondsToSelector:@selector(setProgress:)]) {
+ [(id)indicator setValue:@(self.progress) forKey:@"progress"];
+ }
+ }
+}
+
+- (void)setContentColor:(UIColor *)contentColor {
+ if (contentColor != _contentColor && ![contentColor isEqual:_contentColor]) {
+ _contentColor = contentColor;
+ [self updateViewsForColor:contentColor];
+ }
+}
+
+- (void)setDefaultMotionEffectsEnabled:(BOOL)defaultMotionEffectsEnabled {
+ if (defaultMotionEffectsEnabled != _defaultMotionEffectsEnabled) {
+ _defaultMotionEffectsEnabled = defaultMotionEffectsEnabled;
+ [self updateBezelMotionEffects];
+ }
+}
+
+#pragma mark - NSProgress
+
+- (void)setNSProgressDisplayLinkEnabled:(BOOL)enabled {
+ // We're using CADisplayLink, because NSProgress can change very quickly and observing it may starve the main thread,
+ // so we're refreshing the progress only every frame draw
+ if (enabled && self.progressObject) {
+ // Only create if not already active.
+ if (!self.progressObjectDisplayLink) {
+ self.progressObjectDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateProgressFromProgressObject)];
+ }
+ } else {
+ self.progressObjectDisplayLink = nil;
+ }
+}
+
+- (void)updateProgressFromProgressObject {
+ self.progress = self.progressObject.fractionCompleted;
+}
+
+#pragma mark - Notifications
+
+- (void)registerForNotifications {
+#if !TARGET_OS_TV
+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+
+ [nc addObserver:self selector:@selector(statusBarOrientationDidChange:)
+ name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
+#endif
+}
+
+- (void)unregisterFromNotifications {
+#if !TARGET_OS_TV
+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+ [nc removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
+#endif
+}
+
+#if !TARGET_OS_TV
+- (void)statusBarOrientationDidChange:(NSNotification *)notification {
+ UIView *superview = self.superview;
+ if (!superview) {
+ return;
+ } else {
+ [self updateForCurrentOrientationAnimated:YES];
+ }
+}
+#endif
+
+- (void)updateForCurrentOrientationAnimated:(BOOL)animated {
+ // Stay in sync with the superview in any case
+ if (self.superview) {
+ self.frame = self.superview.bounds;
+ }
+
+ // Not needed on iOS 8+, compile out when the deployment target allows,
+ // to avoid sharedApplication problems on extension targets
+#if __IPHONE_OS_VERSION_MIN_REQUIRED < 80000
+ // Only needed pre iOS 8 when added to a window
+ BOOL iOS8OrLater = kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0;
+ if (iOS8OrLater || ![self.superview isKindOfClass:[UIWindow class]]) return;
+
+ // Make extension friendly. Will not get called on extensions (iOS 8+) due to the above check.
+ // This just ensures we don't get a warning about extension-unsafe API.
+ Class UIApplicationClass = NSClassFromString(@"UIApplication");
+ if (!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) return;
+
+ UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)];
+ UIInterfaceOrientation orientation = application.statusBarOrientation;
+ CGFloat radians = 0;
+
+ if (UIInterfaceOrientationIsLandscape(orientation)) {
+ radians = orientation == UIInterfaceOrientationLandscapeLeft ? -(CGFloat)M_PI_2 : (CGFloat)M_PI_2;
+ // Window coordinates differ!
+ self.bounds = CGRectMake(0, 0, self.bounds.size.height, self.bounds.size.width);
+ } else {
+ radians = orientation == UIInterfaceOrientationPortraitUpsideDown ? (CGFloat)M_PI : 0.f;
+ }
+
+ if (animated) {
+ [UIView animateWithDuration:0.3 animations:^{
+ self.transform = CGAffineTransformMakeRotation(radians);
+ }];
+ } else {
+ self.transform = CGAffineTransformMakeRotation(radians);
+ }
+#endif
+}
+
+@end
+
+
+@implementation MBRoundProgressView
+
+#pragma mark - Lifecycle
+
+- (id)init {
+ return [self initWithFrame:CGRectMake(0.f, 0.f, 37.f, 37.f)];
+}
+
+- (id)initWithFrame:(CGRect)frame {
+ self = [super initWithFrame:frame];
+ if (self) {
+ self.backgroundColor = [UIColor clearColor];
+ self.opaque = NO;
+ _progress = 0.f;
+ _annular = NO;
+ _progressTintColor = [[UIColor alloc] initWithWhite:1.f alpha:1.f];
+ _backgroundTintColor = [[UIColor alloc] initWithWhite:1.f alpha:.1f];
+ }
+ return self;
+}
+
+#pragma mark - Layout
+
+- (CGSize)intrinsicContentSize {
+ return CGSizeMake(37.f, 37.f);
+}
+
+#pragma mark - Properties
+
+- (void)setProgress:(float)progress {
+ if (progress != _progress) {
+ _progress = progress;
+ [self setNeedsDisplay];
+ }
+}
+
+- (void)setProgressTintColor:(UIColor *)progressTintColor {
+ NSAssert(progressTintColor, @"The color should not be nil.");
+ if (progressTintColor != _progressTintColor && ![progressTintColor isEqual:_progressTintColor]) {
+ _progressTintColor = progressTintColor;
+ [self setNeedsDisplay];
+ }
+}
+
+- (void)setBackgroundTintColor:(UIColor *)backgroundTintColor {
+ NSAssert(backgroundTintColor, @"The color should not be nil.");
+ if (backgroundTintColor != _backgroundTintColor && ![backgroundTintColor isEqual:_backgroundTintColor]) {
+ _backgroundTintColor = backgroundTintColor;
+ [self setNeedsDisplay];
+ }
+}
+
+#pragma mark - Drawing
+
+- (void)drawRect:(CGRect)rect {
+ CGContextRef context = UIGraphicsGetCurrentContext();
+ BOOL isPreiOS7 = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0;
+
+ if (_annular) {
+ // Draw background
+ CGFloat lineWidth = isPreiOS7 ? 5.f : 2.f;
+ UIBezierPath *processBackgroundPath = [UIBezierPath bezierPath];
+ processBackgroundPath.lineWidth = lineWidth;
+ processBackgroundPath.lineCapStyle = kCGLineCapButt;
+ CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
+ CGFloat radius = (self.bounds.size.width - lineWidth)/2;
+ CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees
+ CGFloat endAngle = (2 * (float)M_PI) + startAngle;
+ [processBackgroundPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
+ [_backgroundTintColor set];
+ [processBackgroundPath stroke];
+ // Draw progress
+ UIBezierPath *processPath = [UIBezierPath bezierPath];
+ processPath.lineCapStyle = isPreiOS7 ? kCGLineCapRound : kCGLineCapSquare;
+ processPath.lineWidth = lineWidth;
+ endAngle = (self.progress * 2 * (float)M_PI) + startAngle;
+ [processPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
+ [_progressTintColor set];
+ [processPath stroke];
+ } else {
+ // Draw background
+ CGFloat lineWidth = 2.f;
+ CGRect allRect = self.bounds;
+ CGRect circleRect = CGRectInset(allRect, lineWidth/2.f, lineWidth/2.f);
+ CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
+ [_progressTintColor setStroke];
+ [_backgroundTintColor setFill];
+ CGContextSetLineWidth(context, lineWidth);
+ if (isPreiOS7) {
+ CGContextFillEllipseInRect(context, circleRect);
+ }
+ CGContextStrokeEllipseInRect(context, circleRect);
+ // 90 degrees
+ CGFloat startAngle = - ((float)M_PI / 2.f);
+ // Draw progress
+ if (isPreiOS7) {
+ CGFloat radius = (CGRectGetWidth(self.bounds) / 2.f) - lineWidth;
+ CGFloat endAngle = (self.progress * 2.f * (float)M_PI) + startAngle;
+ [_progressTintColor setFill];
+ CGContextMoveToPoint(context, center.x, center.y);
+ CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, 0);
+ CGContextClosePath(context);
+ CGContextFillPath(context);
+ } else {
+ UIBezierPath *processPath = [UIBezierPath bezierPath];
+ processPath.lineCapStyle = kCGLineCapButt;
+ processPath.lineWidth = lineWidth * 2.f;
+ CGFloat radius = (CGRectGetWidth(self.bounds) / 2.f) - (processPath.lineWidth / 2.f);
+ CGFloat endAngle = (self.progress * 2.f * (float)M_PI) + startAngle;
+ [processPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
+ // Ensure that we don't get color overlapping when _progressTintColor alpha < 1.f.
+ CGContextSetBlendMode(context, kCGBlendModeCopy);
+ [_progressTintColor set];
+ [processPath stroke];
+ }
+ }
+}
+
+@end
+
+
+@implementation MBBarProgressView
+
+#pragma mark - Lifecycle
+
+- (id)init {
+ return [self initWithFrame:CGRectMake(.0f, .0f, 120.0f, 20.0f)];
+}
+
+- (id)initWithFrame:(CGRect)frame {
+ self = [super initWithFrame:frame];
+ if (self) {
+ _progress = 0.f;
+ _lineColor = [UIColor whiteColor];
+ _progressColor = [UIColor whiteColor];
+ _progressRemainingColor = [UIColor clearColor];
+ self.backgroundColor = [UIColor clearColor];
+ self.opaque = NO;
+ }
+ return self;
+}
+
+#pragma mark - Layout
+
+- (CGSize)intrinsicContentSize {
+ BOOL isPreiOS7 = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0;
+ return CGSizeMake(120.f, isPreiOS7 ? 20.f : 10.f);
+}
+
+#pragma mark - Properties
+
+- (void)setProgress:(float)progress {
+ if (progress != _progress) {
+ _progress = progress;
+ [self setNeedsDisplay];
+ }
+}
+
+- (void)setProgressColor:(UIColor *)progressColor {
+ NSAssert(progressColor, @"The color should not be nil.");
+ if (progressColor != _progressColor && ![progressColor isEqual:_progressColor]) {
+ _progressColor = progressColor;
+ [self setNeedsDisplay];
+ }
+}
+
+- (void)setProgressRemainingColor:(UIColor *)progressRemainingColor {
+ NSAssert(progressRemainingColor, @"The color should not be nil.");
+ if (progressRemainingColor != _progressRemainingColor && ![progressRemainingColor isEqual:_progressRemainingColor]) {
+ _progressRemainingColor = progressRemainingColor;
+ [self setNeedsDisplay];
+ }
+}
+
+#pragma mark - Drawing
+
+- (void)drawRect:(CGRect)rect {
+ CGContextRef context = UIGraphicsGetCurrentContext();
+
+ CGContextSetLineWidth(context, 2);
+ CGContextSetStrokeColorWithColor(context,[_lineColor CGColor]);
+ CGContextSetFillColorWithColor(context, [_progressRemainingColor CGColor]);
+
+ // Draw background and Border
+ CGFloat radius = (rect.size.height / 2) - 2;
+ CGContextMoveToPoint(context, 2, rect.size.height/2);
+ CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius);
+ CGContextAddArcToPoint(context, rect.size.width - 2, 2, rect.size.width - 2, rect.size.height / 2, radius);
+ CGContextAddArcToPoint(context, rect.size.width - 2, rect.size.height - 2, rect.size.width - radius - 2, rect.size.height - 2, radius);
+ CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius);
+ CGContextDrawPath(context, kCGPathFillStroke);
+
+ CGContextSetFillColorWithColor(context, [_progressColor CGColor]);
+ radius = radius - 2;
+ CGFloat amount = self.progress * rect.size.width;
+
+ // Progress in the middle area
+ if (amount >= radius + 4 && amount <= (rect.size.width - radius - 4)) {
+ CGContextMoveToPoint(context, 4, rect.size.height/2);
+ CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
+ CGContextAddLineToPoint(context, amount, 4);
+ CGContextAddLineToPoint(context, amount, radius + 4);
+
+ CGContextMoveToPoint(context, 4, rect.size.height/2);
+ CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
+ CGContextAddLineToPoint(context, amount, rect.size.height - 4);
+ CGContextAddLineToPoint(context, amount, radius + 4);
+
+ CGContextFillPath(context);
+ }
+
+ // Progress in the right arc
+ else if (amount > radius + 4) {
+ CGFloat x = amount - (rect.size.width - radius - 4);
+
+ CGContextMoveToPoint(context, 4, rect.size.height/2);
+ CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
+ CGContextAddLineToPoint(context, rect.size.width - radius - 4, 4);
+ CGFloat angle = -acos(x/radius);
+ if (isnan(angle)) angle = 0;
+ CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, M_PI, angle, 0);
+ CGContextAddLineToPoint(context, amount, rect.size.height/2);
+
+ CGContextMoveToPoint(context, 4, rect.size.height/2);
+ CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
+ CGContextAddLineToPoint(context, rect.size.width - radius - 4, rect.size.height - 4);
+ angle = acos(x/radius);
+ if (isnan(angle)) angle = 0;
+ CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, -M_PI, angle, 1);
+ CGContextAddLineToPoint(context, amount, rect.size.height/2);
+
+ CGContextFillPath(context);
+ }
+
+ // Progress is in the left arc
+ else if (amount < radius + 4 && amount > 0) {
+ CGContextMoveToPoint(context, 4, rect.size.height/2);
+ CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
+ CGContextAddLineToPoint(context, radius + 4, rect.size.height/2);
+
+ CGContextMoveToPoint(context, 4, rect.size.height/2);
+ CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
+ CGContextAddLineToPoint(context, radius + 4, rect.size.height/2);
+
+ CGContextFillPath(context);
+ }
+}
+
+@end
+
+
+@interface MBBackgroundView ()
+
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV
+@property UIVisualEffectView *effectView;
+#endif
+#if !TARGET_OS_TV
+@property UIToolbar *toolbar;
+#endif
+
+@end
+
+
+@implementation MBBackgroundView
+
+#pragma mark - Lifecycle
+
+- (instancetype)initWithFrame:(CGRect)frame {
+ if ((self = [super initWithFrame:frame])) {
+ if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_7_0) {
+ _style = MBProgressHUDBackgroundStyleBlur;
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV
+ _blurEffectStyle = UIBlurEffectStyleLight;
+#endif
+ if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) {
+ _color = [UIColor colorWithWhite:0.8f alpha:0.6f];
+ } else {
+ _color = [UIColor colorWithWhite:0.95f alpha:0.6f];
+ }
+ } else {
+ _style = MBProgressHUDBackgroundStyleSolidColor;
+ _color = [[UIColor blackColor] colorWithAlphaComponent:0.8];
+ }
+
+ self.clipsToBounds = YES;
+
+ [self updateForBackgroundStyle];
+ }
+ return self;
+}
+
+#pragma mark - Layout
+
+- (CGSize)intrinsicContentSize {
+ // Smallest size possible. Content pushes against this.
+ return CGSizeZero;
+}
+
+#pragma mark - Appearance
+
+- (void)setStyle:(MBProgressHUDBackgroundStyle)style {
+ if (style == MBProgressHUDBackgroundStyleBlur && kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0) {
+ style = MBProgressHUDBackgroundStyleSolidColor;
+ }
+ if (_style != style) {
+ _style = style;
+ [self updateForBackgroundStyle];
+ }
+}
+
+- (void)setColor:(UIColor *)color {
+ NSAssert(color, @"The color should not be nil.");
+ if (color != _color && ![color isEqual:_color]) {
+ _color = color;
+ [self updateViewsForColor:color];
+ }
+}
+
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV
+
+- (void)setBlurEffectStyle:(UIBlurEffectStyle)blurEffectStyle {
+ if (_blurEffectStyle == blurEffectStyle) {
+ return;
+ }
+
+ _blurEffectStyle = blurEffectStyle;
+
+ [self updateForBackgroundStyle];
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark - Views
+
+- (void)updateForBackgroundStyle {
+ MBProgressHUDBackgroundStyle style = self.style;
+ if (style == MBProgressHUDBackgroundStyleBlur) {
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV
+ if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) {
+ UIBlurEffect *effect = [UIBlurEffect effectWithStyle:self.blurEffectStyle];
+ UIVisualEffectView *effectView = [[UIVisualEffectView alloc] initWithEffect:effect];
+ [self addSubview:effectView];
+ effectView.frame = self.bounds;
+ effectView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
+ self.backgroundColor = self.color;
+ self.layer.allowsGroupOpacity = NO;
+ self.effectView = effectView;
+ } else {
+#endif
+#if !TARGET_OS_TV
+ UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectInset(self.bounds, -100.f, -100.f)];
+ toolbar.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
+ toolbar.barTintColor = self.color;
+ toolbar.translucent = YES;
+ [self addSubview:toolbar];
+ self.toolbar = toolbar;
+#endif
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV
+ }
+#endif
+ } else {
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV
+ if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) {
+ [self.effectView removeFromSuperview];
+ self.effectView = nil;
+ } else {
+#endif
+#if !TARGET_OS_TV
+ [self.toolbar removeFromSuperview];
+ self.toolbar = nil;
+#endif
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV
+ }
+#endif
+ self.backgroundColor = self.color;
+ }
+}
+
+- (void)updateViewsForColor:(UIColor *)color {
+ if (self.style == MBProgressHUDBackgroundStyleBlur) {
+ if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) {
+ self.backgroundColor = self.color;
+ } else {
+#if !TARGET_OS_TV
+ self.toolbar.barTintColor = color;
+#endif
+ }
+ } else {
+ self.backgroundColor = self.color;
+ }
+}
+
+@end
+
+
+@implementation MBProgressHUD (Deprecated)
+
+#pragma mark - Class
+
++ (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated {
+ NSArray *huds = [MBProgressHUD allHUDsForView:view];
+ for (MBProgressHUD *hud in huds) {
+ hud.removeFromSuperViewOnHide = YES;
+ [hud hideAnimated:animated];
+ }
+ return [huds count];
+}
+
++ (NSArray *)allHUDsForView:(UIView *)view {
+ NSMutableArray *huds = [NSMutableArray array];
+ NSArray *subviews = view.subviews;
+ for (UIView *aView in subviews) {
+ if ([aView isKindOfClass:self]) {
+ [huds addObject:aView];
+ }
+ }
+ return [NSArray arrayWithArray:huds];
+}
+
+#pragma mark - Lifecycle
+
+- (id)initWithWindow:(UIWindow *)window {
+ return [self initWithView:window];
+}
+
+#pragma mark - Show & hide
+
+- (void)show:(BOOL)animated {
+ [self showAnimated:animated];
+}
+
+- (void)hide:(BOOL)animated {
+ [self hideAnimated:animated];
+}
+
+- (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay {
+ [self hideAnimated:animated afterDelay:delay];
+}
+
+#pragma mark - Threading
+
+- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated {
+ [self showAnimated:animated whileExecutingBlock:^{
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+ // Start executing the requested task
+ [target performSelector:method withObject:object];
+#pragma clang diagnostic pop
+ }];
+}
+
+- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block {
+ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+ [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:NULL];
+}
+
+- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(void (^)(void))completion {
+ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+ [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:completion];
+}
+
+- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue {
+ [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:NULL];
+}
+
+- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue completionBlock:(nullable MBProgressHUDCompletionBlock)completion {
+ self.taskInProgress = YES;
+ self.completionBlock = completion;
+ dispatch_async(queue, ^(void) {
+ block();
+ dispatch_async(dispatch_get_main_queue(), ^(void) {
+ [self cleanUp];
+ });
+ });
+ [self showAnimated:animated];
+}
+
+- (void)cleanUp {
+ self.taskInProgress = NO;
+ [self hideAnimated:self.useAnimation];
+}
+
+#pragma mark - Labels
+
+- (NSString *)labelText {
+ return self.label.text;
+}
+
+- (void)setLabelText:(NSString *)labelText {
+ MBMainThreadAssert();
+ self.label.text = labelText;
+}
+
+- (UIFont *)labelFont {
+ return self.label.font;
+}
+
+- (void)setLabelFont:(UIFont *)labelFont {
+ MBMainThreadAssert();
+ self.label.font = labelFont;
+}
+
+- (UIColor *)labelColor {
+ return self.label.textColor;
+}
+
+- (void)setLabelColor:(UIColor *)labelColor {
+ MBMainThreadAssert();
+ self.label.textColor = labelColor;
+}
+
+- (NSString *)detailsLabelText {
+ return self.detailsLabel.text;
+}
+
+- (void)setDetailsLabelText:(NSString *)detailsLabelText {
+ MBMainThreadAssert();
+ self.detailsLabel.text = detailsLabelText;
+}
+
+- (UIFont *)detailsLabelFont {
+ return self.detailsLabel.font;
+}
+
+- (void)setDetailsLabelFont:(UIFont *)detailsLabelFont {
+ MBMainThreadAssert();
+ self.detailsLabel.font = detailsLabelFont;
+}
+
+- (UIColor *)detailsLabelColor {
+ return self.detailsLabel.textColor;
+}
+
+- (void)setDetailsLabelColor:(UIColor *)detailsLabelColor {
+ MBMainThreadAssert();
+ self.detailsLabel.textColor = detailsLabelColor;
+}
+
+- (CGFloat)opacity {
+ return _opacity;
+}
+
+- (void)setOpacity:(CGFloat)opacity {
+ MBMainThreadAssert();
+ _opacity = opacity;
+}
+
+- (UIColor *)color {
+ return self.bezelView.color;
+}
+
+- (void)setColor:(UIColor *)color {
+ MBMainThreadAssert();
+ self.bezelView.color = color;
+}
+
+- (CGFloat)yOffset {
+ return self.offset.y;
+}
+
+- (void)setYOffset:(CGFloat)yOffset {
+ MBMainThreadAssert();
+ self.offset = CGPointMake(self.offset.x, yOffset);
+}
+
+- (CGFloat)xOffset {
+ return self.offset.x;
+}
+
+- (void)setXOffset:(CGFloat)xOffset {
+ MBMainThreadAssert();
+ self.offset = CGPointMake(xOffset, self.offset.y);
+}
+
+- (CGFloat)cornerRadius {
+ return self.bezelView.layer.cornerRadius;
+}
+
+- (void)setCornerRadius:(CGFloat)cornerRadius {
+ MBMainThreadAssert();
+ self.bezelView.layer.cornerRadius = cornerRadius;
+}
+
+- (BOOL)dimBackground {
+ MBBackgroundView *backgroundView = self.backgroundView;
+ UIColor *dimmedColor = [UIColor colorWithWhite:0.f alpha:.2f];
+ return backgroundView.style == MBProgressHUDBackgroundStyleSolidColor && [backgroundView.color isEqual:dimmedColor];
+}
+
+- (void)setDimBackground:(BOOL)dimBackground {
+ MBMainThreadAssert();
+ self.backgroundView.style = MBProgressHUDBackgroundStyleSolidColor;
+ self.backgroundView.color = dimBackground ? [UIColor colorWithWhite:0.f alpha:.2f] : [UIColor clearColor];
+}
+
+- (CGSize)size {
+ return self.bezelView.frame.size;
+}
+
+- (UIColor *)activityIndicatorColor {
+ return _activityIndicatorColor;
+}
+
+- (void)setActivityIndicatorColor:(UIColor *)activityIndicatorColor {
+ if (activityIndicatorColor != _activityIndicatorColor) {
+ _activityIndicatorColor = activityIndicatorColor;
+ UIActivityIndicatorView *indicator = (UIActivityIndicatorView *)self.indicator;
+ if ([indicator isKindOfClass:[UIActivityIndicatorView class]]) {
+ [indicator setColor:activityIndicatorColor];
+ }
+ }
+}
+
+@end
+
+@implementation MBProgressHUDRoundedButton
+
+#pragma mark - Lifecycle
+
+- (instancetype)initWithFrame:(CGRect)frame {
+ self = [super initWithFrame:frame];
+ if (self) {
+ CALayer *layer = self.layer;
+ layer.borderWidth = 1.f;
+ }
+ return self;
+}
+
+#pragma mark - Layout
+
+- (void)layoutSubviews {
+ [super layoutSubviews];
+ // Fully rounded corners
+ CGFloat height = CGRectGetHeight(self.bounds);
+ self.layer.cornerRadius = ceil(height / 2.f);
+}
+
+- (CGSize)intrinsicContentSize {
+ // Only show if we have associated control events
+ if (self.allControlEvents == 0) return CGSizeZero;
+ CGSize size = [super intrinsicContentSize];
+ // Add some side padding
+ size.width += 20.f;
+ return size;
+}
+
+#pragma mark - Color
+
+- (void)setTitleColor:(UIColor *)color forState:(UIControlState)state {
+ [super setTitleColor:color forState:state];
+ // Update related colors
+ [self setHighlighted:self.highlighted];
+ self.layer.borderColor = color.CGColor;
+}
+
+- (void)setHighlighted:(BOOL)highlighted {
+ [super setHighlighted:highlighted];
+ UIColor *baseColor = [self titleColorForState:UIControlStateSelected];
+ self.backgroundColor = highlighted ? [baseColor colorWithAlphaComponent:0.1f] : [UIColor clearColor];
+}
+
+@end