对MBProgressHUD进行二次封装并精简使用
对MBProgressHUD进行二次封装并精简使用
https://github.com/jdg/MBProgressHUD
几个效果图:
以下源码是MBProgressHUD支持最新的iOS8的版本,没有任何的警告信息
MBProgressHUD.h 与 MBProgressHUD.m
// // MBProgressHUD.h // Version 0.9 // Created by Matej Bukovinski on 2.4.09. // // This code is distributed under the terms and conditions of the MIT license. // Copyright (c) 2013 Matej Bukovinski // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #import <CoreGraphics/CoreGraphics.h> @protocol MBProgressHUDDelegate; typedef enum { /** Progress is shown using an UIActivityIndicatorView. This is the default. */ MBProgressHUDModeIndeterminate, /** Progress is shown using a round, pie-chart like, progress view. */ MBProgressHUDModeDeterminate, /** Progress is shown using a horizontal progress bar */ MBProgressHUDModeDeterminateHorizontalBar, /** Progress is shown using a ring-shaped progress view. */ MBProgressHUDModeAnnularDeterminate, /** Shows a custom view */ MBProgressHUDModeCustomView, /** Shows only labels */ MBProgressHUDModeText } MBProgressHUDMode; typedef enum { /** Opacity animation */ MBProgressHUDAnimationFade, /** Opacity + scale animation */ MBProgressHUDAnimationZoom, MBProgressHUDAnimationZoomOut = MBProgressHUDAnimationZoom, MBProgressHUDAnimationZoomIn } MBProgressHUDAnimation; #ifndef MB_INSTANCETYPE #if __has_feature(objc_instancetype) #define MB_INSTANCETYPE instancetype #else #define MB_INSTANCETYPE id #endif #endif #ifndef MB_STRONG #if __has_feature(objc_arc) #define MB_STRONG strong #else #define MB_STRONG retain #endif #endif #ifndef MB_WEAK #if __has_feature(objc_arc_weak) #define MB_WEAK weak #elif __has_feature(objc_arc) #define MB_WEAK unsafe_unretained #else #define MB_WEAK assign #endif #endif #if NS_BLOCKS_AVAILABLE typedef void (^MBProgressHUDCompletionBlock)(); #endif /** * Displays a simple HUD window containing a progress indicator and two optional labels for short messages. * * This is a simple drop-in class for displaying a progress HUD view similar to Apple's private UIProgressHUD class. * The MBProgressHUD window spans over the entire space given to it by the initWithFrame constructor and catches all * user input on this region, thereby preventing the user operations on components below the view. The HUD itself is * drawn centered as a rounded semi-transparent view which resizes depending on the user specified content. * * This view supports four modes of operation: * - MBProgressHUDModeIndeterminate - shows a UIActivityIndicatorView * - MBProgressHUDModeDeterminate - shows a custom round progress indicator * - MBProgressHUDModeAnnularDeterminate - shows a custom annular progress indicator * - MBProgressHUDModeCustomView - shows an arbitrary, user specified view (@see customView) * * All three modes can have optional labels assigned: * - If the labelText property is set and non-empty then a label containing the provided content is placed below the * indicator view. * - If also the detailsLabelText property is set then another label is placed below the first label. */ @interface MBProgressHUD : UIView /** * Creates a new HUD, adds it to provided view and shows it. The counterpart to this method is hideHUDForView:animated:. * * @param view The view that the HUD will be added to * @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use * animations while appearing. * @return A reference to the created HUD. * * @see hideHUDForView:animated: * @see animationType */ + (MB_INSTANCETYPE)showHUDAddedTo:(UIView *)view animated:(BOOL)animated; /** * Finds the top-most HUD subview and hides it. The counterpart to this method is showHUDAddedTo:animated:. * * @param view The view that is going to be searched for a HUD subview. * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use * animations while disappearing. * @return YES if a HUD was found and removed, NO otherwise. * * @see showHUDAddedTo:animated: * @see animationType */ + (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated; /** * Finds all the HUD subviews and hides them. * * @param view The view that is going to be searched for HUD subviews. * @param animated If set to YES the HUDs will disappear using the current animationType. If set to NO the HUDs will not use * animations while disappearing. * @return the number of HUDs found and removed. * * @see hideHUDForView:animated: * @see animationType */ + (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated; /** * Finds the top-most HUD subview and returns it. * * @param view The view that is going to be searched. * @return A reference to the last HUD subview discovered. */ + (MB_INSTANCETYPE)HUDForView:(UIView *)view; /** * Finds all HUD subviews and returns them. * * @param view The view that is going to be searched. * @return All found HUD views (array of MBProgressHUD objects). */ + (NSArray *)allHUDsForView:(UIView *)view; /** * A convenience constructor that initializes the HUD with the window's bounds. Calls the designated constructor with * window.bounds as the parameter. * * @param window The window instance that will provide the bounds for the HUD. Should be the same instance as * the HUD's superview (i.e., the window that the HUD will be added to). */ - (id)initWithWindow:(UIWindow *)window; /** * A convenience constructor that initializes the HUD with the view's bounds. Calls the designated constructor with * view.bounds as the parameter * * @param view The view instance that will provide the bounds for the HUD. Should be the same instance as * the HUD's superview (i.e., the view that the HUD will be added to). */ - (id)initWithView:(UIView *)view; /** * Display the HUD. You need to make sure that the main thread completes its run loop soon after this method call so * the user interface can be updated. Call this method when your task is already set-up to be executed in a new thread * (e.g., when using something like NSOperation or calling an asynchronous call like NSURLRequest). * * @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use * animations while appearing. * * @see animationType */ - (void)show:(BOOL)animated; /** * Hide the HUD. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to * hide the HUD when your task completes. * * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use * animations while disappearing. * * @see animationType */ - (void)hide:(BOOL)animated; /** * Hide the HUD after a delay. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to * hide the HUD when your task completes. * * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use * animations while disappearing. * @param delay Delay in seconds until the HUD is hidden. * * @see animationType */ - (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay; /** * Shows the HUD while a background task is executing in a new thread, then hides the HUD. * * This method also takes care of autorelease pools so your method does not have to be concerned with setting up a * pool. * * @param method The method to be executed while the HUD is shown. This method will be executed in a new thread. * @param target The object that the target method belongs to. * @param object An optional object to be passed to the method. * @param animated If set to YES the HUD will (dis)appear using the current animationType. If set to NO the HUD will not use * animations while (dis)appearing. */ - (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated; #if NS_BLOCKS_AVAILABLE /** * Shows the HUD while a block is executing on a background queue, then hides the HUD. * * @see showAnimated:whileExecutingBlock:onQueue:completionBlock: */ - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block; /** * Shows the HUD while a block is executing on a background queue, then hides the HUD. * * @see showAnimated:whileExecutingBlock:onQueue:completionBlock: */ - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(MBProgressHUDCompletionBlock)completion; /** * Shows the HUD while a block is executing on the specified dispatch queue, then hides the HUD. * * @see showAnimated:whileExecutingBlock:onQueue:completionBlock: */ - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue; /** * Shows the HUD while a block is executing on the specified dispatch queue, executes completion block on the main queue, and then hides the HUD. * * @param animated If set to YES the HUD will (dis)appear using the current animationType. If set to NO the HUD will * not use animations while (dis)appearing. * @param block The block to be executed while the HUD is shown. * @param queue The dispatch queue on which the block should be executed. * @param completion The block to be executed on completion. * * @see completionBlock */ - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue completionBlock:(MBProgressHUDCompletionBlock)completion; /** * A block that gets called after the HUD was completely hidden. */ @property (copy) MBProgressHUDCompletionBlock completionBlock; #endif /** * MBProgressHUD operation mode. The default is MBProgressHUDModeIndeterminate. * * @see MBProgressHUDMode */ @property (assign) MBProgressHUDMode mode; /** * The animation type that should be used when the HUD is shown and hidden. * * @see MBProgressHUDAnimation */ @property (assign) MBProgressHUDAnimation animationType; /** * The UIView (e.g., a UIImageView) to be shown when the HUD is in MBProgressHUDModeCustomView. * For best results use a 37 by 37 pixel view (so the bounds match the built in indicator bounds). */ @property (MB_STRONG) UIView *customView; /** * The HUD delegate object. * * @see MBProgressHUDDelegate */ @property (MB_WEAK) id<MBProgressHUDDelegate> delegate; /** * An optional short message to be displayed below the activity indicator. The HUD is automatically resized to fit * the entire text. If the text is too long it will get clipped by displaying "..." at the end. If left unchanged or * set to @"", then no message is displayed. */ @property (copy) NSString *labelText; /** * An optional details message displayed below the labelText message. This message is displayed only if the labelText * property is also set and is different from an empty string (@""). The details text can span multiple lines. */ @property (copy) NSString *detailsLabelText; /** * The opacity of the HUD window. Defaults to 0.8 (80% opacity). */ @property (assign) float opacity; /** * The color of the HUD window. Defaults to black. If this property is set, color is set using * this UIColor and the opacity property is not used. using retain because performing copy on * UIColor base colors (like [UIColor greenColor]) cause problems with the copyZone. */ @property (MB_STRONG) UIColor *color; /** * The x-axis offset of the HUD relative to the centre of the superview. */ @property (assign) float xOffset; /** * The y-axis offset of the HUD relative to the centre of the superview. */ @property (assign) float yOffset; /** * The amount of space between the HUD edge and the HUD elements (labels, indicators or custom views). * Defaults to 20.0 */ @property (assign) float margin; /** * The corner radius for the HUD * Defaults to 10.0 */ @property (assign) float cornerRadius; /** * Cover the HUD background view with a radial gradient. */ @property (assign) BOOL dimBackground; /* * Grace period is the time (in seconds) that the invoked method may be run without * showing the HUD. If the task finishes before the grace time runs out, the HUD will * not be shown at all. * This may be used to prevent HUD display for very short tasks. * Defaults to 0 (no grace time). * Grace time functionality is only supported when the task status is known! * @see taskInProgress */ @property (assign) float graceTime; /** * The minimum time (in seconds) that the HUD is shown. * This avoids the problem of the HUD being shown and than instantly hidden. * Defaults to 0 (no minimum show time). */ @property (assign) float minShowTime; /** * Indicates that the executed operation is in progress. Needed for correct graceTime operation. * If you don't set a graceTime (different than 0.0) this does nothing. * This property is automatically set when using showWhileExecuting:onTarget:withObject:animated:. * When threading is done outside of the HUD (i.e., when the show: and hide: methods are used directly), * you need to set this property when your task starts and completes in order to have normal graceTime * functionality. */ @property (assign) BOOL taskInProgress; /** * Removes the HUD from its parent view when hidden. * Defaults to NO. */ @property (assign) BOOL removeFromSuperViewOnHide; /** * Font to be used for the main label. Set this property if the default is not adequate. */ @property (MB_STRONG) UIFont* labelFont; /** * Color to be used for the main label. Set this property if the default is not adequate. */ @property (MB_STRONG) UIColor* labelColor; /** * Font to be used for the details label. Set this property if the default is not adequate. */ @property (MB_STRONG) UIFont* detailsLabelFont; /** * Color to be used for the details label. Set this property if the default is not adequate. */ @property (MB_STRONG) UIColor* detailsLabelColor; /** * The color of the activity indicator. Defaults to [UIColor whiteColor] * Does nothing on pre iOS 5. */ @property (MB_STRONG) UIColor *activityIndicatorColor; /** * The progress of the progress indicator, from 0.0 to 1.0. Defaults to 0.0. */ @property (assign) float progress; /** * The minimum size of the HUD bezel. Defaults to CGSizeZero (no minimum size). */ @property (assign) CGSize minSize; /** * The actual size of the HUD bezel. * You can use this to limit touch handling on the bezel aria only. * @see https://github.com/jdg/MBProgressHUD/pull/200 */ @property (atomic, assign, readonly) CGSize size; /** * Force the HUD dimensions to be equal if possible. */ @property (assign, getter = isSquare) BOOL square; @end @protocol MBProgressHUDDelegate <NSObject> @optional /** * Called after the HUD was fully hidden from the screen. */ - (void)hudWasHidden:(MBProgressHUD *)hud; @end /** * A progress view for showing definite progress by filling up a circle (pie chart). */ @interface MBRoundProgressView : UIView /** * Progress (0.0 to 1.0) */ @property (nonatomic, assign) float progress; /** * Indicator progress color. * Defaults to white [UIColor whiteColor] */ @property (nonatomic, MB_STRONG) UIColor *progressTintColor; /** * Indicator background (non-progress) color. * Defaults to translucent white (alpha 0.1) */ @property (nonatomic, MB_STRONG) UIColor *backgroundTintColor; /* * Display mode - NO = round or YES = annular. Defaults to round. */ @property (nonatomic, assign, getter = isAnnular) BOOL annular; @end /** * A flat bar progress view. */ @interface MBBarProgressView : UIView /** * Progress (0.0 to 1.0) */ @property (nonatomic, assign) float progress; /** * Bar border line color. * Defaults to white [UIColor whiteColor]. */ @property (nonatomic, MB_STRONG) UIColor *lineColor; /** * Bar background color. * Defaults to clear [UIColor clearColor]; */ @property (nonatomic, MB_STRONG) UIColor *progressRemainingColor; /** * Bar progress color. * Defaults to white [UIColor whiteColor]. */ @property (nonatomic, MB_STRONG) UIColor *progressColor; @end
// // MBProgressHUD.m // Version 0.9 // 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 #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 static const CGFloat kPadding = 4.f; static const CGFloat kLabelFontSize = 16.f; static const CGFloat kDetailsLabelFontSize = 12.f; @interface MBProgressHUD () { BOOL useAnimation; SEL methodForExecution; id targetForExecution; id objectForExecution; UILabel *label; UILabel *detailsLabel; BOOL isFinished; CGAffineTransform rotationTransform; } @property (atomic, MB_STRONG) UIView *indicator; @property (atomic, MB_STRONG) NSTimer *graceTimer; @property (atomic, MB_STRONG) NSTimer *minShowTimer; @property (atomic, MB_STRONG) NSDate *showStarted; @end @implementation MBProgressHUD #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; @synthesize activityIndicatorColor; #if NS_BLOCKS_AVAILABLE @synthesize completionBlock; #endif #pragma mark - Class methods + (MB_INSTANCETYPE)showHUDAddedTo:(UIView *)view animated:(BOOL)animated { MBProgressHUD *hud = [[self alloc] initWithView:view]; hud.removeFromSuperViewOnHide = YES; [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 = [UIFont boldSystemFontOfSize:kLabelFontSize]; self.labelColor = [UIColor whiteColor]; self.detailsLabelFont = [UIFont boldSystemFontOfSize:kDetailsLabelFontSize]; self.detailsLabelColor = [UIColor whiteColor]; self.activityIndicatorColor = [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.contentMode = UIViewContentModeCenter; 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 - (BOOL)shouldPerformOrientationTransform { BOOL isPreiOS8 = NSFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_8_0; // prior to iOS8 code needs to take care of rotation if it is being added to the window return isPreiOS8 && [self.superview isKindOfClass:[UIWindow class]]; } - (void)didMoveToSuperview { if ([self shouldPerformOrientationTransform]) { [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) { if (!isActivityIndicator) { // Update to indeterminate indicator [indicator removeFromSuperview]; self.indicator = MB_AUTORELEASE([[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]); [(UIActivityIndicatorView *)indicator startAnimating]; [self addSubview:indicator]; } #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 50000 [(UIActivityIndicatorView *)indicator setColor:self.activityIndicatorColor]; #endif } 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 { [super layoutSubviews]; // Entirely cover the parent view UIView *parent = self.superview; if (parent) { self.frame = parent.bounds; } CGRect bounds = self.bounds; // Determine the total width and height needed CGFloat maxWidth = CGRectGetWidth(bounds) - 4 * margin; CGSize totalSize = CGSizeZero; CGRect indicatorF = indicator.bounds; indicatorF.size.width = MIN(CGRectGetWidth(indicatorF), maxWidth); totalSize.width = MAX(totalSize.width, CGRectGetWidth(indicatorF)); totalSize.height += CGRectGetHeight(indicatorF); 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 && CGRectGetHeight(indicatorF) > 0.f) { totalSize.height += kPadding; } CGFloat remainingHeight = CGRectGetHeight(bounds) - 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(((CGRectGetHeight(bounds) - totalSize.height) / 2)) + margin + yOffset; CGFloat xPos = xOffset; indicatorF.origin.y = yPos; indicatorF.origin.x = round((CGRectGetWidth(bounds) - CGRectGetWidth(indicatorF)) / 2) + xPos; indicator.frame = indicatorF; yPos += CGRectGetHeight(indicatorF); if (labelSize.height > 0.f && CGRectGetHeight(indicatorF) > 0.f) { yPos += kPadding; } CGRect labelF; labelF.origin.y = yPos; labelF.origin.x = round((CGRectGetWidth(bounds) - labelSize.width) / 2) + xPos; labelF.size = labelSize; label.frame = labelF; yPos += labelF.size.height; if (detailsLabelSize.height > 0.f && (CGRectGetHeight(indicatorF) > 0.f || labelSize.height > 0.f)) { yPos += kPadding; } CGRect detailsLabelF; detailsLabelF.origin.y = yPos; detailsLabelF.origin.x = round((CGRectGetWidth(bounds) - 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; } 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(CGRectGetWidth(self.bounds)/2, CGRectGetHeight(self.bounds)/2); //Gradient radius float gradRadius = MIN(CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds)) ; //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((CGRectGetWidth(allRect) - size.width) / 2) + self.xOffset, round((CGRectGetHeight(allRect) - 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", @"activityIndicatorColor", 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"] || [keyPath isEqualToString:@"activityIndicatorColor"]) { [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(statusBarOrientationDidChange:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; } - (void)unregisterFromNotifications { NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; } - (void)statusBarOrientationDidChange:(NSNotification *)notification { UIView *superview = self.superview; if (!superview) { return; } else if ([self shouldPerformOrientationTransform]) { [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]; } // Window coordinates differ below iOS8 // In iOS8 the UIScreen's bounds now interface-oriented // more see https://developer.apple.com/videos/wwdc/2014/#214 CGFloat radians = 0; if (NSFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_8_0) { UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; if (UIInterfaceOrientationIsLandscape(orientation)) { if (orientation == UIInterfaceOrientationLandscapeLeft) { radians = -(CGFloat)M_PI_2; } else { radians = (CGFloat)M_PI_2; } self.bounds = CGRectMake(0, 0, CGRectGetHeight(self.bounds), CGRectGetWidth(self.bounds)); } else { if (orientation == UIInterfaceOrientationPortraitUpsideDown) { radians = (CGFloat)M_PI; } else { radians = 0; } } } rotationTransform = CGAffineTransformMakeRotation(radians); if (animated) { [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:0.3]; } [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 BOOL isPreiOS7 = NSFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0; CGFloat lineWidth = isPreiOS7 ? 5.f : 2.f; UIBezierPath *processBackgroundPath = [UIBezierPath bezierPath]; processBackgroundPath.lineWidth = lineWidth; processBackgroundPath.lineCapStyle = kCGLineCapButt; CGPoint center = CGPointMake(CGRectGetWidth(self.bounds)/2, CGRectGetHeight(self.bounds)/2); CGFloat radius = (CGRectGetWidth(self.bounds) - 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 [_progressTintColor setStroke]; [_backgroundTintColor setFill]; CGContextSetLineWidth(context, 2.0f); CGContextFillEllipseInRect(context, circleRect); CGContextStrokeEllipseInRect(context, circleRect); // Draw progress CGPoint center = CGPointMake(CGRectGetWidth(allRect) / 2, CGRectGetHeight(allRect) / 2); CGFloat radius = (CGRectGetWidth(allRect) - 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 = (CGRectGetHeight(rect) / 2) - 2; CGContextMoveToPoint(context, 2, CGRectGetHeight(rect)/2); CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius); CGContextAddLineToPoint(context, CGRectGetWidth(rect) - radius - 2, 2); CGContextAddArcToPoint(context, CGRectGetWidth(rect) - 2, 2, CGRectGetWidth(rect) - 2, CGRectGetHeight(rect) / 2, radius); CGContextAddArcToPoint(context, CGRectGetWidth(rect) - 2, CGRectGetHeight(rect) - 2, CGRectGetWidth(rect) - radius - 2, CGRectGetHeight(rect) - 2, radius); CGContextAddLineToPoint(context, radius + 2, CGRectGetHeight(rect) - 2); CGContextAddArcToPoint(context, 2, CGRectGetHeight(rect) - 2, 2, CGRectGetHeight(rect)/2, radius); CGContextFillPath(context); // Draw border CGContextMoveToPoint(context, 2, CGRectGetHeight(rect)/2); CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius); CGContextAddLineToPoint(context, CGRectGetWidth(rect) - radius - 2, 2); CGContextAddArcToPoint(context, CGRectGetWidth(rect) - 2, 2, CGRectGetWidth(rect) - 2, CGRectGetHeight(rect) / 2, radius); CGContextAddArcToPoint(context, CGRectGetWidth(rect) - 2, CGRectGetHeight(rect) - 2, CGRectGetWidth(rect) - radius - 2, CGRectGetHeight(rect) - 2, radius); CGContextAddLineToPoint(context, radius + 2, CGRectGetHeight(rect) - 2); CGContextAddArcToPoint(context, 2, CGRectGetHeight(rect) - 2, 2, CGRectGetHeight(rect)/2, radius); CGContextStrokePath(context); CGContextSetFillColorWithColor(context, [_progressColor CGColor]); radius = radius - 2; float amount = self.progress * CGRectGetWidth(rect); // Progress in the middle area if (amount >= radius + 4 && amount <= (CGRectGetWidth(rect) - radius - 4)) { CGContextMoveToPoint(context, 4, CGRectGetHeight(rect)/2); CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius); CGContextAddLineToPoint(context, amount, 4); CGContextAddLineToPoint(context, amount, radius + 4); CGContextMoveToPoint(context, 4, CGRectGetHeight(rect)/2); CGContextAddArcToPoint(context, 4, CGRectGetHeight(rect) - 4, radius + 4, CGRectGetHeight(rect) - 4, radius); CGContextAddLineToPoint(context, amount, CGRectGetHeight(rect) - 4); CGContextAddLineToPoint(context, amount, radius + 4); CGContextFillPath(context); } // Progress in the right arc else if (amount > radius + 4) { float x = amount - (CGRectGetWidth(rect) - radius - 4); CGContextMoveToPoint(context, 4, CGRectGetHeight(rect)/2); CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius); CGContextAddLineToPoint(context, CGRectGetWidth(rect) - radius - 4, 4); float angle = -acos(x/radius); if (isnan(angle)) angle = 0; CGContextAddArc(context, CGRectGetWidth(rect) - radius - 4, CGRectGetHeight(rect)/2, radius, M_PI, angle, 0); CGContextAddLineToPoint(context, amount, CGRectGetHeight(rect)/2); CGContextMoveToPoint(context, 4, CGRectGetHeight(rect)/2); CGContextAddArcToPoint(context, 4, CGRectGetHeight(rect) - 4, radius + 4, CGRectGetHeight(rect) - 4, radius); CGContextAddLineToPoint(context, CGRectGetWidth(rect) - radius - 4, CGRectGetHeight(rect) - 4); angle = acos(x/radius); if (isnan(angle)) angle = 0; CGContextAddArc(context, CGRectGetWidth(rect) - radius - 4, CGRectGetHeight(rect)/2, radius, -M_PI, angle, 1); CGContextAddLineToPoint(context, amount, CGRectGetHeight(rect)/2); CGContextFillPath(context); } // Progress is in the left arc else if (amount < radius + 4 && amount > 0) { CGContextMoveToPoint(context, 4, CGRectGetHeight(rect)/2); CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius); CGContextAddLineToPoint(context, radius + 4, CGRectGetHeight(rect)/2); CGContextMoveToPoint(context, 4, CGRectGetHeight(rect)/2); CGContextAddArcToPoint(context, 4, CGRectGetHeight(rect) - 4, radius + 4, CGRectGetHeight(rect) - 4, radius); CGContextAddLineToPoint(context, radius + 4, CGRectGetHeight(rect)/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
以下是本人在MBProgressHUD基础上封装的类,觉得部分的使用基于block
ShowHUD.h 与 ShowHUD.m
// // ShowHUD.h // TestHUD // // Created by YouXianMing on 14-9-29. // Copyright (c) 2014年 YouXianMing. All rights reserved. // #import <Foundation/Foundation.h> #import "MBProgressHUD.h" @class ShowHUD; // 定义block typedef void (^ConfigShowHUDBlock)(ShowHUD *config); typedef UIView *(^ConfigShowHUDCustomViewBlock)(); // 定义枚举值 typedef enum { Fade = MBProgressHUDAnimationFade, Zoom = MBProgressHUDAnimationZoom, ZoomOut = MBProgressHUDAnimationZoomOut, ZoomIn = MBProgressHUDAnimationZoomIn, } HUDAnimationType; @interface ShowHUD : NSObject // 动画效果 @property (nonatomic, assign) HUDAnimationType animationStyle; // 动画样式 // 文本加菊花 @property (nonatomic, strong) NSString *text; // 文本 @property (nonatomic, strong) UIFont *textFont; // 文本字体 // 自定义view @property (nonatomic, strong) UIView *customView; // 自定义view 37x37尺寸 // 只显示文本的相关设置 @property (nonatomic, assign) BOOL showTextOnly; // 只显示文本 // 边缘留白 @property (nonatomic, assign) float margin; // 边缘留白 // 颜色设置(设置了颜色之后,透明度就会失效) @property (nonatomic, strong) UIColor *backgroundColor; // 背景颜色 @property (nonatomic, strong) UIColor *labelColor; // 文本颜色 // 透明度 @property (nonatomic, assign) float opacity; // 透明度 // 圆角 @property (nonatomic, assign) float cornerRadius; // 圆角 // 仅仅显示文本并持续几秒的方法 /* - 使用示例 - [ShowHUD showTextOnly:@"请稍后,显示不了..." configParameter:^(ShowHUD *config) { config.margin = 10.f; // 边缘留白 config.opacity = 0.7f; // 设定透明度 config.cornerRadius = 2.f; // 设定圆角 } duration:3 inView:self.view]; */ + (void)showTextOnly:(NSString *)text configParameter:(ConfigShowHUDBlock)config duration:(NSTimeInterval)sec inView:(UIView *)view; // 显示文本与菊花并持续几秒的方法(文本为nil时只显示菊花) /* - 使用示例 - [ShowHUD showText:@"请稍后,显示不了..." configParameter:^(ShowHUD *config) { config.margin = 10.f; // 边缘留白 config.opacity = 0.7f; // 设定透明度 config.cornerRadius = 2.f; // 设定圆角 } duration:3 inView:self.view]; */ + (void)showText:(NSString *)text configParameter:(ConfigShowHUDBlock)config duration:(NSTimeInterval)sec inView:(UIView *)view; // 加载自定义view并持续几秒的方法 /* - 使用示例 - [ShowHUD showText:@"请稍后,显示不了..." configParameter:^(ShowHUD *config) { config.margin = 10.f; // 边缘留白 config.opacity = 0.7f; // 设定透明度 config.cornerRadius = 2.f; // 设定圆角 } duration:3 inView:self.view]; */ + (void)showCustomView:(ConfigShowHUDCustomViewBlock)viewBlock configParameter:(ConfigShowHUDBlock)config duration:(NSTimeInterval)sec inView:(UIView *)view; + (instancetype)showTextOnly:(NSString *)text configParameter:(ConfigShowHUDBlock)config inView:(UIView *)view; + (instancetype)showText:(NSString *)text configParameter:(ConfigShowHUDBlock)config inView:(UIView *)view; + (instancetype)showCustomView:(ConfigShowHUDCustomViewBlock)viewBlock configParameter:(ConfigShowHUDBlock)config inView:(UIView *)view; - (void)hide; @end
// // ShowHUD.m // TestHUD // // Created by YouXianMing on 14-9-29. // Copyright (c) 2014年 YouXianMing. All rights reserved. // #import "ShowHUD.h" #ifdef DEBUG #define ShowHUD_DLog(fmt, ...) NSLog((@"ShowHUD.m:%s:%d" fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__); #else #define ShowHUD_DLog(...) #endif @interface ShowHUD ()<MBProgressHUDDelegate> { MBProgressHUD *_hud; } @end @implementation ShowHUD - (instancetype)initWithView:(UIView *)view { if (view == nil) { return nil; } self = [super init]; if (self) { _hud = [[MBProgressHUD alloc] initWithView:view]; _hud.delegate = self; // 设置代理 _hud.animationType = MBProgressHUDAnimationZoom; // 默认动画样式 _hud.removeFromSuperViewOnHide = YES; // 该视图隐藏后则自动从父视图移除掉 [view addSubview:_hud]; } return self; } - (void)hide:(BOOL)hide afterDelay:(NSTimeInterval)delay { [_hud hide:hide afterDelay:delay]; } - (void)hide { [_hud hide:YES]; } - (void)show:(BOOL)show { // 根据属性判断是否要显示文本 if (_text != nil && _text.length != 0) { _hud.labelText = _text; } // 设置文本字体 if (_textFont) { _hud.labelFont = _textFont; } // 如果设置这个属性,则只显示文本 if (_showTextOnly == YES && _text != nil && _text.length != 0) { _hud.mode = MBProgressHUDModeText; } // 设置背景色 if (_backgroundColor) { _hud.color = _backgroundColor; } // 文本颜色 if (_labelColor) { _hud.labelColor = _labelColor; } // 设置圆角 if (_cornerRadius) { _hud.cornerRadius = _cornerRadius; } // 设置透明度 if (_opacity) { _hud.opacity = _opacity; } // 自定义view if (_customView) { _hud.mode = MBProgressHUDModeCustomView; _hud.customView = _customView; } // 边缘留白 if (_margin > 0) { _hud.margin = _margin; } [_hud show:show]; } #pragma mark - HUD代理方法 - (void)hudWasHidden:(MBProgressHUD *)hud { [_hud removeFromSuperview]; _hud = nil; } #pragma mark - 重写setter方法 @synthesize animationStyle = _animationStyle; - (void)setAnimationStyle:(HUDAnimationType)animationStyle { _animationStyle = animationStyle; _hud.animationType = (MBProgressHUDAnimation)_animationStyle; } - (HUDAnimationType)animationStyle { return _animationStyle; } #pragma mark - 便利的方法 + (void)showTextOnly:(NSString *)text configParameter:(ConfigShowHUDBlock)config duration:(NSTimeInterval)sec inView:(UIView *)view { ShowHUD *hud = [[ShowHUD alloc] initWithView:view]; hud.text = text; hud.showTextOnly = YES; hud.margin = 10.f; // 配置额外的参数 config(hud); // 显示 [hud show:YES]; // 延迟sec后消失 [hud hide:YES afterDelay:sec]; } + (void)showText:(NSString *)text configParameter:(ConfigShowHUDBlock)config duration:(NSTimeInterval)sec inView:(UIView *)view { ShowHUD *hud = [[ShowHUD alloc] initWithView:view]; hud.text = text; hud.margin = 10.f; // 配置额外的参数 config(hud); // 显示 [hud show:YES]; // 延迟sec后消失 [hud hide:YES afterDelay:sec]; } + (void)showCustomView:(ConfigShowHUDCustomViewBlock)viewBlock configParameter:(ConfigShowHUDBlock)config duration:(NSTimeInterval)sec inView:(UIView *)view { ShowHUD *hud = [[ShowHUD alloc] initWithView:view]; hud.margin = 10.f; // 配置额外的参数 config(hud); // 自定义View hud.customView = viewBlock(); // 显示 [hud show:YES]; [hud hide:YES afterDelay:sec]; } + (instancetype)showTextOnly:(NSString *)text configParameter:(ConfigShowHUDBlock)config inView:(UIView *)view { ShowHUD *hud = [[ShowHUD alloc] initWithView:view]; hud.text = text; hud.showTextOnly = YES; hud.margin = 10.f; // 配置额外的参数 config(hud); // 显示 [hud show:YES]; return hud; } + (instancetype)showText:(NSString *)text configParameter:(ConfigShowHUDBlock)config inView:(UIView *)view { ShowHUD *hud = [[ShowHUD alloc] initWithView:view]; hud.text = text; hud.margin = 10.f; // 配置额外的参数 config(hud); // 显示 [hud show:YES]; return hud; } + (instancetype)showCustomView:(ConfigShowHUDCustomViewBlock)viewBlock configParameter:(ConfigShowHUDBlock)config inView:(UIView *)view { ShowHUD *hud = [[ShowHUD alloc] initWithView:view]; hud.margin = 10.f; // 配置额外的参数 config(hud); // 自定义View hud.customView = viewBlock(); // 显示 [hud show:YES]; return hud; } - (void)dealloc { ShowHUD_DLog(@"资源释放了,没有泄露^_^"); } @end
使用时候的源码如下:
// // ViewController.m // TestHUD // // Created by YouXianMing on 14-9-29. // Copyright (c) 2014年 YouXianMing. All rights reserved. // #import "ViewController.h" #import "ShowHUD.h" // 引入头文件 typedef enum : NSUInteger { CASE_1, // 显示文本和菊花,延时3秒后消失 CASE_2, // 仅仅显示文本,延时3秒后消失 CASE_3, // 加载自定义view,3秒后消失 } E_CASE; @interface ViewController ()<MBProgressHUDDelegate> @property (nonatomic, assign) NSInteger caseType; @end @implementation ViewController - (void)showHUD { UIWindow *window = [UIApplication sharedApplication].keyWindow; switch (_caseType++ % 3) { case CASE_1: { [ShowHUD showText:@"YouXianMing" configParameter:^(ShowHUD *config) { config.margin = 10.f; // 边缘留白 config.opacity = 0.7f; // 设定透明度 config.cornerRadius = 1.f; // 设定圆角 config.textFont = [UIFont systemFontOfSize:11.f]; } duration:3 inView:window]; } break; case CASE_2: { [ShowHUD showTextOnly:@"YouXianMing" configParameter:^(ShowHUD *config) { config.animationStyle = ZoomOut; // 设置动画方式 config.margin = 20.f; // 边缘留白 config.opacity = 0.8f; // 设定透明度 config.cornerRadius = 0.1f; // 设定圆角 config.backgroundColor = [[UIColor redColor] colorWithAlphaComponent:0.8]; // 设置背景色 config.labelColor = [[UIColor whiteColor] colorWithAlphaComponent:1.0];// 设置文本颜色 } duration:3 inView:window]; } break; // case CASE_3: { // BackgroundView *backView = [[BackgroundView alloc] initInView:window]; // backView.startDuration = 0.25; // backView.endDuration = 0.25; // [backView addToView]; // // ShowHUD *hud = [ShowHUD showCustomView:^UIView *{ // // 返回一个自定义view即可,hud会自动根据你返回的view调整空间 // MulticolorView *showView = [[MulticolorView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)]; // showView.lineWidth = 1.f; // showView.sec = 1.5f; // showView.colors = @[(id)[UIColor cyanColor].CGColor, // (id)[UIColor yellowColor].CGColor, // (id)[UIColor cyanColor].CGColor]; // [showView startAnimation]; // return showView; // } configParameter:^(ShowHUD *config) { // config.animationStyle = Zoom; // 设定动画方式 // config.margin = 10.f; // 边缘留白 // config.cornerRadius = 2.f; // 边缘圆角 // config.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.4f]; // } inView:window]; // // // 延迟5秒后消失 // [GCDQueue executeInMainQueue:^{ // [hud hide]; // [backView removeSelf]; // } afterDelaySecs:5]; // } break; default: break; } } - (void)viewDidLoad { [super viewDidLoad]; _caseType = 0; UIButton *button = [[UIButton alloc] initWithFrame:self.view.bounds]; [self.view addSubview:button]; [button addTarget:self action:@selector(buttonEvent:) forControlEvents:UIControlEventTouchUpInside]; } - (void)buttonEvent:(id)sender { [self showHUD]; } @end
以下是使用上的一些小细节