你知道 UIAlertViewUIActionSheet (以及它们各自的 delegate protocols) 在 iOS 8 中已经被废弃了吗?

这是真的。在你的代码中按住 ⌘ 点击 UIAlertView 或者 UIActionSheet,你就会看到最上面的注释:

UIAlertView is deprecated. Use UIAlertController with a preferredStyle ofUIAlertControllerStyleAlert instead.

你可能好奇为什么 Xcode 不直接警告你这个变化呢?别急,往下读:

Swift

@availability(iOS, introduced=2.0)

虽然类已经被废弃了,但在 @availability 属性中并没有表达出这一点。UIAlertView 目前还是能用的。

最开始的时候,UIAlertView 充满了无底线的让步,牺牲格式和设计正确性来顺应开发者的喜好。它的 delegate 类型是在初始化函数中注释的 (delegate:(id /* <UIAlertViewDelegate */)delegate),并且在 protocol 方法中实现了让人匪夷所思的概念——buttonAtIndex: 的 "clicked" 而不是 "tapped";然后还提供了不限数量的参数来引入 otherButtonTitles,因此 button 的索引管理变得非常痛苦;一个 -show 方法也根本不考虑 view 的层级关系......类似的问题数不胜数。

UIActionSheet 也一样糟糕,但是开发者大多数时间里都没时间想起那些糟糕的使用方法,与其相关的抱怨特就更少了。

因此,介绍 UIAlertController 给大家,就好比派出军队解放一座被占领的城市一样。它不仅仅改进了那些让人头疼的 API,也开辟了一条为最新设备上 UIKit 特性适配的新路径。

本周文章的主题就是 UIAlertController,向大家展示如何替换旧的 alert,以及这些操作方法的高级扩展。


UIAlertController 同时替代了 UIAlertView 和 UIActionSheet,从系统层级上统一了 alert 的概念 —— 即以 modal 方式或 popover 方式展示。

UIAlertController 是 UIViewController 的子类,而非其先前的方式。因此新的 alert 可以由 view controller 展示相关的配置中获益很多。

UIAlertController 不管是要用 alert 还是 action sheet 方式展示,都要以 title 和 message 参数来初始化。Alert 会在当前显示的 view controller 中心以模态形式出现,action sheet 则会在底部滑出。Alert 可以同时有按钮和输入框,action sheet 仅支持按钮。

新的方式并没有把所有的 alert 按钮配置都放在初始化函数中,而是引入了一个新类 UIAlertAction 的对象,在初始化之后可以进行配置。这种形式的 API 重构让对按钮数量、类型、顺序方便有了更大的控制。同时也弃用了 UIAlertView 和 UIActionSheet 使用的delegate 这种方式,而是采用更简便的完成时回调。

新旧 Alert 方式对比

标准的 Alert 样式

旧方法:UIAlertView

Swift

let alertView = UIAlertView(title: "Default Style", message: "A standard alert.", delegate: self, cancelButtonTitle: "Cancel", otherButtonTitles: "OK")
alertView.alertViewStyle = .Default
alertView.show() // MARK: UIAlertViewDelegate func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int) {
switch buttonIndex {
// ...
}
}

新方法:UIAlertController

Swift

let alertController = UIAlertController(title: "Default Style", message: "A standard alert.", preferredStyle: .Alert)

let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action) in
// ...
}
alertController.addAction(cancelAction) let OKAction = UIAlertAction(title: "OK", style: .Default) { (action) in
// ...
}
alertController.addAction(OKAction) self.presentViewController(alertController, animated: true) {
// ...
}

标准的 Action Sheet 样式

UIActionSheet

Swift

let actionSheet = UIActionSheet(title: "Takes the appearance of the bottom bar if specified; otherwise, same as UIActionSheetStyleDefault.", delegate: self, cancelButtonTitle: "Cancel", destructiveButtonTitle: "Destroy", otherButtonTitles: "OK")
actionSheet.actionSheetStyle = .Default
actionSheet.showInView(self.view) // MARK: UIActionSheetDelegate func actionSheet(actionSheet: UIActionSheet, clickedButtonAtIndex buttonIndex: Int) {
switch buttonIndex {
...
}
}

UIAlertController

Swift

let alertController = UIAlertController(title: nil, message: "Takes the appearance of the bottom bar if specified; otherwise, same as UIActionSheetStyleDefault.", preferredStyle: .ActionSheet)

let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action) in
// ...
}
alertController.addAction(cancelAction) let OKAction = UIAlertAction(title: "OK", style: .Default) { (action) in
// ...
}
alertController.addAction(OKAction) let destroyAction = UIAlertAction(title: "Destroy", style: .Destructive) { (action) in
println(action)
}
alertController.addAction(destroyAction) self.presentViewController(alertController, animated: true) {
// ...
}

新功能

