你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> iOS 更加優雅便捷的UIAlertView

iOS 更加優雅便捷的UIAlertView

編輯:IOS開發基礎

原文

前言:

之前做過一套關於UIAlertView/UIAlertController的混合封裝,詳見:

iOS (封裝)一句話調用系統的alertView和alertController  

這個是將alertView和alertController做了版本適配封裝在一起的,提供了變參和數組兩種方式,不過現在看來,雖然是“一句話”調用,但並不是很優雅的方式。

這次,改變了方案,將UIAlertView和UIAlertController分開進行了處理,整體代碼也輕量了很多。

  • 基於UIAlertView封裝的JXTAlertView,這個是將之前寫Demo時搞的一套快捷使用alertView的工具抽離整理出來的,並提供了C函數直接調用,像這樣:
    jxt_showAlertTitle(@"簡易調試使用alert,單按鈕,標題默認為“確定”");
    就可以直接顯示出一個alertView。

  • 基於UIAlertController封裝的JXTAlertController,支持iOS8及以上。調用方式為UIViewController的擴展分類方法,支持使用鏈式語法的方式配置alert的按鈕及樣式,相對於變參或者數組,更加簡潔。

代碼及Demo見GitHub:

JXTAlertManager

1468630-5faebd1f6f2a3ccf.png

JXTAlertManager大體結構

1. JXTAlertView 便捷調試工具

之所以叫做快捷調試工具,就是因為這套代碼是之前寫Demo時搞出來的。所以,如果不是要適配iOS7及以下版本的話,這套代碼還是建議只用在平時Demo測試。也因此,並沒有針對UIActionSheet再進行封裝,說白了是因為懶……
平時寫一些Demo代碼時,總會用到alert、toast、HUD這些工具,如果沒有一套簡便的工具,會麻煩很多,所以便從輕量便捷角度出發,基於UIAlertView,封裝實現了alert、toast、HUD這些常用工具。

1468630-e99f5311021af24a.gif

JXTAlertView大致效果演示

1.1.快捷使用alertView

如果只是簡單的一個提示,可以這樣使用(這裡只是一個示例,詳細用法見源碼):
jxt_showAlertTitle(@"簡易調試使用alert,單按鈕,標題默認為“確定”");
其實現是基於:

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

這是常用的兩個以內的按鈕的alertView,也可以這樣使用:

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

針對於復雜的多按鈕的alertView,還是使用變參方式,按鈕響應,根據添加的按鈕標題的index號依序區分:

[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.簡單的toast

這裡的toast提示,有別於傳統意義上的toast,因為其是基於alertView實現的,是一個沒有按鈕的alertView。可自定義展示延時時間,支持關閉回調的配置。

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

1.3.三種HUD的實現

這裡的HUD區別於toast之處在於,其關閉時機可控,並不是單純的一個延時展示。
三種HUD是指單純的文字型、帶風火輪(菊花)的加載窗、帶進度條的加載窗。
後兩者用KVC的方式訪問了alertView的私有屬性accessoryView實現,這樣做可能沒有太大問題,不過還是不建議線上開發使用,而且利用這種方式訪問私有屬性本來就是不太安全的,一旦key(私有屬性名)改變了,不做容錯處理,會崩潰,源碼實現中做了一定的容錯,但是,一旦對應key變化,也就導致對應功能失效了。

示例代碼(C函數方式):

jxt_showLoadingHUDTitleMessage(@"title", @"message");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{  
  jxt_dismissHUD();
});

HUD還有對應的簡易的顯示加載成功失敗狀態的方法,以及刷新進度條進度值的方法,詳情見Demo。

2. JXTAlertController(iOS8)(鏈式語法的“隱患”)

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

1468630-b1e71fb54160106c.gif

JXTAlertController大致效果演示

2.1.結構說明

/**
 JXTAlertController: show-alert(iOS8)

 @param title             title
 @param message           message
 @param appearanceProcess alert配置過程
 @param actionBlock       alert點擊響應回調
 */
- (void)jxt_showAlertWithTitle:(nullable NSString *)title
                       message:(nullable NSString *)message
             appearanceProcess:(JXTAlertAppearanceProcess)appearanceProcess
                  actionsBlock:(nullable JXTAlertActionBlock)actionBlock NS_AVAILABLE_IOS(8_0);

上述方法是針對UIViewController做的分類擴展,詳見源碼。
也就是在某個VC中使用時,可直接用self指針調用。
JXTAlertAppearanceProcess是配置塊,JXTAlertActionBlock是按鈕響應回調塊。

2.2.鏈式語法添加按鈕

[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是當前alertController對象,addActionCancelTitle(@"cancel")是添加一個按鈕,其等效於:

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

}]];

