| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020 |
- //
- // MBProgressHUD.m
- // Version 0.8
- // Created by Matej Bukovinski on 2.4.09.
- //
- #import "MBProgressHUD.h"
- #import <tgmath.h>
- #if __has_feature(objc_arc)
- #define MB_AUTORELEASE(exp) exp
- #define MB_RELEASE(exp) exp
- #define MB_RETAIN(exp) exp
- #else
- #define MB_AUTORELEASE(exp) [exp autorelease]
- #define MB_RELEASE(exp) [exp release]
- #define MB_RETAIN(exp) [exp retain]
- #endif
- #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 60000
- #define MBLabelAlignmentCenter NSTextAlignmentCenter
- #else
- #define MBLabelAlignmentCenter UITextAlignmentCenter
- #endif
- #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
- #define MB_TEXTSIZE(text, font) [text length] > 0 ? [text \
- sizeWithAttributes:@{NSFontAttributeName:font}] : CGSizeZero;
- #else
- #define MB_TEXTSIZE(text, font) [text length] > 0 ? [text sizeWithFont:font] : CGSizeZero;
- #endif
- #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
- #define MB_MULTILINE_TEXTSIZE(text, font, maxSize, mode) [text length] > 0 ? [text \
- boundingRectWithSize:maxSize options:(NSStringDrawingUsesLineFragmentOrigin) \
- attributes:@{NSFontAttributeName:font} context:nil].size : CGSizeZero;
- #else
- #define MB_MULTILINE_TEXTSIZE(text, font, maxSize, mode) [text length] > 0 ? [text \
- sizeWithFont:font constrainedToSize:maxSize lineBreakMode:mode] : CGSizeZero;
- #endif
- static const CGFloat kPadding = 4.f;
- static const CGFloat kLabelFontSize = 16.f;
- static const CGFloat kDetailsLabelFontSize = 12.f;
- @interface MBProgressHUD ()
- - (void)setupLabels;
- - (void)registerForKVO;
- - (void)unregisterFromKVO;
- - (NSArray *)observableKeypaths;
- - (void)registerForNotifications;
- - (void)unregisterFromNotifications;
- - (void)updateUIForKeypath:(NSString *)keyPath;
- - (void)hideUsingAnimation:(BOOL)animated;
- - (void)showUsingAnimation:(BOOL)animated;
- - (void)done;
- - (void)updateIndicators;
- - (void)handleGraceTimer:(NSTimer *)theTimer;
- - (void)handleMinShowTimer:(NSTimer *)theTimer;
- - (void)setTransformForCurrentOrientation:(BOOL)animated;
- - (void)cleanUp;
- - (void)launchExecution;
- - (void)deviceOrientationDidChange:(NSNotification *)notification;
- - (void)hideDelayed:(NSNumber *)animated;
- @property (atomic, MB_STRONG) UIView *indicator;
- @property (atomic, MB_STRONG) NSTimer *graceTimer;
- @property (atomic, MB_STRONG) NSTimer *minShowTimer;
- @property (atomic, MB_STRONG) NSDate *showStarted;
- @property (atomic, assign) CGSize size;
- @end
- @implementation MBProgressHUD {
-
- BOOL useAnimation;
- SEL methodForExecution;
- id targetForExecution;
- id objectForExecution;
- UILabel *label;
- UILabel *detailsLabel;
- BOOL isFinished;
- CGAffineTransform rotationTransform;
- }
- #pragma mark - Properties
- @synthesize animationType;
- @synthesize delegate;
- @synthesize opacity;
- @synthesize color;
- @synthesize labelFont;
- @synthesize labelColor;
- @synthesize detailsLabelFont;
- @synthesize detailsLabelColor;
- @synthesize indicator;
- @synthesize xOffset;
- @synthesize yOffset;
- @synthesize minSize;
- @synthesize square;
- @synthesize margin;
- @synthesize dimBackground;
- @synthesize graceTime;
- @synthesize minShowTime;
- @synthesize graceTimer;
- @synthesize minShowTimer;
- @synthesize taskInProgress;
- @synthesize removeFromSuperViewOnHide;
- @synthesize customView;
- @synthesize showStarted;
- @synthesize mode;
- @synthesize labelText;
- @synthesize detailsLabelText;
- @synthesize progress;
- @synthesize size;
- #if NS_BLOCKS_AVAILABLE
- @synthesize completionBlock;
- #endif
- #pragma mark - Class methods
- + (MB_INSTANCETYPE)showHUDAddedTo:(UIView *)view animated:(BOOL)animated {
- MBProgressHUD *hud = [[self alloc] initWithFrame:CGRectMake(0, 0,view.width,view.height)];
- [view addSubview:hud];
- [hud show:animated];
- return MB_AUTORELEASE(hud);
- }
- + (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated {
- MBProgressHUD *hud = [self HUDForView:view];
- if (hud != nil) {
- hud.removeFromSuperViewOnHide = YES;
- [hud hide:animated];
- return YES;
- }
- return NO;
- }
- + (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated {
- NSArray *huds = [MBProgressHUD allHUDsForView:view];
- for (MBProgressHUD *hud in huds) {
- hud.removeFromSuperViewOnHide = YES;
- [hud hide:animated];
- }
- return [huds count];
- }
- + (MB_INSTANCETYPE)HUDForView:(UIView *)view {
- NSEnumerator *subviewsEnum = [view.subviews reverseObjectEnumerator];
- for (UIView *subview in subviewsEnum) {
- if ([subview isKindOfClass:self]) {
- return (MBProgressHUD *)subview;
- }
- }
- return nil;
- }
- + (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)initWithFrame:(CGRect)frame {
- self = [super initWithFrame:frame];
- if (self) {
- // Set default values for properties
- self.animationType = MBProgressHUDAnimationFade;
- self.mode = MBProgressHUDModeIndeterminate;
- self.labelText = nil;
- self.detailsLabelText = nil;
- self.opacity = 0.8f;
- self.color = nil;
- self.labelFont = YBLBFont(kLabelFontSize);
- self.labelColor = [UIColor whiteColor];
- self.detailsLabelFont = YBLBFont(kDetailsLabelFontSize);
- self.detailsLabelColor = [UIColor whiteColor];
- self.xOffset = 0.0f;
- self.yOffset = 0.0f;
- self.dimBackground = NO;
- self.margin = 20.0f;
- self.cornerRadius = 10.0f;
- self.graceTime = 0.0f;
- self.minShowTime = 0.0f;
- self.removeFromSuperViewOnHide = NO;
- self.minSize = CGSizeZero;
- self.square = NO;
- self.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin
- | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
- // Transparent background
- self.opaque = NO;
- self.backgroundColor = [UIColor clearColor];
- // Make it invisible for now
- self.alpha = 0.0f;
-
- taskInProgress = NO;
- rotationTransform = CGAffineTransformIdentity;
-
- [self setupLabels];
- [self updateIndicators];
- [self registerForKVO];
- [self registerForNotifications];
- }
- return self;
- }
- - (id)initWithView:(UIView *)view {
- NSAssert(view, @"View must not be nil.");
- return [self initWithFrame:view.bounds];
- }
- - (id)initWithWindow:(UIWindow *)window {
- return [self initWithView:window];
- }
- - (void)dealloc {
- [self unregisterFromNotifications];
- [self unregisterFromKVO];
- #if !__has_feature(objc_arc)
- [color release];
- [indicator release];
- [label release];
- [detailsLabel release];
- [labelText release];
- [detailsLabelText release];
- [graceTimer release];
- [minShowTimer release];
- [showStarted release];
- [customView release];
- [labelFont release];
- [labelColor release];
- [detailsLabelFont release];
- [detailsLabelColor release];
- #if NS_BLOCKS_AVAILABLE
- [completionBlock release];
- #endif
- [super dealloc];
- #endif
- }
- #pragma mark - Show & hide
- - (void)show:(BOOL)animated {
- useAnimation = animated;
- // If the grace time is set postpone the HUD display
- if (self.graceTime > 0.0) {
- self.graceTimer = [NSTimer scheduledTimerWithTimeInterval:self.graceTime target:self
- selector:@selector(handleGraceTimer:) userInfo:nil repeats:NO];
- }
- // ... otherwise show the HUD imediately
- else {
- [self setNeedsDisplay];
- [self showUsingAnimation:useAnimation];
- }
- }
- - (void)hide:(BOOL)animated {
- useAnimation = animated;
- // If the minShow time is set, calculate how long the hud was shown,
- // and pospone the hiding operation if necessary
- if (self.minShowTime > 0.0 && showStarted) {
- NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:showStarted];
- if (interv < self.minShowTime) {
- self.minShowTimer = [NSTimer scheduledTimerWithTimeInterval:(self.minShowTime - interv) target:self
- selector:@selector(handleMinShowTimer:) userInfo:nil repeats:NO];
- return;
- }
- }
- // ... otherwise hide the HUD immediately
- [self hideUsingAnimation:useAnimation];
- }
- - (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay {
- [self performSelector:@selector(hideDelayed:) withObject:[NSNumber numberWithBool:animated] afterDelay:delay];
- }
- - (void)hideDelayed:(NSNumber *)animated {
- [self hide:[animated boolValue]];
- }
- #pragma mark - Timer callbacks
- - (void)handleGraceTimer:(NSTimer *)theTimer {
- // Show the HUD only if the task is still running
- if (taskInProgress) {
- [self setNeedsDisplay];
- [self showUsingAnimation:useAnimation];
- }
- }
- - (void)handleMinShowTimer:(NSTimer *)theTimer {
- [self hideUsingAnimation:useAnimation];
- }
- #pragma mark - View Hierrarchy
- - (void)didMoveToSuperview {
- // We need to take care of rotation ourselfs if we're adding the HUD to a window
- if ([self.superview isKindOfClass:[UIWindow class]]) {
- [self setTransformForCurrentOrientation:NO];
- }
- }
- #pragma mark - Internal show & hide operations
- - (void)showUsingAnimation:(BOOL)animated {
- if (animated && animationType == MBProgressHUDAnimationZoomIn) {
- self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(0.5f, 0.5f));
- } else if (animated && animationType == MBProgressHUDAnimationZoomOut) {
- self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(1.5f, 1.5f));
- }
- self.showStarted = [NSDate date];
- // Fade in
- if (animated) {
- [UIView beginAnimations:nil context:NULL];
- [UIView setAnimationDuration:0.30];
- self.alpha = 1.0f;
- if (animationType == MBProgressHUDAnimationZoomIn || animationType == MBProgressHUDAnimationZoomOut) {
- self.transform = rotationTransform;
- }
- [UIView commitAnimations];
- }
- else {
- self.alpha = 1.0f;
- }
- }
- - (void)hideUsingAnimation:(BOOL)animated {
- // Fade out
- if (animated && showStarted) {
- [UIView beginAnimations:nil context:NULL];
- [UIView setAnimationDuration:0.30];
- [UIView setAnimationDelegate:self];
- [UIView setAnimationDidStopSelector:@selector(animationFinished:finished:context:)];
- // 0.02 prevents the hud from passing through touches during the animation the hud will get completely hidden
- // in the done method
- if (animationType == MBProgressHUDAnimationZoomIn) {
- self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(1.5f, 1.5f));
- } else if (animationType == MBProgressHUDAnimationZoomOut) {
- self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(0.5f, 0.5f));
- }
- self.alpha = 0.02f;
- [UIView commitAnimations];
- }
- else {
- self.alpha = 0.0f;
- [self done];
- }
- self.showStarted = nil;
- }
- - (void)animationFinished:(NSString *)animationID finished:(BOOL)finished context:(void*)context {
- [self done];
- }
- - (void)done {
- [NSObject cancelPreviousPerformRequestsWithTarget:self];
- isFinished = YES;
- self.alpha = 0.0f;
- if (removeFromSuperViewOnHide) {
- [self removeFromSuperview];
- }
- #if NS_BLOCKS_AVAILABLE
- if (self.completionBlock) {
- self.completionBlock();
- self.completionBlock = NULL;
- }
- #endif
- if ([delegate respondsToSelector:@selector(hudWasHidden:)]) {
- [delegate performSelector:@selector(hudWasHidden:) withObject:self];
- }
- }
- #pragma mark - Threading
- - (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated {
- methodForExecution = method;
- targetForExecution = MB_RETAIN(target);
- objectForExecution = MB_RETAIN(object);
- // Launch execution in new thread
- self.taskInProgress = YES;
- [NSThread detachNewThreadSelector:@selector(launchExecution) toTarget:self withObject:nil];
- // Show HUD view
- [self show:animated];
- }
- #if NS_BLOCKS_AVAILABLE
- - (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:(MBProgressHUDCompletionBlock)completion {
- self.taskInProgress = YES;
- self.completionBlock = completion;
- dispatch_async(queue, ^(void) {
- block();
- dispatch_async(dispatch_get_main_queue(), ^(void) {
- [self cleanUp];
- });
- });
- [self show:animated];
- }
- #endif
- - (void)launchExecution {
- @autoreleasepool {
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
- // Start executing the requested task
- [targetForExecution performSelector:methodForExecution withObject:objectForExecution];
- #pragma clang diagnostic pop
- // Task completed, update view in main thread (note: view operations should
- // be done only in the main thread)
- [self performSelectorOnMainThread:@selector(cleanUp) withObject:nil waitUntilDone:NO];
- }
- }
- - (void)cleanUp {
- taskInProgress = NO;
- #if !__has_feature(objc_arc)
- [targetForExecution release];
- [objectForExecution release];
- #else
- targetForExecution = nil;
- objectForExecution = nil;
- #endif
- [self hide:useAnimation];
- }
- #pragma mark - UI
- - (void)setupLabels {
- label = [[UILabel alloc] initWithFrame:self.bounds];
- label.adjustsFontSizeToFitWidth = NO;
- label.textAlignment = MBLabelAlignmentCenter;
- label.opaque = NO;
- label.backgroundColor = [UIColor clearColor];
- label.textColor = self.labelColor;
- label.font = self.labelFont;
- label.text = self.labelText;
- [self addSubview:label];
-
- detailsLabel = [[UILabel alloc] initWithFrame:self.bounds];
- detailsLabel.font = self.detailsLabelFont;
- detailsLabel.adjustsFontSizeToFitWidth = NO;
- detailsLabel.textAlignment = MBLabelAlignmentCenter;
- detailsLabel.opaque = NO;
- detailsLabel.backgroundColor = [UIColor clearColor];
- detailsLabel.textColor = self.detailsLabelColor;
- detailsLabel.numberOfLines = 0;
- detailsLabel.font = self.detailsLabelFont;
- detailsLabel.text = self.detailsLabelText;
- [self addSubview:detailsLabel];
- }
- - (void)updateIndicators {
-
- BOOL isActivityIndicator = [indicator isKindOfClass:[UIActivityIndicatorView class]];
- BOOL isRoundIndicator = [indicator isKindOfClass:[MBRoundProgressView class]];
-
- if (mode == MBProgressHUDModeIndeterminate && !isActivityIndicator) {
- // Update to indeterminate indicator
- [indicator removeFromSuperview];
- self.indicator = MB_AUTORELEASE([[UIActivityIndicatorView alloc]
- initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]);
- [(UIActivityIndicatorView *)indicator startAnimating];
- [self addSubview:indicator];
- }
- else if (mode == MBProgressHUDModeDeterminateHorizontalBar) {
- // Update to bar determinate indicator
- [indicator removeFromSuperview];
- self.indicator = MB_AUTORELEASE([[MBBarProgressView alloc] init]);
- [self addSubview:indicator];
- }
- else if (mode == MBProgressHUDModeDeterminate || mode == MBProgressHUDModeAnnularDeterminate) {
- if (!isRoundIndicator) {
- // Update to determinante indicator
- [indicator removeFromSuperview];
- self.indicator = MB_AUTORELEASE([[MBRoundProgressView alloc] init]);
- [self addSubview:indicator];
- }
- if (mode == MBProgressHUDModeAnnularDeterminate) {
- [(MBRoundProgressView *)indicator setAnnular:YES];
- }
- }
- else if (mode == MBProgressHUDModeCustomView && customView != indicator) {
- // Update custom view indicator
- [indicator removeFromSuperview];
- self.indicator = customView;
- [self addSubview:indicator];
- } else if (mode == MBProgressHUDModeText) {
- [indicator removeFromSuperview];
- self.indicator = nil;
- }
- }
- #pragma mark - Layout
- - (void)layoutSubviews {
-
- // Entirely cover the parent view
- UIView *parent = self.superview;
- if (parent && [parent isKindOfClass:[UIWindow class]]) {
- self.frame = parent.bounds;
- // CGRectMake(0, iPhoneX?88:64, parent.bounds.size.width, parent.bounds.size.height-(iPhoneX?88:64));
- }
- CGRect bounds = self.bounds;
-
- // Determine the total widt and height needed
- CGFloat maxWidth = bounds.size.width - 4 * margin;
- CGSize totalSize = CGSizeZero;
-
- CGRect indicatorF = indicator.bounds;
- indicatorF.size.width = MIN(indicatorF.size.width, maxWidth);
- totalSize.width = MAX(totalSize.width, indicatorF.size.width);
- totalSize.height += indicatorF.size.height;
-
- CGSize labelSize = MB_TEXTSIZE(label.text, label.font);
- labelSize.width = MIN(labelSize.width, maxWidth);
- totalSize.width = MAX(totalSize.width, labelSize.width);
- totalSize.height += labelSize.height;
- if (labelSize.height > 0.f && indicatorF.size.height > 0.f) {
- totalSize.height += kPadding;
- }
- CGFloat remainingHeight = bounds.size.height - totalSize.height - kPadding - 4 * margin;
- CGSize maxSize = CGSizeMake(maxWidth, remainingHeight);
- CGSize detailsLabelSize = MB_MULTILINE_TEXTSIZE(detailsLabel.text, detailsLabel.font, maxSize, detailsLabel.lineBreakMode);
- totalSize.width = MAX(totalSize.width, detailsLabelSize.width);
- totalSize.height += detailsLabelSize.height;
- if (detailsLabelSize.height > 0.f && (indicatorF.size.height > 0.f || labelSize.height > 0.f)) {
- totalSize.height += kPadding;
- }
-
- totalSize.width += 2 * margin;
- totalSize.height += 2 * margin;
-
- // Position elements
- CGFloat yPos = round(((bounds.size.height - totalSize.height) / 2)) + margin + yOffset;
- CGFloat xPos = xOffset;
- indicatorF.origin.y = yPos;
- indicatorF.origin.x = round((bounds.size.width - indicatorF.size.width) / 2) + xPos;
- indicator.frame = indicatorF;
- yPos += indicatorF.size.height;
-
- if (labelSize.height > 0.f && indicatorF.size.height > 0.f) {
- yPos += kPadding;
- }
- CGRect labelF;
- labelF.origin.y = yPos;
- labelF.origin.x = round((bounds.size.width - labelSize.width) / 2) + xPos;
- labelF.size = labelSize;
- label.frame = labelF;
- yPos += labelF.size.height;
-
- if (detailsLabelSize.height > 0.f && (indicatorF.size.height > 0.f || labelSize.height > 0.f)) {
- yPos += kPadding;
- }
- CGRect detailsLabelF;
- detailsLabelF.origin.y = yPos;
- detailsLabelF.origin.x = round((bounds.size.width - detailsLabelSize.width) / 2) + xPos;
- detailsLabelF.size = detailsLabelSize;
- detailsLabel.frame = detailsLabelF;
-
- // Enforce minsize and quare rules
- if (square) {
- CGFloat max = MAX(totalSize.width, totalSize.height);
- if (max <= bounds.size.width - 2 * margin) {
- totalSize.width = max;
- }
- if (max <= bounds.size.height - 2 * margin) {
- totalSize.height = max;
- }
- }
- if (totalSize.width < minSize.width) {
- totalSize.width = minSize.width;
- }
- if (totalSize.height < minSize.height) {
- totalSize.height = minSize.height;
- }
-
- self.size = totalSize;
- }
- #pragma mark BG Drawing
- - (void)drawRect:(CGRect)rect {
-
- CGContextRef context = UIGraphicsGetCurrentContext();
- UIGraphicsPushContext(context);
- if (self.dimBackground) {
- //Gradient colours
- size_t gradLocationsNum = 2;
- CGFloat gradLocations[2] = {0.0f, 1.0f};
- CGFloat gradColors[8] = {0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.75f};
- CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
- CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradColors, gradLocations, gradLocationsNum);
- CGColorSpaceRelease(colorSpace);
- //Gradient center
- CGPoint gradCenter= CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
- //Gradient radius
- float gradRadius = MIN(self.bounds.size.width , self.bounds.size.height) ;
- //Gradient draw
- CGContextDrawRadialGradient (context, gradient, gradCenter,
- 0, gradCenter, gradRadius,
- kCGGradientDrawsAfterEndLocation);
- CGGradientRelease(gradient);
- }
- // Set background rect color
- if (self.color) {
- CGContextSetFillColorWithColor(context, self.color.CGColor);
- } else {
- CGContextSetGrayFillColor(context, 0.0f, self.opacity);
- }
-
- // Center HUD
- CGRect allRect = self.bounds;
- // Draw rounded HUD backgroud rect
- CGRect boxRect = CGRectMake(round((allRect.size.width - size.width) / 2) + self.xOffset,
- round((allRect.size.height - size.height) / 2) + self.yOffset, size.width, size.height);
- float radius = self.cornerRadius;
- CGContextBeginPath(context);
- CGContextMoveToPoint(context, CGRectGetMinX(boxRect) + radius, CGRectGetMinY(boxRect));
- CGContextAddArc(context, CGRectGetMaxX(boxRect) - radius, CGRectGetMinY(boxRect) + radius, radius, 3 * (float)M_PI / 2, 0, 0);
- CGContextAddArc(context, CGRectGetMaxX(boxRect) - radius, CGRectGetMaxY(boxRect) - radius, radius, 0, (float)M_PI / 2, 0);
- CGContextAddArc(context, CGRectGetMinX(boxRect) + radius, CGRectGetMaxY(boxRect) - radius, radius, (float)M_PI / 2, (float)M_PI, 0);
- CGContextAddArc(context, CGRectGetMinX(boxRect) + radius, CGRectGetMinY(boxRect) + radius, radius, (float)M_PI, 3 * (float)M_PI / 2, 0);
- CGContextClosePath(context);
- CGContextFillPath(context);
- UIGraphicsPopContext();
- }
- #pragma mark - KVO
- - (void)registerForKVO {
- for (NSString *keyPath in [self observableKeypaths]) {
- [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL];
- }
- }
- - (void)unregisterFromKVO {
- for (NSString *keyPath in [self observableKeypaths]) {
- [self removeObserver:self forKeyPath:keyPath];
- }
- }
- - (NSArray *)observableKeypaths {
- return [NSArray arrayWithObjects:@"mode", @"customView", @"labelText", @"labelFont", @"labelColor",
- @"detailsLabelText", @"detailsLabelFont", @"detailsLabelColor", @"progress", nil];
- }
- - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
- if (![NSThread isMainThread]) {
- [self performSelectorOnMainThread:@selector(updateUIForKeypath:) withObject:keyPath waitUntilDone:NO];
- } else {
- [self updateUIForKeypath:keyPath];
- }
- }
- - (void)updateUIForKeypath:(NSString *)keyPath {
- if ([keyPath isEqualToString:@"mode"] || [keyPath isEqualToString:@"customView"]) {
- [self updateIndicators];
- } else if ([keyPath isEqualToString:@"labelText"]) {
- label.text = self.labelText;
- } else if ([keyPath isEqualToString:@"labelFont"]) {
- label.font = self.labelFont;
- } else if ([keyPath isEqualToString:@"labelColor"]) {
- label.textColor = self.labelColor;
- } else if ([keyPath isEqualToString:@"detailsLabelText"]) {
- detailsLabel.text = self.detailsLabelText;
- } else if ([keyPath isEqualToString:@"detailsLabelFont"]) {
- detailsLabel.font = self.detailsLabelFont;
- } else if ([keyPath isEqualToString:@"detailsLabelColor"]) {
- detailsLabel.textColor = self.detailsLabelColor;
- } else if ([keyPath isEqualToString:@"progress"]) {
- if ([indicator respondsToSelector:@selector(setProgress:)]) {
- [(id)indicator setValue:@(progress) forKey:@"progress"];
- }
- return;
- }
- [self setNeedsLayout];
- [self setNeedsDisplay];
- }
- #pragma mark - Notifications
- - (void)registerForNotifications {
- NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
- [nc addObserver:self selector:@selector(deviceOrientationDidChange:)
- name:UIDeviceOrientationDidChangeNotification object:nil];
- }
- - (void)unregisterFromNotifications {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- }
- - (void)deviceOrientationDidChange:(NSNotification *)notification {
- UIView *superview = self.superview;
- if (!superview) {
- return;
- } else if ([superview isKindOfClass:[UIWindow class]]) {
- [self setTransformForCurrentOrientation:YES];
- } else {
- self.frame = self.superview.bounds;
- [self setNeedsDisplay];
- }
- }
- - (void)setTransformForCurrentOrientation:(BOOL)animated {
- // Stay in sync with the superview
- if (self.superview) {
- self.bounds = self.superview.bounds;
- [self setNeedsDisplay];
- }
-
- UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
- CGFloat radians = 0;
- if (UIInterfaceOrientationIsLandscape(orientation)) {
- if (orientation == UIInterfaceOrientationLandscapeLeft) { radians = -(CGFloat)M_PI_2; }
- else { radians = (CGFloat)M_PI_2; }
- // Window coordinates differ!
- self.bounds = CGRectMake(0, 0, self.bounds.size.height, self.bounds.size.width);
- } else {
- if (orientation == UIInterfaceOrientationPortraitUpsideDown) { radians = (CGFloat)M_PI; }
- else { radians = 0; }
- }
- rotationTransform = CGAffineTransformMakeRotation(radians);
-
- if (animated) {
- [UIView beginAnimations:nil context:nil];
- }
- [self setTransform:rotationTransform];
- if (animated) {
- [UIView commitAnimations];
- }
- }
- @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];
- [self registerForKVO];
- }
- return self;
- }
- - (void)dealloc {
- [self unregisterFromKVO];
- #if !__has_feature(objc_arc)
- [_progressTintColor release];
- [_backgroundTintColor release];
- [super dealloc];
- #endif
- }
- #pragma mark - Drawing
- - (void)drawRect:(CGRect)rect {
-
- CGRect allRect = self.bounds;
- CGRect circleRect = CGRectInset(allRect, 2.0f, 2.0f);
- CGContextRef context = UIGraphicsGetCurrentContext();
-
- if (_annular) {
- // Draw background
- CGFloat lineWidth = 5.f;
- UIBezierPath *processBackgroundPath = [UIBezierPath bezierPath];
- processBackgroundPath.lineWidth = lineWidth;
- processBackgroundPath.lineCapStyle = kCGLineCapRound;
- CGPoint center = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
- 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 = kCGLineCapRound;
- 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
- [_progressTintColor setStroke];
- [_backgroundTintColor setFill];
- CGContextSetLineWidth(context, 2.0f);
- CGContextFillEllipseInRect(context, circleRect);
- CGContextStrokeEllipseInRect(context, circleRect);
- // Draw progress
- CGPoint center = CGPointMake(allRect.size.width / 2, allRect.size.height / 2);
- CGFloat radius = (allRect.size.width - 4) / 2;
- CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees
- CGFloat endAngle = (self.progress * 2 * (float)M_PI) + startAngle;
- CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 1.0f); // white
- CGContextMoveToPoint(context, center.x, center.y);
- CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, 0);
- CGContextClosePath(context);
- CGContextFillPath(context);
- }
- }
- #pragma mark - KVO
- - (void)registerForKVO {
- for (NSString *keyPath in [self observableKeypaths]) {
- [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL];
- }
- }
- - (void)unregisterFromKVO {
- for (NSString *keyPath in [self observableKeypaths]) {
- [self removeObserver:self forKeyPath:keyPath];
- }
- }
- - (NSArray *)observableKeypaths {
- return [NSArray arrayWithObjects:@"progressTintColor", @"backgroundTintColor", @"progress", @"annular", nil];
- }
- - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
- [self setNeedsDisplay];
- }
- @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;
- [self registerForKVO];
- }
- return self;
- }
- - (void)dealloc {
- [self unregisterFromKVO];
- #if !__has_feature(objc_arc)
- [_lineColor release];
- [_progressColor release];
- [_progressRemainingColor release];
- [super dealloc];
- #endif
- }
- #pragma mark - Drawing
- - (void)drawRect:(CGRect)rect {
-
-
- CGContextRef context = UIGraphicsGetCurrentContext();
-
- CGContextSetLineWidth(context, 2);
- CGContextSetStrokeColorWithColor(context,[_lineColor CGColor]);
- CGContextSetFillColorWithColor(context, [_progressRemainingColor CGColor]);
-
- // Draw background
- float 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;
- float 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) {
- float 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);
- float 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);
- }
- }
- #pragma mark - KVO
- - (void)registerForKVO {
- for (NSString *keyPath in [self observableKeypaths]) {
- [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL];
- }
- }
- - (void)unregisterFromKVO {
- for (NSString *keyPath in [self observableKeypaths]) {
- [self removeObserver:self forKeyPath:keyPath];
- }
- }
- - (NSArray *)observableKeypaths {
- return [NSArray arrayWithObjects:@"lineColor", @"progressRemainingColor", @"progressColor", @"progress", nil];
- }
- - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
- [self setNeedsDisplay];
- }
- @end
|