UIAlertController 并不只是对已有的 API 做了清理,而是进行了标准化归纳。以前,预设的样式闲置有很多(swizzling 虽然可以提供更多的功能但还是有很大风险)。UIAlertController 让以前看起来很神奇的事情变为了可能。

UIAlertController is not just a cleanup of pre-existing APIs, it's a generalization of them. Previously, one was constrained to whatever presets were provided (swizzling in additional functionality at their own risk). With UIAlertController, it's possible to do a lot more out-of-the-box:

带有警示按钮的 Alert

这种行为已经被 UIAlertActionStyle 所涵盖,共有三种类型:

  • .Default: 对按钮应用标准样式。
  • .Cancel: 对按钮应用取消样式,代表取消操作不做任何改变。
  • .Destructive: 对按钮应用警示性的样式,提示用户这样做可能会改变或删除某些数据。

所以想要对模态的 alert 加一个警示性的按钮,只需要加上 .Destructive 风格的 UIAlertAction 属性:

Swift

let alertController = UIAlertController(title: "Title", message: "Message", preferredStyle: .Alert)

let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action) in
println(action)
}
alertController.addAction(cancelAction) let destroyAction = UIAlertAction(title: "Destroy", style: .Destructive) { (action) in
println(action)
}
alertController.addAction(destroyAction) self.presentViewController(alertController, animated: true) {
// ...
}

大于 2 个按钮的 Alert

有 1 个或者 2 个操作的时候,按钮会水平排布。更多按钮的情况,就会像 action sheet 那样展示:

Swift

let oneAction = UIAlertAction(title: "One", style: .Default) { (_) in }
let twoAction = UIAlertAction(title: "Two", style: .Default) { (_) in }
let threeAction = UIAlertAction(title: "Three", style: .Default) { (_) in }
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (_) in } alertController.addAction(oneAction)
alertController.addAction(twoAction)
alertController.addAction(threeAction)
alertController.addAction(cancelAction)

创建登录表单

iOS 5 就为 UIAlertView 加入了 alertViewStyle 属性,将原本私有的 API 暴露出来给开发者使用 —— 像某些系统内建应用一样允许在 alert 中显示登录和密码框。

在 iOS 8 中,UIAlertController 则加入了 addTextFieldWithConfigurationHandler 方法:

Swift

let loginAction = UIAlertAction(title: "Login", style: .Default) { (_) in
let loginTextField = alertController.textFields![0] as UITextField
let passwordTextField = alertController.textFields![1] as UITextField login(loginTextField.text, passwordTextField.text)
}
loginAction.enabled = false let forgotPasswordAction = UIAlertAction(title: "Forgot Password", style: .Destructive) { (_) in }
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (_) in } alertController.addTextFieldWithConfigurationHandler { (textField) in
textField.placeholder = "Login" NSNotificationCenter.defaultCenter().addObserverForName(UITextFieldTextDidChangeNotification, object: textField, queue: NSOperationQueue.mainQueue()) { (notification) in
loginAction.enabled = textField.text != ""
}
} alertController.addTextFieldWithConfigurationHandler { (textField) in
textField.placeholder = "Password"
textField.secureTextEntry = true
} alertController.addAction(loginAction)
alertController.addAction(forgotPasswordAction)
alertController.addAction(cancelAction)

创建注册表单

UIAlertController 想得比以前更周到一些,它提供了展示无限个输入框的权限,并且每一个输入框都可以根据需求进行自定义。这让仅在一个模态的 alert 中实现完善的注册功能成为可能:

Swift

alertController.addTextFieldWithConfigurationHandler { (textField) in
textField.placeholder = "Email"
textField.keyboardType = .EmailAddress
} alertController.addTextFieldWithConfigurationHandler { (textField) in
textField.placeholder = "Password"
textField.secureTextEntry = true
} alertController.addTextFieldWithConfigurationHandler { (textField) in
textField.placeholder = "Password Confirmation"
textField.secureTextEntry = true
}

虽然有了这些,但必须要说明的是,不用矫枉过正。不能因为你可以这样做,就代表你应该这样做。忘了这玩意吧,提供一个 view controller 来做注册功能吧,因为你本就应该这样做!

注意

如果试图向 alert controller 添加带有 .ActionSheet 属性的输入框,将会抛出异常:

Terminating app due to uncaught exception NSInternalInconsistencyException, reason: 'Text fields can only be added to an alert controller of styleUIAlertControllerStyleAlert'

同样的,向 alert 或 action sheet 添加一个以上的 .Cancel 按钮将会抛出异常:

Terminating app due to uncaught exception NSInternalInconsistencyException, reason: 'UIAlertController can only have one action with a style ofUIAlertActionStyleCancel'

