UIAlert​Controller

本文介绍了iOS8中UIAlertView和UIActionSheet已被废弃的事实,解释了Xcode为何未直接警告此变更。接着,文章详细阐述了引入UIAlertController的原因和优势,包括统一alert概念、改进API设计、适配最新设备的UIKit特性等。最后,通过对比旧新方法,展示了如何用UIAlertController替换旧alert,并介绍了新功能如警示按钮、多个按钮、创建登录和注册表单等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

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

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

@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 样式

A Standard Alert

旧方法:UIAlertView
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
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 样式

A Standard Action Sheet

UIActionSheet
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
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

Alert with Destructive Button

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

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

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

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

Alert with More Than 2 Buttons

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

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)

创建登录表单

Creating a Login Form

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

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

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)

创建注册表单

Creating a Sign Up Form

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

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'

<think>好的,我现在需要回答用户关于Spring MVC中DispatcherServlet、HandlerAdapter和Controller之间关系的问题。用户的具体问题是:DispatcherServlet管理多少个HandlerAdapter,以及每个HandlerAdapter管理多少个Controller。我需要结合提供的引用内容以及自己的知识来构建一个准确的回答。 首先,根据用户提供的引用[3],DispatcherServlet是实现Servlet接口的类,负责处理请求的逻辑。引用[1]提到DispatcherServlet默认会加载所有实现了HandlerMapping接口的bean,但用户的问题是关于HandlerAdapter的,所以可能需要更多的内部知识。 我知道,在Spring MVC中,DispatcherServlet并不直接处理请求,而是委托给HandlerAdapter来处理。HandlerAdapter的作用是解耦DispatcherServlet和具体的控制器(Controller),因为不同的控制器可能有不同的处理方式,比如基于注解的@Controller、传统的Controller接口实现,或者其他类型的处理器如HttpRequestHandler。 接下来,用户问DispatcherServlet管理多少个HandlerAdapter。根据Spring MVC的默认配置,通常会有多个HandlerAdapter实例注册到应用上下文中。例如,常用的有RequestMappingHandlerAdapter(处理基于@RequestMapping注解的方法)、HttpRequestHandlerAdapter(处理HttpRequestHandler接口的实现)、SimpleControllerHandlerAdapter(处理实现了Controller接口的类)等。默认情况下,DispatcherServlet会检测并加载所有存在的HandlerAdapter bean,但引用[1]提到可以通过设置detectAllHandlerMappings参数为false来改变这一行为,不过这里用户可能混淆了HandlerMapping和HandlerAdapter,不过机制类似,可能HandlerAdapter的加载也是类似的。 因此,DispatcherServlet管理的HandlerAdapter的数量取决于应用上下文中注册了多少种HandlerAdapter的实现。默认情况下,Spring MVC会自动注册几种常用的HandlerAdapter,所以通常会有多个,但具体数量由配置决定。 然后是每个HandlerAdapter管理多少个Controller的问题。HandlerAdapter并不直接“管理”Controller,而是负责调用适合的Controller。例如,RequestMappingHandlerAdapter会处理所有使用@Controller注解并通过@RequestMapping映射的控制器方法;而SimpleControllerHandlerAdapter则处理实现了Controller接口的实例。因此,每个HandlerAdapter负责处理一类特定的Controller,具体的Controller数量取决于应用中定义了多少个该类型的Controller。 需要确认的是,HandlerAdapter和Controller之间是一对多的关系,但每个HandlerAdapter负责一种类型的Controller。例如,所有使用@Controller注解的控制器方法都由RequestMappingHandlerAdapter处理,而每个这样的方法可能被视为一个处理单元,但Controller类本身可能包含多个方法。 另外,引用[2]提到了DispatcherServlet处理请求的doPost方法,这涉及到processRequest方法,可能在这个过程中,DispatcherServlet会选择合适的HandlerAdapter来执行实际的控制器处理。 总结回答的结构:首先解释DispatcherServlet如何管理多个HandlerAdapter,默认情况下加载所有实现,数量取决于配置。然后说明每个HandlerAdapter负责特定类型的ControllerController的数量由应用中的定义决定。需要引用提供的资料中的相关部分,例如引用[1]中的detectAllHandlerMappings的设置可能类比到HandlerAdapter的加载机制,但需要谨慎,因为用户提供的引用可能不完全对应,但可以作为参考。 还需要生成相关问题,可能需要根据Spring MVC的组件交互来设计问题,例如HandlerMapping的作用,如何自定义HandlerAdapter等。 最后,确保使用正确的中文表达,行内公式用$...$,独立公式用$$...$$,但在这个问题中可能不需要数学公式,但需要遵守其他格式要求,如代码块和引用标记。</think>在Spring MVC框架中,`DispatcherServlet`、`HandlerAdapter`和`Controller`的协作关系如下: ### 1. DispatcherServlet与HandlerAdapter的关系 `DispatcherServlet`是前端控制器,负责请求的统一分发,但它不直接调用控制器方法,而是通过`HandlerAdapter`接口实现**适配调用**。 - **默认情况下**,`DispatcherServlet`会加载应用上下文中所有实现了`HandlerAdapter`接口的Bean[^1]。 - **常见HandlerAdapter类型**: - `RequestMappingHandlerAdapter`:处理基于`@Controller`和`@RequestMapping`注解的控制器。 - `HttpRequestHandlerAdapter`:处理`HttpRequestHandler`接口的实现类(如静态资源处理)。 - `SimpleControllerHandlerAdapter`:处理传统`Controller`接口的实现类。 - **管理数量**:Spring MVC默认注册多个`HandlerAdapter`,具体数量取决于应用配置和使用的技术栈(如是否启用WebFlux)。 ### 2. HandlerAdapter与Controller的关系 `HandlerAdapter`的作用是**解耦DispatcherServlet与具体Controller的实现方式**: - **一对多映射**:一个`HandlerAdapter`可以适配**一类Controller**。例如: - `RequestMappingHandlerAdapter`适配所有`@Controller`类中通过`@RequestMapping`定义的方法。 - `HttpRequestHandlerAdapter`适配所有实现`HttpRequestHandler`的Bean。 - **Controller数量**:每个`HandlerAdapter`管理的Controller数量取决于应用中定义的对应类型Controller的数量。例如,若应用有10个`@Controller`类,则`RequestMappingHandlerAdapter`会适配这10个类中的所有请求处理方法。 ### 代码示例:HandlerAdapter调用逻辑 ```java // DispatcherServlet通过HandlerAdapter执行Controller方法 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); ModelAndView mv = ha.handle(request, response, mappedHandler.getHandler()); ``` ### 关键流程总结 1. `DispatcherServlet`根据请求找到对应的`HandlerMapping`(如`RequestMappingHandlerMapping`)。 2. 通过`HandlerAdapter`执行具体的Controller方法,适配不同控制器类型[^3]。 3. 返回`ModelAndView`结果并渲染响应。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值