委派指南-Swift 4
现实世界中的代表团
一个很好的起点是通过解释现实世界中的授权。 在现实世界中,委托封装了某人(委托人)将任务(委托人)交给其他人(委托人)。 有两个名词和一个动词组成委派:
- 委托(动词):“将任务或责任委托给另一个人”。
- 委托人(名词):“委托他人的人”。
- 代表(名词):“被选择或当选代表他人的人”。
为了澄清-委托人(名词)会将责任(动词)委托给委托人(名词)。 现在我们有了组成委派的组件的字典定义,接下来让我们实际展示一个真实的委派示例:
在仓库中,将有一名物流经理( 代理人 ),他们将知道卡车何时到达以及何时到达。 物流经理很忙,虽然他们很擅长管理物流,但在装卸和搬运箱子时却不那么擅长。 因此,他们将这项工作( 代表–动词 )交给仓库操作员( 代表–名词 )。 后勤经理告诉仓库操作员,一辆卡车将在09.00到达,当到达09.00时,他们告诉仓库操作员该卡车已经到达。 然后,仓库操作员卸下货车并将箱子运到需要的地方。
iOS世界中的代表团
数字世界中的委派与现实世界中的委派非常相似,不同之处在于,我们拥有的不是对象而不是人,而对象在Swift中将是类,结构或枚举的实例。 让我们以对现实世界相同的方式来分解软件开发中的委托:
- 委托(动词):将任务或责任委托给另一个对象。
- 委托人(名词):将职责“移交给”另一种类型的实例(对象)的类或结构实例(对象)。
- 委托(名词):保证处理已委派职责的对象,即提供委派的功能。
需要澄清的是,委托是指一个对象(代理)代表另一个对象(代理)提供功能时的行为。 现在,让我们通过从参与对象的角度来考虑委派来进一步深入研究:
您创建一个待办事项列表应用程序。 您可以将
UIViewController
子类化,以创建名为“ToDoListViewController
”( 委托–名词 )的视图控制器对象。 您将一个UITableView
实例( 委托人 )放在ToDoListViewController
。UITableView
会显示要执行的项目并检测与之交互的时间,但会将处理这些交互( 委托–动词 )的责任移交给另一个对象。UITableView
实例( delegator )将向事件的委托对象通知其将要处理或刚刚处理的事件。ToDoListViewController
( 委托人–名词 )声明它将提供响应UITableView处理的事件而发生的功能。假设
UITableView
实例( delegator )可以讲话,该对象可能会说:“ 我是一个表视图对象,并且选择了第3行 ”。 “ToDoListViewController
”( 代表–名词 )可能会说“已收到消息,我将引导用户到与UITableView
该行相对应的屏幕 。 ”
在iOS的世界中,委派还涉及另一个超级重要的元素-协议。 将在协议中定义将委派的属性和方法。 委托对象必须确认此“委托协议”,并在此过程中确保它将实现同意作为委托的功能。 Swift语言指南将协议的作用总结如下:
委托是一种设计模式,使类或结构可以将其某些职责移交给(或委托)其他类型的实例。 通过定义封装委托职责的协议来实现此设计模式,从而确保保证符合的类型(称为委托)提供已委托的功能 。 委托可用于响应特定操作,或从外部源检索数据而无需了解该源的基础类型。
下图中的安德鲁·班克罗夫特很好地捕捉了运动部件:
因此,我们对什么是委托有更多了解,但是要继续使委托变得生动起来,我们需要了解为什么我们在软件开发的背景下使用委托? 我们已经说过,我们使用委托将责任从一个对象“移交给”另一个对象,但是为什么要这样做呢? 建立这种职责分离有什么好处?
一个对象将责任委派给另一个对象的主要好处是,它允许委派对象具有通用性,即可以在许多不同的上下文中工作,而无需了解和处理特定的上下文本身,否则将导致庞大而紧密的工作。耦合类型。
以UITableView
为例。
想象一下我们正在构建待办事项列表应用程序。 待办事项列表屏幕将使用UITableView
。 用户将能够按下“ +”按钮添加项目,并滑动单元格以删除项目,这将发出“嘶哑”的声音。 该屏幕将由ToDoListViewController
管理。 设置屏幕还将使用UITableView
。 用户将能够看到设置选项,然后点击一个选项以导航到该设置的屏幕。 此屏幕将由SettingsViewController
管理。
现在,我们不想扩展UITableView
类来识别何时点按“ +”按钮,当从特定屏幕添加新项时发出噪音,或者在单元格被选中时将用户锁定在正确的屏幕上在设置屏幕上点击。 我们最终会得到一个巨大的UITableView
类,该类在许多用例中都有很多冗余功能。
相反,我们可以使用委托。 UITableView
可以传达已插入新行的信息,并允许ToDoListViewController
处理“嘶哑”的声音,或者已敲击行并让SettingsViewController
决定应将用户导航到何处。 UITableView类( 委托人–名词 )不需要知道父级UIViewController( 委托人–名词 )的特定逻辑或上下文。
约翰·桑德尔说得很好:
当类型需要在许多不同的上下文中使用并且在所有这些上下文中都有明确的所有者时,委托通常是一个不错的选择,就像
UITableView
经常由父容器视图或其视图控制器拥有一样。
委派被描述为“沟通设计模式”。 顾名思义,我们使用委托在类型对象之间进行通信。 那么什么样的交流呢?
首先,委派对象使用委派来传达事件已发生,然后委派代表决定如何处理该事件。 其次,有时委派对象提供允许委派对象自定义有关委派对象属性或状态的方法。
如果您阅读Apple的文档,则经常会看到委托方法要么“告诉”委托人,要么“询问”委托人。
交流活动
当UITableView
对象是委托者时,它可以向委托对象传达发生了许多事件:
func tableView(UITableView, didSelectRowAt : IndexPath
–告诉委托人现在已选择指定的行。
func tableView(UITableView, didEndEditingRowAt : IndexPath?)
–告诉委托人表视图已离开编辑模式。
func tableView(UITableView, didHighlightRowAt : IndexPath)
–告诉委托人突出显示了指定的行。
然后,委托(即ToDoListViewController
可以决定对此做出响应,即将用户引导到与所选单元格相对应的页面。
自定义委托对象
当UITableView
对象是委托者时,它可以要求委托者提供有关其应如何配置自身的信息,即,应使行高度为多少,或者是否应允许突出显示行。
func tableView(UITableView, heightForRowAt : IndexPath) -> CGFloat
–向委托人询问要用于指定位置的行的高度吗?
func tableView(UITableView, shouldShowMenuForRowAt : IndexPath) -> Bool
–问代表是否应针对某一行显示编辑菜单?
func tableView(UITableView, shouldHighlightRowAt : IndexPath) -> Bool
–问委托是否应突出显示指定的行?
这个理论很好,但实际上让我们付诸实践。 首先,我们将首先创建自己的自定义委托对象,以展示委派如何工作,不仅限于UITableViews,而UITableViews一直是本文的重点。
其次,我们将实现UITableView
并利用一些预定义和常用的委托方法。 如果这还没有道理,那就不用担心……很快,我们只需要动动双手,使代表团更加生动活泼!
示例1 –自定义委派
在此示例中, ViewControllerB
(委托者)识别UISlider
更改以及是否存在委托,它将传达UISlider
更改为的值。 ViewControllerA
(委托-名词)将提名自己为委托对象。 ViewControllerA
的目的是更新UIView
,使其可以使用用户使用UISlider
选择的UISlider
。 它将全部组合如下:
该应用程序Main.storyboard如下所示,左侧为ViewControllerB
,右侧为ViewControllerB
。
在开始之前,要么在Main.storyboard中创建以上内容,要么从我的GitHub下载演示项目,以获取一些起点。
实际应用程序的行为如下:
委托协议
我们首先创建委托协议,该协议将指定要委托的职责。 为了使委托人遵守协议,它必须提供委托人协议中定义的所有属性和方法的实现。 我们将定义一种方法updateAlpha(value: )
。
协议可以在ViewControllerA
文件的顶部或独立文件中创建。 如果该协议简短且实用,我建议在委托对象文件(即ViewControllerA
的顶部实现该协议。
AnyObject
通过将AnyObject
协议添加到协议的继承列表中,可以将协议采用限制为类类型(而不是结构或枚举)。 从Swift 4开始,声明仅类协议的首选方法是使用AnyObject
而不是class
。
委托对象
ViewControllerB
是我们的委托对象。 它管理包含UISlider
的ContainerView。 它具有UISlider
的IBOutlet和IBAction。
在IB动作内部,我们有两行代码。 第一行获取滑块的值并将其四舍五入为最接近的整数。 第二行将滑块“捕捉”到四舍五入的值,否则滑块将位于增量之间。
然后,我们再添加两行代码。
第一行新的weak var delegate: ViewControllerBDelegate?
是我们保留对委托对象的引用的方式。 请注意,属性的类型为ViewControllerBDelegate
即我们的委托协议。
第二行是delegate?.updateAlpha(value: slideValue/10)
,该行基本上说:“如果对象已声明自己为委托,则将幻灯片的值(除以10)传递给updateAlpha(value: )
委托实现的方法。
代表
最后,我们有ViewControllerA
,我们的委托对象。
有两行重要的代码块。 第一个有效地将ViewControllerB
嵌入到viewDidLoad()
ViewControllerB
中。 对于委托至关重要的代码中最重要的部分是viewController.delegate = self
。 这段代码基本上说:“ ViewControllerB
的委托将是ViewControllerA
”。
另一个重要的代码块如下。 ViewControllerA: ViewControllerBDelegate
声明委托视图控制器确认委托协议。 updateAlpha(value: Float)
函数中的代码是神奇的事情……它是代表委托人提供功能的委托人。
当使用以下代码在ViewControllerB
中移动滑块时,将value
参数的float
传递给委托: delegate?.updateAlpha(value: slideValue/10)
,在这里,此值用于提供我们想要的功能在此特定上下文中来自updateAlpha
函数。 在另一个委托对象中,我们可以实现特定于该视图控制器上下文的不同功能。
示例2 – UITableView委托
委托对象
委托协议
代表
委派的工作原理-Andrew Bancroft的Swift开发人员指南
了解Swift 4中的代理人和委托作者Andrew Jaffee
Swift语言指南-协议
约翰·桑德尔(John Sundell)的Swift代表团
TiborBödecs的Swift代表设计模式
由James Rochabrun在Swift中实现代表
肖恩·艾伦的《快速代理协议模式指南》