iOS 更加優(yōu)雅便捷的UIAlertView/UIAlertController封裝使用

現(xiàn)已支持CocoaPods

前言:

之前做過(guò)一套關(guān)于UIAlertView/UIAlertController的混合封裝(舊版封裝鏈接:iOS (封裝)一句話(huà)調(diào)用系統(tǒng)的alertView和alertController
那個(gè)是將alertView和alertController做了版本適配封裝在一起的,提供了變參和數(shù)組兩種方式,不過(guò)現(xiàn)在看來(lái),雖然是“一句話(huà)”調(diào)用,但并不是很優(yōu)雅的方式。

這次,改變了方案,將UIAlertView和UIAlertController分開(kāi)進(jìn)行了處理,整體代碼也輕量了很多。
新版的代碼及Demo見(jiàn)GitHub:

JXTAlertManager

  • 基于UIAlertView封裝的JXTAlertView,這個(gè)是將之前寫(xiě)Demo時(shí)搞的一套快捷使用alertView的工具抽離整理出來(lái)的,并提供了C函數(shù)直接調(diào)用,像這樣:
    jxt_showAlertTitle(@"簡(jiǎn)易調(diào)試使用alert,單按鈕,標(biāo)題默認(rèn)為“確定”");
    就可以直接顯示出一個(gè)alertView。
  • 基于UIAlertController封裝的JXTAlertController,支持iOS8及以上。調(diào)用方式為UIViewController的擴(kuò)展分類(lèi)方法,支持使用鏈?zhǔn)秸Z(yǔ)法的方式配置alert的按鈕及樣式,相對(duì)于變參或者數(shù)組,更加簡(jiǎn)潔。
JXTAlertManager大體結(jié)構(gòu)

1. JXTAlertView 便捷調(diào)試工具

之所以叫做快捷調(diào)試工具,就是因?yàn)檫@套代碼是之前寫(xiě)Demo時(shí)搞出來(lái)的。所以,如果不是要適配iOS7及以下版本的話(huà),這套代碼還是建議只用在平時(shí)Demo測(cè)試。也因此,并沒(méi)有針對(duì)UIActionSheet再進(jìn)行封裝,說(shuō)白了是因?yàn)閼小?br> 平時(shí)寫(xiě)一些Demo代碼時(shí),總會(huì)用到alert、toast、HUD這些工具,如果沒(méi)有一套簡(jiǎn)便的工具,會(huì)麻煩很多,所以便從輕量便捷角度出發(fā),基于UIAlertView,封裝實(shí)現(xiàn)了alert、toast、HUD這些常用工具。

JXTAlertView大致效果演示

1.1.快捷使用alertView

如果只是簡(jiǎn)單的一個(gè)提示,可以這樣使用(這里只是一個(gè)示例,詳細(xì)用法見(jiàn)源碼):
jxt_showAlertTitle(@"簡(jiǎn)易調(diào)試使用alert,單按鈕,標(biāo)題默認(rèn)為“確定”");
其實(shí)現(xiàn)是基于:

[JXTAlertView showAlertViewWithTitle:title 
                             message:message 
                   cancelButtonTitle:cancelButtonTitle 
                    otherButtonTitle:otherButtonTitle
                   cancelButtonBlock:cancelBlock 
                    otherButtonBlock:otherBlock];

這是常用的兩個(gè)以?xún)?nèi)的按鈕的alertView,也可以這樣使用:

jxt_showAlertTwoButton(@"title", @"message", @"cancel", ^(NSInteger buttonIndex) {
    NSLog(@"cancel");
}, @"other", ^(NSInteger buttonIndex) {
    NSLog(@"other");
});

針對(duì)于復(fù)雜的多按鈕的alertView,還是使用變參方式,按鈕響應(yīng),根據(jù)添加的按鈕標(biāo)題的index號(hào)依序區(qū)分:

[JXTAlertView showAlertViewWithTitle:@"title"
                             message:@"message"
                   cancelButtonTitle:@"cancel"
                    buttonIndexBlock:^(NSInteger buttonIndex) {
    if (buttonIndex == 0) {
        NSLog(@"cancel");
    }
    else if (buttonIndex == 1) {
        NSLog(@"按鈕1");
    }
    else if (buttonIndex == 2) {
        NSLog(@"按鈕2");
    }
    else if (buttonIndex == 3) {
        NSLog(@"按鈕3");
    }
    else if (buttonIndex == 4) {
        NSLog(@"按鈕4");
    }
    else if (buttonIndex == 5) {
        NSLog(@"按鈕5");
    }
} otherButtonTitles:@"按鈕1", @"按鈕2", @"按鈕3", @"按鈕4", @"按鈕5", nil];

1.2.簡(jiǎn)單的toast

這里的toast提示,有別于傳統(tǒng)意義上的toast,因?yàn)槠涫腔赼lertView實(shí)現(xiàn)的,是一個(gè)沒(méi)有按鈕的alertView。可自定義展示延時(shí)時(shí)間,支持關(guān)閉回調(diào)的配置。

[JXTAlertView showToastViewWithTitle:@"title"
                             message:@"message"
                            duration:2
                   dismissCompletion:^(NSInteger buttonIndex) {
    NSLog(@"關(guān)閉");
}];

1.3.三種HUD的實(shí)現(xiàn)

這里的HUD區(qū)別于toast之處在于,其關(guān)閉時(shí)機(jī)可控,并不是單純的一個(gè)延時(shí)展示。
三種HUD是指單純的文字型、帶風(fēng)火輪(菊花)的加載窗、帶進(jìn)度條的加載窗。
后兩者用KVC的方式訪(fǎng)問(wèn)了alertView的私有屬性accessoryView實(shí)現(xiàn),這樣做可能沒(méi)有太大問(wèn)題,不過(guò)還是不建議線(xiàn)上開(kāi)發(fā)使用,而且利用這種方式訪(fǎng)問(wèn)私有屬性本來(lái)就是不太安全的,一旦key(私有屬性名)改變了,不做容錯(cuò)處理,會(huì)崩潰,源碼實(shí)現(xiàn)中做了一定的容錯(cuò),但是,一旦對(duì)應(yīng)key變化,也就導(dǎo)致對(duì)應(yīng)功能失效了。

  • 示例代碼(C函數(shù)方式):
jxt_showLoadingHUDTitleMessage(@"title", @"message");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    jxt_dismissHUD();
});

HUD還有對(duì)應(yīng)的簡(jiǎn)易的顯示加載成功失敗狀態(tài)的方法,以及刷新進(jìn)度條進(jìn)度值的方法,詳情見(jiàn)Demo。

2. JXTAlertController(iOS8)(鏈?zhǔn)秸Z(yǔ)法的“隱患”)

JXTAlertController是基于系統(tǒng)的UIAlertController封裝的,因此也只能支持iOS8及以上系統(tǒng)版本。
雖然源碼中的JXTAlertManagerHeader.h做了一個(gè)版本適配,但是,其意義更多在于提示,很可能因此出錯(cuò),所以,如果要適配iOS7,對(duì)應(yīng)方法還是需要自行適配。
下面都以alert舉例,actionSheet同理。

JXTAlertController大致效果演示

2.1.結(jié)構(gòu)說(shuō)明

/**
 JXTAlertController: show-alert(iOS8)

 @param title             title
 @param message           message
 @param appearanceProcess alert配置過(guò)程
 @param actionBlock       alert點(diǎn)擊響應(yīng)回調(diào)
 */
- (void)jxt_showAlertWithTitle:(nullable NSString *)title
                       message:(nullable NSString *)message
             appearanceProcess:(JXTAlertAppearanceProcess)appearanceProcess
                  actionsBlock:(nullable JXTAlertActionBlock)actionBlock NS_AVAILABLE_IOS(8_0);

上述方法是針對(duì)UIViewController做的分類(lèi)擴(kuò)展,詳見(jiàn)源碼。
也就是在某個(gè)VC中使用時(shí),可直接用self指針調(diào)用。
JXTAlertAppearanceProcess是配置塊,JXTAlertActionBlock是按鈕響應(yīng)回調(diào)塊。

2.2.鏈?zhǔn)秸Z(yǔ)法添加按鈕

[self jxt_showActionSheetWithTitle:@"title"
                           message:@"message"
                 appearanceProcess:^(JXTAlertController * _Nonnull alertMaker) {
    alertMaker.
    addActionCancelTitle(@"cancel").
    addActionDestructiveTitle(@"按鈕1").
    addActionDefaultTitle(@"按鈕2").
    addActionDefaultTitle(@"按鈕3").
    addActionDestructiveTitle(@"按鈕4");
} actionsBlock:^(NSInteger buttonIndex, UIAlertAction * _Nonnull action, JXTAlertController * _Nonnull alertSelf) {
    
    if ([action.title isEqualToString:@"cancel"]) {
        NSLog(@"cancel");
    }
    else if ([action.title isEqualToString:@"按鈕1"]) {
        NSLog(@"按鈕1");
    }
    else if ([action.title isEqualToString:@"按鈕2"]) {
        NSLog(@"按鈕2");
    }
    else if ([action.title isEqualToString:@"按鈕3"]) {
        NSLog(@"按鈕3");
    }
    else if ([action.title isEqualToString:@"按鈕4"]) {
        NSLog(@"按鈕4");
    }
}];

appearanceProcess配置塊中,alertMaker是當(dāng)前alertController對(duì)象,addActionCancelTitle(@"cancel")是添加一個(gè)按鈕,其等效于:

[alertController addAction:[UIAlertAction actionWithTitle:@"cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
    
}]];

這里引入了簡(jiǎn)單的鏈?zhǔn)秸Z(yǔ)法,可以連續(xù)添加系統(tǒng)支持的三類(lèi)action按鈕,當(dāng)然UIAlertActionStyleCancel這個(gè)樣式的action只能添加一次。這樣可以大大簡(jiǎn)化代碼。

actionsBlock是action按鈕響應(yīng)回調(diào),可以根據(jù)index區(qū)分響應(yīng),index根據(jù)執(zhí)行add的語(yǔ)法鏈從0依序增加,cancel類(lèi)型的action布局位置是固定的,和添加順序無(wú)關(guān),但其index與添加順序有關(guān)。
對(duì)于復(fù)雜或者特殊的alertController,也可以根據(jù)action.title或者action區(qū)分響應(yīng)。

2.3.鏈?zhǔn)秸Z(yǔ)法的“隱患”

用過(guò)Masonry這個(gè)庫(kù)的,應(yīng)該都對(duì)鏈?zhǔn)秸Z(yǔ)法不會(huì)太陌生。鏈?zhǔn)秸Z(yǔ)法可以使得代碼簡(jiǎn)化且邏輯清晰化。但是,其也有一定的“隱患”存在。
Masonry應(yīng)該是用的最多的一個(gè)自動(dòng)布局的三方庫(kù),類(lèi)似的還有SDAutoLayout(這里只是舉例,同樣的三方還有很多,這個(gè)應(yīng)該是除了Masonry外,用的相對(duì)多一些的一個(gè))這樣的,同樣的鏈?zhǔn)秸Z(yǔ)法,后者似乎更加簡(jiǎn)潔優(yōu)雅。那為什么大名鼎鼎的Masonry不這么干呢?我想是因?yàn)椤鞍踩薄?/p>

SDAutoLayout的Demo做一個(gè)實(shí)驗(yàn):

view為nil導(dǎo)致的崩潰

這里把view0置為nil,之后運(yùn)行,程序直接崩潰了。。。這類(lèi)似于執(zhí)行一個(gè)未賦值的空block。
有人可能會(huì)認(rèn)為這樣的實(shí)驗(yàn)沒(méi)有意義,為nil了干嘛還要布局呢?其實(shí)這是筆者前陣子在封裝一個(gè)鏈?zhǔn)綆?kù)時(shí)遇到的問(wèn)題:實(shí)際應(yīng)用開(kāi)發(fā)中,情況要復(fù)雜很多,有些view是動(dòng)態(tài)添加的,甚至是根據(jù)接口數(shù)據(jù)動(dòng)態(tài)創(chuàng)建的,如果在處理這類(lèi)代碼邏輯中稍有不慎,就會(huì)造成上述問(wèn)題,給不存在的view進(jìn)行布局,直接導(dǎo)致程序崩潰。。。
其實(shí)這也是代碼書(shū)寫(xiě)規(guī)范的問(wèn)題,針對(duì)這類(lèi)動(dòng)態(tài)view,在處理時(shí),本就應(yīng)該添加if條件判斷的,不過(guò)有時(shí)容易忽視,或者他人接手相關(guān)代碼時(shí),也容易忽略。如果用Masonry的塊配置布局,就不會(huì)發(fā)生這類(lèi)問(wèn)題,因?yàn)檫@種情況,對(duì)于Masonry那種寫(xiě)法,就是一個(gè)空指針執(zhí)行一個(gè)方法,其結(jié)果就是不執(zhí)行,而像SDAutoLayout這類(lèi)的,不作判空處理,就會(huì)導(dǎo)致程序崩潰。這里尤其要注意。

2.4.其他配置

針對(duì)alert上的輸入框,保持系統(tǒng)的添加方式,示例如下:

[self jxt_showAlertWithTitle:@"title"
                     message:@"message"
           appearanceProcess:^(JXTAlertController * _Nonnull alertMaker) {
    alertMaker.
    addActionDestructiveTitle(@"獲取輸入框1").
    addActionDestructiveTitle(@"獲取輸入框2");

    [alertMaker setAlertDidShown:^{
        [self logMsg:@"alertDidShown"];//不用擔(dān)心循環(huán)引用
    }];
    alertMaker.alertDidDismiss = ^{
        [self logMsg:@"alertDidDismiss"];
    };
    
    [alertMaker addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.placeholder = @"輸入框1-請(qǐng)輸入";
    }];
    [alertMaker addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.placeholder = @"輸入框2-請(qǐng)輸入";
    }];
} actionsBlock:^(NSInteger buttonIndex, UIAlertAction * _Nonnull action, JXTAlertController * _Nonnull alertSelf) {
    if (buttonIndex == 0) {
        UITextField *textField = alertSelf.textFields.firstObject;
        [self logMsg:textField.text];//不用擔(dān)心循環(huán)引用
    }
    else if (buttonIndex == 1) {
        UITextField *textField = alertSelf.textFields.lastObject;
        [self logMsg:textField.text];
    }
}];

對(duì)于alert展示和關(guān)閉的回調(diào),同樣支持以block的方式配置。
如果appearanceProcess塊不進(jìn)行任何配置操作,即無(wú)按鈕的alert,同樣默認(rèn)以toast模式處理。可通過(guò)設(shè)置toastStyleDuration屬性,配置toast展示延遲時(shí)間。

2.5.改變alertController的字體顏色

可以通過(guò)KVC的方式訪(fǎng)問(wèn)alertController的私有屬性,從而進(jìn)行修改對(duì)應(yīng)的字體的顏色,甚至字體。
對(duì)于UIAlertAction,可以用下面的方式修改字體顏色:
[alertAction setValue:[UIColor grayColor] forKey:@"titleTextColor"];

修改UIAlertAction字體顏色的效果

但是就像前面說(shuō)的,個(gè)人并不推薦這類(lèi)方式,所以源碼中沒(méi)有提供相關(guān)的方法。
如果有對(duì)應(yīng)的特殊需求,總體來(lái)說(shuō),還是自定義alert視圖比較靈活,網(wǎng)上相關(guān)的開(kāi)源庫(kù)也有很多可以直接使用,不必總是糾結(jié)于系統(tǒng)的實(shí)現(xiàn)方式。

最后,歡迎使用JXTAlertManager,如果遇到任何問(wèn)題,請(qǐng)及時(shí)聯(lián)系作者。

參考文章:
1.iOS更改UIActionController彈出字體的樣式
2.UIAlertController 簡(jiǎn)單修改title以及按鈕的字體顏色
3.How to add subview inside UIAlertView for iOS 7?
4.UIAlertView addSubview in iOS7
5.iOS UIAlertView中UIActivityindicatorView風(fēng)火輪提示加載等待

·轉(zhuǎn)載請(qǐng)聲明作者、出處·

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,241評(píng)論 4 61
  • 在斯利美門(mén)口,我們相擁而別,說(shuō)著甜膩的告別話(huà),末了,揮揮手,我背著你們往家走,烈日烤得我透不過(guò)氣來(lái),一不小心把我心...
    九娘閱讀 321評(píng)論 0 2
  • 提起“選擇”二字,對(duì)于善于選擇的人來(lái)說(shuō)只是平常小事;而對(duì)于選擇起來(lái)有頗多困難的人來(lái)說(shuō),就是大事啦。 我也不知道為何...
    木子骙骙閱讀 268評(píng)論 0 0
  • 第十六天打卡 可愛(ài)的老虎 準(zhǔn)備材料 一根鉛筆 一塊橡皮 一支黑筆 一盒彩色鉛筆 一張白紙 繪圖步驟 1.在...
    趙縵纓閱讀 1,805評(píng)論 0 1