12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496 |
- //
- // 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
|