iOS 8的新特性之一就是让接口更有适应性、更灵活,因此许多视图控制器的实现方式发生了巨大的变化,并且在某些旧的UIKit控件也同样发生了许多变化,往统一的方向发展,比如说Alert Views、Action Sheets、Popovers以及Search Bar Controllers。这里将会对Alert Views和Action Sheets发生的改变进行一个大致的介绍.
UIAlertView
随着苹果上次iOS 5的发布,对话框视图样式出现在了我们面前,直到现在它都没有发生过很大的变化。下面的代码片段展示了如何初始化和显示一个带有“取消”和“好的”按钮的对话框视图。
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"我是标题" message:@"我是传递的消息正文" delegate:self cancelButtonTitle:@"取消按钮" otherButtonTitles:@"确定按钮", nil];
[alertView show];
展示效果
并且通过一系列的代理方法来处理按钮点击的事件
也可以通过更改UIAlertView的alertViewStyle属性来实现输入文字、密码,登录框的效果。
typedef NS_ENUM(NSInteger, UIAlertViewStyle) {
UIAlertViewStyleDefault = 0, //默认效果
UIAlertViewStyleSecureTextInput,//单行密文文本输入框
UIAlertViewStylePlainTextInput,//单行普通文本输入框
UIAlertViewStyleLoginAndPasswordInput//登录输入框
}
单行输入框效果
alertView.alertViewStyle = UIAlertViewStylePlainTextInput;
单行secure entry
alertView.alertViewStyle = UIAlertViewStyleSecureTextInput;
双行登录框效果
alertView.alertViewStyle = UIAlertViewStyleLoginAndPasswordInput;
通过
- (nullable UITextField *)textFieldAtIndex:(NSInteger)textFieldIndex ;
这个方法可以拿到在index位置的文本输入框. 从而监听文本输入框的改变
[alertView textFieldAtIndex:0];
[alertView textFieldAtIndex:1];
UIActionSheet
展示效果是从屏幕的底部往上钻出1个可点击的表格,对于这个控件的使用与UIAlertView 基本类似
UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"我是标题" delegate:self cancelButtonTitle:@"取消按钮" destructiveButtonTitle:@"确定按钮" otherButtonTitles:@"关闭", nil];
[sheet showInView:self.view];
也有一系列的代理方法来处理点击按钮事件
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex;
- (void)actionSheetCancel:(UIActionSheet *)actionSheet;
- (void)willPresentActionSheet:(UIActionSheet *)actionSheet ;
- (void)didPresentActionSheet:(UIActionSheet *)actionSheet;
- (void)actionSheet:(UIActionSheet *)actionSheet willDismissWithButtonIndex:(NSInteger)buttonIndex;
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex;
要说明一点,苹果官方现在并不提倡在iOS 8后中使用UIAlertView(9.0废弃)和UIActionSheet(8.3废弃),取而代之的是UIAlertController。下面我们就来介绍UIAlertController的使用方法
UIAlertController
UIAlertController以一种模块化替换的方式来代替上面两个控件的功能和作用。是使用弹出对话框(alert)还是使用向上钻出的菜单(action sheet),就取决于在创建控制器时,是如何设置首选样式的。
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"我是标题" message:@"我是要传递的消息" preferredStyle:UIAlertControllerStyleActionSheet];
preferredStyle的枚举,通过指定样式来确定是以什么方法展示
typedef NS_ENUM(NSInteger, UIAlertControllerStyle) {
UIAlertControllerStyleActionSheet = 0,
UIAlertControllerStyleAlert
}
可以比较一下两种不同的创建对话框的代码,创建基础UIAlertController的代码和创建UIAlertView的代码非常相似:
由于创建出来的AlertController 变成了1个控制器 所以要使用modal的方式将控制器展示出来
[self presentViewController:alertController animated:YES completion:nil];
如果仅仅是上面的两句代码 同创建UIAlertView相比,我们没有指定代理,也没有在初始化过程中指定按钮。那么控制器弹出后 没有任何按钮和文本输入框. 不过要特别注意第三个参数,要确定选择的是对话框样式还是向上钻的菜单样式
接下来要往alertController中添加按钮或者文本输入框,通过创建UIAlertAction的实例,可以将动作按钮添加到控制器上。UIAlertAction由标题字符串、样式以及当用户选中该动作时运行的代码块组成。通过UIAlertActionStyle,您可以选择如下三种动作样式:常规(default)、取消(cancel)以及破坏性的,警示效果(destruective)[点击这个按钮一般要造成改变,所以按钮的样式系统默认会变为红色]。为了实现原来我们在创建UIAlertView时创建的按钮效果,我们只需创建两个动作按钮并将它们添加到控制器上即可。
按钮
typedef NS_ENUM(NSInteger, UIAlertActionStyle) {
UIAlertActionStyleDefault = 0,//默认
UIAlertActionStyleCancel,//取消 唯一
UIAlertActionStyleDestructive//破坏性,警示效果按钮文字变成了红色。根据苹果官方的定义,“警示”样式的按钮是用在可能会改变或删除数据的操作上。因此用了红色的醒目标识来警示用户。
}
UIAlertAction *CancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
}];
UIAlertAction *action2 = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
}];
样式如下
按钮显示的次序取决于它们添加到对话框控制器上的次序。一般来说,在拥有两个按钮的对话框中,您应当将取消按钮放在左边。要注意,取消按钮是唯一的,如果您添加了第二个取消按钮,那么你就会得到如下的一个运行时异常:
Terminating app due to uncaught exception
‘NSInternalInconsistencyException’, reason: ‘UIAlertController can only have one action with a style of UIAlertActionStyleCancel’
文本对话框
UIAlertController极大的灵活性意味着您不必拘泥于内置样式。以前我们只能在默认视图、文本框视图、密码框视图、登录和密码输入框视图中选择,现在我们可以向对话框中添加任意数目的UITextField对象,并且可以使用所有的UITextField特性。当您向对话框控制器中添加文本框时,您需要指定一个用来配置文本框的代码块
要重新建立原来的登录和密码样式对话框,我们可以向其中添加两个文本框,然后用合适的占位符来配置它们,最后将密码输入框设置使用安全文本输入。例如
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"我是标题" message:@"我是要传递的消息" preferredStyle:UIAlertControllerStyleActionSheet];
//取消按钮
UIAlertAction *CancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
}];
//警示按钮
UIAlertAction *doneAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
}];
//文本输入框
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
//配置文本框的代码块
textField.placeholder = @"登录";
textField.textColor = [UIColor greenColor];
}];
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = @"密码";
textField.secureTextEntry = YES;
}];
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = @"打酱油的";
}];
[alertController addAction:CancelAction];
[alertController addAction:doneAction];
[self presentViewController:alertController animated:YES completion:nil];
上面的例子添加了三个文本输入框 那么显示的效果就会变成这样
但是如果试图向样式为actionSheet的alertController添加输入框,将会抛出异常:
Terminating app due to uncaught exception:
NSInternalInconsistencyException, reason: 'Text fields can only be added to an alert controller of style UIAlertControllerStyleAlert'
在很多情况下 当我们点击确定按钮之后希望拿到文本框的值然后做一些事情.那么我们可以这样来操作
在 doneAction 创建的时候在代码块里面添加下面的代码 当点击确定按钮的时候 在控制台就会打印 在textField输入的值
UIAlertAction *doneAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"%@--%@",alertController.textFields.firstObject.text,alertController.textFields.lastObject.text);
}];
要注意的是,以上的写法是有问题的..由于在创建 doneAction 的时候 在block 里面 引用了 alertController 之后再将doneAction 添加到 alertController 里面. 那么将会产生循环引用导致alertController无法销毁.
所以此时应该这样做
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"我是标题" message:@"我是要传递的消息" preferredStyle:UIAlertControllerStyleAlert];
//避免产生循环引用问题
__weak typeof(alertController) weakAlert = alertController;
UIAlertAction *doneAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"%@--%@",weakAlert.textFields.firstObject.text,weakAlert.textFields.lastObject.text);
}];
........................
[alertController addAction:doneAction];
[self presentViewController:alertController animated:YES completion:nil];
如果需要监听文本输入框输入的改变.可以使用两种方式
- 1 使用通知(需要在alertView 销毁的时候 ,移除通知 ,即在每个按钮动作的handler代码块中添加合适的代码来移除).
doneAction.enabled = NO;
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = @"登录";
textField.textColor = [UIColor greenColor];
//注册通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(accountTextFieldChange:) name:UITextFieldTextDidChangeNotification object:textField];
}];
- (void)accountTextFieldChange:(NSNotification *)notification{
//在这里拿到文本框
}
//移除通知
__weak typeof(alertController) weakAlert = alertController;
UIAlertAction *doneAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"%@--%@",weakAlert.textFields.firstObject.text,weakAlert.textFields.lastObject.text);
[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidChangeNotification object:nil];
}];
- 2 使用addTarget(推荐使用这种方式.)
举个栗子:当文本输入框里面的内容长度>3的时候才让确定按钮能够点击.
首先要冻结 doneAction 按钮
doneAction.enabled = NO;
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = @"登录";
textField.textColor = [UIColor greenColor];
[textField addTarget:self action:@selector(accountTextFieldChange:) forControlEvents:UIControlEventEditingChanged];
- (void)accountTextFieldChange:(UITextField *)accountTextFiled{
//获取alertController
UIAlertController *alertController = (UIAlertController *)self.presentedViewController;
//拿到doneAction按钮
UIAlertAction * doneAction = alertController.actions[1];
//判断语句
if (accountTextFiled.text.length >= 3) {
doneAction.enabled = YES;
}