1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489 |
- //
- // MBProgressHUD.m
- // Version 1.0.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 {
- static MBProgressHUD*hud;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- 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]) {
- return (MBProgressHUD *)subview;
- }
- }
- 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 {
- 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 overlaping 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
- CGFloat radius = (rect.size.height / 2) - 2;
- CGContextMoveToPoint(context, 2, rect.size.height/2);
- CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius);
- CGContextAddLineToPoint(context, rect.size.width - radius - 2, 2);
- 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);
- CGContextAddLineToPoint(context, radius + 2, rect.size.height - 2);
- CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius);
- CGContextFillPath(context);
-
- // Draw border
- CGContextMoveToPoint(context, 2, rect.size.height/2);
- CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius);
- CGContextAddLineToPoint(context, rect.size.width - radius - 2, 2);
- 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);
- CGContextAddLineToPoint(context, radius + 2, rect.size.height - 2);
- CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius);
- CGContextStrokePath(context);
-
- 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 (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];
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- #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:UIBlurEffectStyleLight];
- 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 (^)())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
|