IOS之UIAlert​Controller的更多相关文章

  1. iOS Container View Controller

    一.UIViewController 做iOS开发的经常会和UIViewController打交道,从类名可知UIViewController属于MVC模型中的C(Controller),说的更具体点 ...

  2. IOS Note - View Controller(视图控制器)

    Application Delegate(应用程序委托) Application Name: SingleView SingleViewAppDelegate.h #import <UIKit/ ...

  3. 唐巧的iOS技术博客选摘

    1. 那些被遗漏的objective-c保留字:http://blog.devtang.com/blog/2013/04/29/the-missing-objc-keywords/   2. 使用cr ...

  4. 《iOS开发全然上手——使用iOS 7和Xcode 5开发移动与平板应用》之Objective-C新手训练营

    编写Hello World应用程序通常被觉得,是学习不论什么编程语言的第一步.在这一章,你将创建iOS版的Hello World应用程序作为起步,高速了解Xcode这个开发iOS应用程序的主要工具. ...

  5. iOS 中关于ViewController总结

    以前写程序时,经常被旋转问题弄的头疼,今天为了解决这个问题,偶然看到了苹果官方文档 View Controller Programming Guide for iOS. 这才发现这个必读的资料!以前许 ...

  6. 【IOS笔记】About Events in iOS

    About Events in iOS Users manipulate their iOS devices in a number of ways, such as touching the scr ...

  7. 《iOS 11 安全区域适配总结》

    本文来自于腾讯Bugly公众号(weixinBugly),作者:sonialiu,未经作者同意,请勿转载,原文地址:http://mp.weixin.qq.com/s/W1_0VrchCO50owhJ ...

  8. iOS移动开发周报-第17期

    lhq iOS移动开发周报-第17期 前言 欢迎国内的iOS同行或技术作者向我提交周报线索,线索可以是新闻.教程.开发工具或开源项目,将相关文章的简介和链接在微博上发布并 @唐巧_boy 即可. [摘 ...

  9. Event Handling Guide for iOS--(一)--About Events in iOS

    About Events in iOS Users manipulate their iOS devices in a number of ways, such as touching the scr ...

随机推荐

  1. 《Linux内核修炼之道》精华分享与讨论(5)——Kernel地图:Kconfig与Makefile

    转自:http://blog.csdn.net/fudan_abc/article/details/5340408 Makefile不是Make Love 从前在学校,混了四年,没有学到任何东西,每天 ...

  2. 极客时间_Vue开发实战_06.Vue组件的核心概念(2):事件

    06.Vue组件的核心概念(2):事件 通过emit传递给父组件 我们点击了重置失败,上层的div的click=handleDivClick是接收不到.重置失败的点击的行为的 通常情况下,你不用.st ...

  3. 2-1赋值运算符 & 2-2自增自减运算符 &2-3

    2-1赋值运算符 先定义一个变量,把定义好的变量在赋值给另外一个变量.变向之间的互相赋值 2-2自增自减运算符 元素符,放在变量前和变量后的区别 先进行自增运算,再进行赋值运算.这里先进行num1的+ ...

  4. UVaLive 3902 Network (无根树转有根树,贪心)

    题意:一个树形网络,叶子是客户端,其他的是服务器.现在只有一台服务器提供服务,使得不超k的客户端流畅,但是其他的就不行了, 现在要在其他结点上安装服务器,使得所有的客户端都能流畅,问最少要几台. 析: ...

  5. MFC的CString使用

    CString没有基类.一个CString对象由可变长度的一队字符组成.CString使用类似于Basic的语法提供函数和操作符.连接和比较操作符以及简化的内存管理使CString对象比普通字符串数组 ...

  6. 一种Unity2D多分辨率屏幕适配方案

    http://www.cnblogs.com/flyFreeZn/p/4073655.html 此文将阐述一种简单有效的Unity2D多分辨率屏幕适配方案,该方案适用于基于原生开发的Unity2D游戏 ...

  7. bzoj 4464: [Jsoi2013]旅行时的困惑【贪心】

    据说正解是有上下界最小流,但是这种1e5的玩意问什么要跑网络流啊-- 贪心即可,注意一点是可以有多条路径经过一条边-- 以1为根,设d[u][0/1]为u到父亲的边是向下/向上,g记录这个点儿子中不能 ...

  8. 2014-7-7 NOIP模拟赛(图论)

    1.无线通讯网(wireless.pas/cpp/c) [题目描述] 国防部计划用无线网络连接若干个边防哨所.2种不同的通讯技术用来搭建无线网络:每个边防哨所都要配备无线电收发器:有一些哨所还可以增配 ...

  9. css 的继承性

    目录 css 的继承性是什么? 父元素的属性那些可以被子元素继承,哪些不能呢? css 的继承性是什么? 在面向对象语言都会存在继承的概念,在面向对象语言中,继承的特点:继承了父类的属性和方法. 那么 ...

  10. C#求圆的周长、面积、体积

    窗体应用程序 private void button1_Click(object sender, EventArgs e) { double r; r = Convert.ToInt32(textBo ...