MBProgressHUD
是一個(gè)顯示提示窗口的三方庫(kù),常用于用戶交互、后臺(tái)耗時(shí)操作等的提示。通過(guò)顯示一個(gè)提示框,通知用戶操作或任務(wù)的執(zhí)行狀態(tài);同時(shí),利用動(dòng)畫效果,降低用戶等待的焦慮心理,增強(qiáng)用戶體驗(yàn)。
??本篇文章從源碼角度來(lái)看一下MBProgressHUD
是如何實(shí)現(xiàn)的,所用的知識(shí)都是比較基礎(chǔ)的,不過(guò)還是值得我們學(xué)習(xí)一下。詳解如下:
1. 類介紹
-
MBProgressHUD
?? 這是MBProgressHUD
的主要類,提供豐富的屬性來(lái)調(diào)整視圖的樣式。 -
MBRoundProgressView
?? 這是提供Determinate
視圖顯示的類,有非圓環(huán)和圓環(huán)視圖兩種方式。 -
MBBarProgressView
?? 這是提供進(jìn)度條的視圖類。 -
MBBackgroundView
?? 這是MBProgressHUD
的背景視圖類,利用UIVisualEffectView
提供毛玻璃效果
2. MBProgressHUD類的顯示模式
-
MBProgressHUDModeIndeterminate
Indeterminate -
MBProgressHUDModeDeterminate
Determinate -
MBProgressHUDModeDeterminateHorizontalBar
DeterminateHorizontalBar -
MBProgressHUDModeAnnularDeterminate
AnnularDeterminate -
MBProgressHUDModeCustomView
這是自定義視圖 -
MBProgressHUDModeText
Text
3.動(dòng)畫模式
-
MBProgressHUDAnimationFade
: 漸變模式 -
MBProgressHUDAnimationZoom
: Zoom In & Zoom Out -
MBProgressHUDAnimationZoomOut
: 消失時(shí)帶變小動(dòng)畫 -
MBProgressHUDAnimationZoomIn
: 出現(xiàn)時(shí)帶變大動(dòng)畫
4. 背景樣式
-
MBProgressHUDBackgroundStyleSolidColor
: 正常顏色 -
MBProgressHUDBackgroundStyleBlur
: 毛玻璃效果
5. 視圖內(nèi)容
-
@property (strong, nonatomic, readonly) UILabel *label;
: 標(biāo)題 -
@property (strong, nonatomic, readonly) UILabel *detailsLabel;
:詳情 -
@property (strong, nonatomic, readonly) UIButton *button
: 按鈕(顯示在標(biāo)題下方) -
@property (strong, nonatomic, nullable) UIView *customView;
:用戶自定義視圖 -
@property (strong, nonatomic, readonly) MBBackgroundView *backgroundView;
: 整個(gè)背景視圖 -
@property (strong, nonatomic, readonly) MBBackgroundView *bezelView;
:提示框背景視圖 -
@property (strong, nonatomic, nullable) UIColor *contentColor UI_APPEARANCE_SELECTOR;
: 提示框的內(nèi)容顏色 -
@property (assign, nonatomic) CGPoint offset UI_APPEARANCE_SELECTOR;
:提示框相對(duì)父視圖中心點(diǎn)的偏移量 -
@property (assign, nonatomic) CGFloat margin UI_APPEARANCE_SELECTOR;
:提示框內(nèi)的內(nèi)容視圖的邊距 -
@property (assign, nonatomic) CGSize minSize UI_APPEARANCE_SELECTOR;
:提示框最小尺寸 -
@property (assign, nonatomic) BOOL removeFromSuperViewOnHide;
:隱藏時(shí)從父視圖中刪除 -
@property (assign, nonatomic) NSTimeInterval graceTime;
:延遲多久后顯示提示框,避免快速執(zhí)行的任務(wù)也顯示提示框,給用戶造成視覺(jué)干擾。 -
@property (assign, nonatomic) NSTimeInterval minShowTime;
:提示框視圖最少展示的時(shí)間
6. 創(chuàng)建和隱藏視圖
- 創(chuàng)建流程
?? 通過(guò)+ (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated
類方法創(chuàng)建視圖,也可以通過(guò)對(duì)象方法創(chuàng)建,不過(guò)建議用類方法,不僅創(chuàng)建方便,而且會(huì)自動(dòng)的添加到父視圖,然后進(jìn)行顯示。其中,創(chuàng)建過(guò)程如下:
- (void)commonInit {
// Set default values for properties
_animationType = MBProgressHUDAnimationFade;
_mode = MBProgressHUDModeIndeterminate;
_margin = 20.0f;
_opacity = 1.f;
_defaultMotionEffectsEnabled = YES;
// Default color, depending on the current iOS version
BOOL isLegacy = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0;
_contentColor = isLegacy ? [UIColor whiteColor] : [UIColor colorWithWhite:0.f alpha:0.7f];
// Transparent background
self.opaque = NO;
self.backgroundColor = [UIColor clearColor];
// Make it invisible for now
self.alpha = 0.0f;
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.layer.allowsGroupOpacity = NO;
[self setupViews];
[self updateIndicators];
[self registerForNotifications];
}
我們可以發(fā)現(xiàn),通過(guò)添加子空間后,根據(jù)視圖模式調(diào)用updateIndicators
方法來(lái)創(chuàng)建不同的視圖,最后添加了一個(gè)狀態(tài)欄的通知,用來(lái)在橫豎屏?xí)r跳轉(zhuǎn)視圖。其中,在顯示提示框時(shí),會(huì)首先判斷graceTime
,如過(guò)不為0
,那么就創(chuàng)建一個(gè)定時(shí)器倒計(jì)時(shí),時(shí)間到之后再判斷任務(wù)是否結(jié)束,如果finished
不為空,就開(kāi)始顯示提示框。
- (void)showAnimated:(BOOL)animated {
MBMainThreadAssert();
[self.minShowTimer invalidate];
self.useAnimation = animated;
self.finished = NO;
// If the grace time is set, postpone the HUD display
if (self.graceTime > 0.0) {
NSTimer *timer = [NSTimer timerWithTimeInterval:self.graceTime target:self selector:@selector(handleGraceTimer:) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
self.graceTimer = timer;
}
// ... otherwise show the HUD immediately
else {
[self showUsingAnimation:self.useAnimation];
}
}
- (void)handleGraceTimer:(NSTimer *)theTimer {
// Show the HUD only if the task is still running
if (!self.hasFinished) {
[self showUsingAnimation:self.useAnimation];
}
}
- 隱藏視圖
?? 通過(guò)+ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated
隱藏視圖,其中會(huì)根據(jù)minShowTime
來(lái)判斷是否立即隱藏提示框。如果,minShowTime
不為0,那么會(huì)創(chuàng)建一個(gè)定時(shí)器,并把定時(shí)器加入到common
模式的runloop
里,等時(shí)間到后再把提示框隱藏。
- (void)hideAnimated:(BOOL)animated {
MBMainThreadAssert();
[self.graceTimer invalidate];
self.useAnimation = animated;
self.finished = YES;
// If the minShow time is set, calculate how long the HUD was shown,
// and postpone the hiding operation if necessary
if (self.minShowTime > 0.0 && self.showStarted) {
NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:self.showStarted];
if (interv < self.minShowTime) {
NSTimer *timer = [NSTimer timerWithTimeInterval:(self.minShowTime - interv) target:self selector:@selector(handleMinShowTimer:) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
self.minShowTimer = timer;
return;
}
}
// ... otherwise hide the HUD immediately
[self hideUsingAnimation:self.useAnimation];
}
7. MBRoundProgressView 和 MBBarProgressView
這兩個(gè)類,分別創(chuàng)建了 Determinate
和 進(jìn)度條 的提示框視圖,具體實(shí)現(xiàn)方法是在 - (void)drawRect:(CGRect)rect
方法里通過(guò) UIBezierPath
或者 Quarts2D
畫出,設(shè)計(jì)思想算是常規(guī),請(qǐng)參考代碼細(xì)讀。
8. MBProgressHUD應(yīng)用
對(duì)于三方框架,使用之前,最好先封裝一層(繼承或分類),方便以后的調(diào)試和新框架替換。封裝時(shí),盡量用類方法,使用時(shí)比較簡(jiǎn)潔。
- 添加提示框
+ (void)showHUDWithText:(NSString *)text inView:(UIView *)view deley:(NSTimeInterval)time
{
if (text == nil || text.length <= 0) {
return;
}
if (view == nil) {
view = [[UIApplication sharedApplication].windows lastObject];
}
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:view animated:YES];
HUD.mode = MBProgressHUDModeText;
[HUD hideAnimated:YES afterDelay:1.5];
}
- 隱藏提示框 (改方法調(diào)用時(shí),最好在主線程,異步線程可能會(huì)出現(xiàn)問(wèn)題)
+ (void)hideHUDForView:(UIView *)view
{
if (view == nil) view = [[UIApplication sharedApplication].windows lastObject];
[self hideHUDForView:view animated:YES];
}