文档章节

iOS 更加优雅便捷的UIAlertView/UIAlertController封装使用

秦无炎
 秦无炎
发布于 2017/02/06 10:46
字数 2496
阅读 6
收藏 1

原文出处:霖溦

前言:

之前做过一套关于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

JXTAlertManager大体结构

1. JXTAlertView 便捷调试工具

之所以叫做快捷调试工具,就是因为这套代码是之前写Demo时搞出来的。所以,如果不是要适配iOS7及以下版本的话,这套代码还是建议只用在平时Demo测试。也因此,并没有针对UIActionSheet再进行封装,说白了是因为懒……
平时写一些Demo代码时,总会用到alert、toast、HUD这些工具,如果没有一套简便的工具,会麻烦很多,所以便从轻量便捷角度出发,基于UIAlertView,封装实现了alert、toast、HUD这些常用工具。

JXTAlertView大致效果演示

1.1.快捷使用alertView

如果只是简单的一个提示,可以这样使用(这里只是一个示例,详细用法见源码):
jxt_showAlertTitle(@"简易调试使用alert,单按钮,标题默认为“确定”");
其实现是基于:

1

2

3

4

5

[JXTAlertView showAlertViewWithTitle:title 

                             message:message 

                   cancelButtonTitle:cancelButtonTitle 

                    otherButtonTitle:otherButtonTitle                   cancelButtonBlock:cancelBlock 

                    otherButtonBlock:otherBlock];

这是常用的两个以内的按钮的alertView,也可以这样使用:

1

2

3

jxt_showAlertTwoButton(@"title"@"message"@"cancel", ^(NSInteger buttonIndex) {

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

    NSLog(@"other");});

针对于复杂的多按钮的alertView,还是使用变参方式,按钮响应,根据添加的按钮标题的index号依序区分:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

[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。可自定义展示延时时间,支持关闭回调的配置。

1

2

3

4

5

[JXTAlertView showToastViewWithTitle:@"title"

                             message:@"message"

                            duration:2

                   dismissCompletion:^(NSInteger buttonIndex) {

    NSLog(@"关闭");}];

1.3.三种HUD的实现

这里的HUD区别于toast之处在于,其关闭时机可控,并不是单纯的一个延时展示。
三种HUD是指单纯的文字型、带风火轮(菊花)的加载窗、带进度条的加载窗。
后两者用KVC的方式访问了alertView的私有属性accessoryView实现,这样做可能没有太大问题,不过还是不建议线上开发使用,而且利用这种方式访问私有属性本来就是不太安全的,一旦key(私有属性名)改变了,不做容错处理,会崩溃,源码实现中做了一定的容错,但是,一旦对应key变化,也就导致对应功能失效了。

  • 示例代码(C函数方式):

1

2

3

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同理。

JXTAlertController大致效果演示

2.1.结构说明

1

2

3

4

5

6

7

8

9

10

11

/**

 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.链式语法添加按钮

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

[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")是添加一个按钮,其等效于:

1

2

3

[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做一个实验:

view为nil导致的崩溃

这里把view0置为nil,之后运行,程序直接崩溃了。。。这类似于执行一个未赋值的空block。
有人可能会认为这样的实验没有意义,为nil了干嘛还要布局呢?其实这是笔者前阵子在封装一个链式库时遇到的问题:实际应用开发中,情况要复杂很多,有些view是动态添加的,甚至是根据接口数据动态创建的,如果在处理这类代码逻辑中稍有不慎,就会造成上述问题,给不存在的view进行布局,直接导致程序崩溃。。。
其实这也是代码书写规范的问题,针对这类动态view,在处理时,本就应该添加if条件判断的,不过有时容易忽视,或者他人接手相关代码时,也容易忽略。如果用Masonry的块配置布局,就不会发生这类问题,因为这种情况,对于Masonry那种写法,就是一个空指针执行一个方法,其结果就是不执行,而像SDAutoLayout这类的,不作判空处理,就会导致程序崩溃。这里尤其要注意。

2.4.其他配置

针对alert上的输入框,保持系统的添加方式,示例如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

[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"];

修改UIAlertAction字体颜色的效果

但是就像前面说的,个人并不推荐这类方式,所以源码中没有提供相关的方法。
如果有对应的特殊需求,总体来说,还是自定义alert视图比较灵活,网上相关的开源库也有很多可以直接使用,不必总是纠结于系统的实现方式。

最后,欢迎使用JXTAlertManager,如果遇到任何问题,请及时联系作者。

参考文章:
1.iOS更改UIActionController弹出字体的样式
2.UIAlertController 简单修改title以及按钮的字体颜色
3.How to add subview inside UIAlertView for iOS 7?
4.UIAlertView addSubview in iOS7
5.iOS UIAlertView中UIActivityindicatorView风火轮提示加载等待

本文转载自:

共有 人打赏支持
秦无炎
粉丝 4
博文 125
码字总数 6363
作品 0
朝阳
程序员
私信 提问
IOS开发框架 EasyIOS 2.2 版本更新

2.2 版本更新 修改Action类中的配置方式:由原来的宏调用改为类方法配置 针对IOS8优化 为UIScrollView增加下拉放大效果 新增EZNavigationController类,解决ios7中快速push容易crash的问题 ...

墨水
2015/01/04
6.7K
8
如何在iOS上实现Android风格的Toast

iOS的风格和Apple其他产品一样,简单而粗暴。没有给人其他选择的余地,让你又爱又恨。同样的,Apple对待iOS平台的开发人员和对待大众消费者一样,也不给你留余地。UIAlertView就是一个鲜明标...

鉴客
2011/11/21
829
0
UI_06 UIControl及其子类

⼀、UISegmentedControl的使⽤ 分段控件。每个segment都能被点击,相当于集成了若干个button。 通常我们会点击不同的segment来切换不同的view。 1、常⽤⽅法和属性 initWithItems: //UISegme...

黑伞将军
2015/08/21
0
0
General APIs mapping (通用 API mapping)

1、Android到Windows的API mappings 对于Android开发者,同样可以在API mapping 索引中找到映射,与使用iOS映射的方式一样。 我们提供了一个 API 映射索引来帮助你找到与 Windows 运行时 AP...

失足处男的倒霉孩子
2013/12/25
178
0
一步一步学习iOS 5编程(第三版)-PDF中文版-正式发布!

目前,这是第一本介绍iOS 5.x 和 Xcode 4.4 的中文版书籍,尤其适合于iOS 编程开发初学者。本教程由 EntLib.com 团队编写。如有任何技术问题,欢迎留言。 电子版 – PDF 格式,支持iPhone、i...

entlib
2012/09/20
0
2

没有更多内容

加载失败,请刷新页面

加载更多

JS 调用Angularjs 的方法

// 1. 获取 Controllerlet appElement = document.querySelector('[data-ng-controller=MessagesCtrl]');let scope = angular.element(appElement).scope();// 2. 调用方法scope.l......

Moks角木
32分钟前
1
0
dubbo+zookeeper与 eureka的区别

CAP CAP 原则指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得 在分布式架构里, P必须有 Zookeeper保证C P 当...

群星纪元
41分钟前
2
0
云计算之边缘计算大势所趋

如果说边缘计算是公同认定的目标,那么我们看到,不同类型的厂商基于自身的特点,会从不同的起点、沿着不同的路径,向这个目标奔跑。上次参加阿里云的一次活动,看到他们将边缘计算的厂商分成...

linuxCool
46分钟前
0
0
前端通过后端传过来的'\n' ,''等字符串换行失败问题

后台推送换行符 '\n' 或 '<br/>' 等字符串到前台不会换行 详细描述 后台逻辑处理返回String字符串,其中包含\n或<br/>等换行符号,但是前端渲染时候却并没有真正的换行 也尝试了大佬的各种 ...

下次用oschina
53分钟前
2
0
volatile能保证有序性吗?

在前面提到volatile关键字能禁止指令重排序,所以volatile能在一定程度上保证有序性。   volatile关键字禁止指令重排序有两层意思:   1)当程序执行到volatile变量的读操作或者写操作时...

无精疯
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部