MBProcessHUD-源碼分析與仿寫(三)

前言

閱讀優(yōu)秀的開源項目是提高編程能力的有效手段混滔,我們能夠從中開拓思維舶吗、拓寬視野征冷,學(xué)習到很多不同的設(shè)計思想以及最佳實踐。閱讀他人代碼很重要誓琼,但動手仿寫检激、練習卻也是很有必要的肴捉,它能進一步加深我們對項目的理解,將這些東西內(nèi)化為自己的知識和能力叔收。然而真正做起來卻很不容易齿穗,開源項目閱讀起來還是比較困難,需要一些技術(shù)基礎(chǔ)和耐心饺律。
本系列將對一些著名的iOS開源類庫進行深入閱讀及分析窃页,并仿寫這些類庫的基本實現(xiàn),加深我們對底層實現(xiàn)的理解和認識复濒,提升我們iOS開發(fā)的編程技能脖卖。

MBProcessHUD

MBProcessHUD是一個iOS上的提示框庫,支持加載提示巧颈、進度框畦木、文字提示等,使用簡單洛二,功能強大馋劈,還能夠自定義顯示內(nèi)容,廣泛應(yīng)用于iOS app中晾嘶。這是它的地址:https://github.com/jdg/MBProgressHUD
簡單看一下界面效果:

Paste_Image.png

實現(xiàn)原理

MBProcessHUD繼承自UIView妓雾,實際上是一個覆蓋全屏的半透明指示器組件。它由以下幾個部分構(gòu)成垒迂,分別是:Loading加載動畫械姻,標題欄,背景欄以及其它欄(如詳情欄机断、按鈕)楷拳。我們把MBProcessHUD添加到頁面上,顯示任務(wù)進度及提示信息吏奸,同時屏蔽用戶交互操作欢揖。
MBProcessHUD的Loading加載動畫來自系統(tǒng)類UIActivityIndicatorView,在頁面加載時奋蔚,開啟轉(zhuǎn)圈動畫她混,頁面銷毀時取消轉(zhuǎn)圈動畫。
MBProcessHUD根據(jù)加載內(nèi)容動態(tài)布局泊碑,它通過計算需要顯示的內(nèi)容坤按,動態(tài)調(diào)整頁面元素的位置大小,放置到屏幕的中央馒过,顯示的內(nèi)容可以由使用者指定臭脓。MBProcessHUD v1.0版之前是通過frame計算各個元素的位置,最新的版本采用了約束布局腹忽。
MBProcessHUD使用KVO監(jiān)聽一些屬性值的變化来累,如labelText砚作,model。這些屬性被修改時佃扼,MBProcessHUD視圖相應(yīng)更新偎巢,傳入新值。

仿寫MBProcessHUD

我們模仿MBProcessHUD寫一個簡單的彈出框組件兼耀,以加深對它的理解。在這個demo中求冷,我們不完全重寫MBProcessHUD瘤运,只實現(xiàn)基本功能。
首先在demo中創(chuàng)建ZCJHUD匠题,繼承UIView拯坟。

Paste_Image.png

在ZCJHUD頭文件中,定義幾種顯示模式

typedef NS_ENUM(NSInteger, ZCJHUDMode) {
    /** 轉(zhuǎn)圈動畫模式韭山,默認值 */
    ZCJHUDModeIndeterminate,
    /** 只顯示標題 */
    ZCJHUDModeText
};

定義對外的接口郁季,顯示模式mode,標題內(nèi)容labelText
@interface ZCJHUD : UIView

@property (nonatomic, assign) ZCJHUDMode mode;

@property (nonatomic, strong) NSString *labelText;

- (instancetype)initWithView:(UIView *)view;

- (void)show;

- (void)hide;

@end

自身初始化钱磅,設(shè)置組件默認屬性梦裂,更新布局,注冊kvo監(jiān)視屬性變化盖淡。
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_mode = ZCJHUDModeIndeterminate;
_labelText = nil;
_size = CGSizeZero;
self.opaque = NO;
self.backgroundColor = [UIColor clearColor];
self.alpha = 0;

        [self setupView];
        [self updateIndicators];
        [self registerForKVO];
    }
    return self;
}

初始化轉(zhuǎn)圈動畫年柠,并添加到hud上,ZCJHUDModeIndeterminate模式才有這個動畫
- (void)updateIndicators {
BOOL isActivityIndicator = [_indicator isKindOfClass:[UIActivityIndicatorView class]];

    if (_mode == ZCJHUDModeIndeterminate) {
        if (!isActivityIndicator) {
            // Update to indeterminate indicator
            [_indicator removeFromSuperview];
            self.indicator = ([[UIActivityIndicatorView alloc]
                                             initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]);
            [(UIActivityIndicatorView *)_indicator startAnimating];
            [self addSubview:_indicator];
        }
    } else if (_mode == ZCJHUDModeText) {
        [_indicator removeFromSuperview];
        self.indicator = nil;
    }
}

兩個主要方法褪迟,顯示和隱藏hud

-(void)show {
    self.alpha = 1;
}

-(void)hide {
    self.alpha = 0;
    [self removeFromSuperview];
}

這里使用了frame動態(tài)布局
- (void)layoutSubviews {
[super layoutSubviews];

    // 覆蓋整個視圖冗恨,屏蔽交互操作
    UIView *parent = self.superview;
    if (parent) {
        self.frame = parent.bounds;
    }
    CGRect bounds = self.bounds;
    
    
    CGFloat maxWidth = bounds.size.width - 4 * kMargin;
    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;
    }
    
    totalSize.width += 2 * kMargin;
    totalSize.height += 2 * kMargin;
    
    // Position elements
    CGFloat yPos = round(((bounds.size.height - totalSize.height) / 2)) + kMargin;
    CGFloat xPos = 0;
    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;
    
    _size = totalSize;
}

繪制背景框
- (void)drawRect:(CGRect)rect {

    CGContextRef context = UIGraphicsGetCurrentContext();
    UIGraphicsPushContext(context);
    
    
    CGContextSetGrayFillColor(context, 0.0f, 0.8);
    
    // Center HUD
    CGRect allRect = self.bounds;
    // Draw rounded HUD backgroud rect
    CGRect boxRect = CGRectMake(round((allRect.size.width - _size.width) / 2),
                                round((allRect.size.height - _size.height) / 2) , _size.width, _size.height);
    float radius = 10;
    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();
}

kvo監(jiān)控屬性變化,使用者在修改屬性時味赃,觸發(fā)頁面刷新掀抹,賦上新值。注意在頁面銷毀時要取消kvo監(jiān)控心俗,否則程序會崩潰

#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", @"labelText", 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"]) {
        [self updateIndicators];
    } else if ([keyPath isEqualToString:@"labelText"]) {
        _label.text = self.labelText;
    }
}


- (void)dealloc {
    [self unregisterFromKVO];
}

最終效果如下圖:

Paste_Image.png

最后附上 demo的地址:https://github.com/superzcj/ZCJHUD

總結(jié)

MBProcessHUD還是比較簡單的傲武,都是一些常用的東西。
希望借助這篇文章另凌,動手仿寫一遍MBProcessHUD谱轨,能更深刻地理解和認識MBProcessHUD。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吠谢,一起剝皮案震驚了整個濱河市土童,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌工坊,老刑警劉巖献汗,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件敢订,死亡現(xiàn)場離奇詭異,居然都是意外死亡罢吃,警方通過查閱死者的電腦和手機楚午,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尿招,“玉大人矾柜,你說我怎么就攤上這事【兔眨” “怎么了怪蔑?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長丧荐。 經(jīng)常有香客問我缆瓣,道長,這世上最難降的妖魔是什么虹统? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任弓坞,我火速辦了婚禮,結(jié)果婚禮上车荔,老公的妹妹穿的比我還像新娘渡冻。我一直安慰自己,他們只是感情好夸赫,可當我...
    茶點故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布菩帝。 她就那樣靜靜地躺著,像睡著了一般茬腿。 火紅的嫁衣襯著肌膚如雪呼奢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天切平,我揣著相機與錄音握础,去河邊找鬼。 笑死悴品,一個胖子當著我的面吹牛禀综,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播苔严,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼定枷,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了届氢?” 一聲冷哼從身側(cè)響起欠窒,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎退子,沒想到半個月后岖妄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體型将,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年荐虐,在試婚紗的時候發(fā)現(xiàn)自己被綠了七兜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡福扬,死狀恐怖腕铸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情忧换,我是刑警寧澤恬惯,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站亚茬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏浓恳。R本人自食惡果不足惜刹缝,卻給世界環(huán)境...
    茶點故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望颈将。 院中可真熱鬧梢夯,春花似錦、人聲如沸晴圾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽死姚。三九已至人乓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間都毒,已是汗流浹背色罚。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留账劲,地道東北人戳护。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像瀑焦,于是被迫代替她去往敵國和親腌且。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,494評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫榛瓮、插件铺董、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,059評論 4 62
  • 4.13 半塊鵝蛋 餅子 青菜 八寶粥 中午,油餅一小塊榆芦,米飯6口 土豆絲?西紅柿雞蛋湯 晚上一塊餅干 一個橘子 紅豆湯
    麥子旋閱讀 150評論 0 0
  • 在我實習之前柄粹,我和大多數(shù)人一樣喘鸟,認為小孩子都特別難管、特別煩人驻右、特別不聽話什黑。而在我當了小學(xué)教師之后,我才發(fā)現(xiàn)原來孩...
    Airing閱讀 1,058評論 1 3