這裡引入了簡單的鏈式語法,可以連續添加系統支持的三類action按鈕,當然UIAlertActionStyleCancel這個樣式的action只能添加一次。這樣可以大大簡化代碼。

actionsBlock是action按鈕響應回調,可以根據index區分響應,index根據執行add的語法鏈從0依序增加,cancel類型的action布局位置是固定的,和添加順序無關,但其index與添加順序有關。
對於復雜或者特殊的alertController,也可以根據action.title或者action區分響應。

2.3.鏈式語法的“隱患”

用過Masonry這個庫的,應該都對鏈式語法不會太陌生。鏈式語法可以使得代碼簡化且邏輯清晰化。但是,其也有一定的“隱患”存在。
Masonry應該是用的最多的一個自動布局的三方庫,類似的還有SDAutoLayout(這裡只是舉例,同樣的三方還有很多,這個應該是除了Masonry外,用的相對多一些的一個)這樣的,同樣的鏈式語法,後者似乎更加簡潔優雅。那為什麼大名鼎鼎的Masonry不這麼干呢?我想是因為“安全”。

用SDAutoLayout的Demo做一個實驗:

1468630-1ab34f91d23640d2.png

view為nil導致的崩潰

這裡把view0置為nil,之後運行,程序直接崩潰了。。。這類似於執行一個未賦值的空block。
有人可能會認為這樣的實驗沒有意義,為nil了干嘛還要布局呢?其實這是筆者前陣子在封裝一個鏈式庫時遇到的問題:實際應用開發中,情況要復雜很多,有些view是動態添加的,甚至是根據接口數據動態創建的,如果在處理這類代碼邏輯中稍有不慎,就會造成上述問題,給不存在的view進行布局,直接導致程序崩潰。。。
其實這也是代碼書寫規范的問題,針對這類動態view,在處理時,本就應該添加if條件判斷的,不過有時容易忽視,或者他人接手相關代碼時,也容易忽略。如果用Masonry的塊配置布局,就不會發生這類問題,因為這種情況,對於Masonry那種寫法,就是一個空指針執行一個方法,其結果就是不執行,而像SDAutoLayout這類的,不作判空處理,就會導致程序崩潰。這裡尤其要注意。

2.4.其他配置

針對alert上的輸入框,保持系統的添加方式,示例如下:

[self jxt_showAlertWithTitle:@"title"
                     message:@"message"
           appearanceProcess:^(JXTAlertController * _Nonnull alertMaker) {
    alertMaker.
    addActionDestructiveTitle(@"獲取輸入框1").
    addActionDestructiveTitle(@"獲取輸入框2");
    
    [alertMaker setAlertDidShown:^{
        [self logMsg:@"alertDidShown"];//不用擔心循環引用
    }];
    alertMaker.alertDidDismiss = ^{
        [self logMsg:@"alertDidDismiss"];
    };
    
    [alertMaker addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.placeholder = @"輸入框1-請輸入";
    }];
    [alertMaker addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.placeholder = @"輸入框2-請輸入";
    }];
} actionsBlock:^(NSInteger buttonIndex, UIAlertAction * _Nonnull action, JXTAlertController * _Nonnull alertSelf) {
    if (buttonIndex == 0) {
        UITextField *textField = alertSelf.textFields.firstObject;
        [self logMsg:textField.text];//不用擔心循環引用
    }
    else if (buttonIndex == 1) {
        UITextField *textField = alertSelf.textFields.lastObject;
        [self logMsg:textField.text];
    }
}];

對於alert展示和關閉的回調,同樣支持以block的方式配置。

如果appearanceProcess塊不進行任何配置操作,即無按鈕的alert,同樣默認以toast模式處理。可通過設置toastStyleDuration屬性,配置toast展示延遲時間。

2.5.改變alertController的字體顏色

可以通過KVC的方式訪問alertController的私有屬性,從而進行修改對應的字體的顏色,甚至字體。
對於UIAlertAction,可以用下面的方式修改字體顏色:

[alertAction setValue:[UIColor grayColor] forKey:@"titleTextColor"];

44.jpg

修改UIAlertAction字體顏色的效果

但是就像前面說的,個人並不推薦這類方式,所以源碼中沒有提供相關的方法。
如果有對應的特殊需求,總體來說,還是自定義alert視圖比較靈活,網上相關的開源庫也有很多可以直接使用,不必總是糾結於系統的實現方式。

最後,歡迎使用JXTAlertManager,如果遇到任何問題,請及時聯系作者。

參考文章:

  • iOS更改UIActionController彈出字體的樣式

  • UIAlertController 簡單修改title以及按鈕的字體顏色

  • How to add subview inside UIAlertView for iOS 7?

  • UIAlertView addSubview in iOS7

  • iOS UIAlertView中UIActivityindicatorView風火輪提示加載等待

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved