所谓动作就是由UIControl子类(一个控件)实例发出的一条消息,用于通知你该控件上发生了一个重要的用户事件。UIControl子类都是非常简单的界面对象,用户可以直接与其交互,比如,按钮(UIButton)和分割控件(UISegmentedControl)等。
重要的用户事件(控件事件)列在了UIControl类文档Constants中的UIControlEvents下。不同控件实现了不同的控件事件;比如,分割控件的Value Changed事件表示用户轻拍并选择了不同的分段,按钮的Touch Up Inside事件则表示用户轻拍了按钮。控件事件本身并没有外在效果;控件会形象地做出响应(比如,被轻拍的按钮看起来就像按下去了一样),不过它并不会自动共享事件发生的信息。如果想知道一个控件事件是何时发生的以便能够在代码中对其做出响应,你就需要让该控件事件触发一条动作消息。
下面是其运作方式。控件会维护一个内部分发表:对于每个控件事件来说都可以有任意数量的目标-动作对,其中动作是个消息选择器(即方法名),目标则是消息将会发送到的对象。当控件事件发生时,控件会查询其分发表,寻找与该控件事件相关的所有目标-动作对,并将每一条动作消息发送给相应的目标(如图11-1所示)。
图11-1:目标-动作架构
有两种方式可以操纵控件的动作分发表:
动作连接
可以在nib中配置动作连接。第7章曾介绍过如何做到这一点,不过并未介绍底层机制。现在一切都很明显了:nib编辑器中形成的动作连接是配置控件动作分发表的一种可视化方式。
代码
可以通过代码直接操纵控件的动作分发表。这里所用的关键方法是UIControl的实例方法addTarget:action:forControlEvents:,其中target:是个对象,action:是个选择器(在Swift中是个字符串),controlEvents:是通过位掩码指定的。与通知中心不同,控件还提供了用于内省分发表的方法。
对于UIControl的target:与关于NSNotifications的selector:也要小心。当控件事件触发时,目标一定要存在才行,它要有一个与动作选择器相对应的方法,Objective-C必须要能调用该方法。否则就会出问题。
回忆一下第7章介绍的关于控件与动作的示例。我们有一个buttonPressed:方法:
@IBAction func buttonPressed(sender:AnyObject) { let alert = UIAlertController( title: /"Howdy!/", message: /"You tapped me!/", preferredStyle: .Alert) alert.addAction( UIAlertAction(title: /"OK/", style: .Cancel, handler: nil)) self.presentViewController(alert, animated: true, completion: nil)}
该方法的目的在于当用户轻拍了界面上的某个按钮时它会被调用。在第7章中,我们通过在nib中创建了一个动作连接来做到这一点:将按钮的Touch Up Inside事件连接到了ViewController的buttonPressed:方法。实际上,我们构造了一个目标-动作对,并将该目标-动作对添加到了按钮的Touch Up Inside控件事件分发表中。
相对于在nib中进行操作,我们可以通过代码达成所愿。假设我们从来没有绘制过这个动作连接;相反,我们有一个从视图控制器到按钮的名为button的插座变量连接。当nib加载后,视图控制器就可以像如下代码一样配置按钮的分发表:
self.button.addTarget(self, action: /"buttonPressed:/", forControlEvents: .TouchUpInside)
一个控件事件可以有多个目标-动作对。你可能有意这么配置,但也有可能无意而为之。不小心为一个控件事件指定一个目标-动作对但又没有移除现有的目标-动作对是非常容易犯的一个错误,这会导致一些非常奇怪的行为。比如,如果在nib中构造了一个动作连接并且又通过代码配置了分发表,那么轻拍按钮就会导致buttonPressed:被调用两次。
动作选择器的签名可以是如下3种形式之一:
·完整形式接收两个参数:
·控件,通常是AnyObject类型。
·生成控件事件的UIEvent。
·一种简写形式,也是最常使用的一种形式,省略了第2个参数。buttonPressed:就是个例子;它只接收一个参数sender。当buttonPressed:通过来自于按钮的动作消息被调用时,sender就是对该按钮的引用。
·还有一种简写形式,它会将这两个参数全部省略。
UIEvent是什么,作用又是什么呢?当用户使用手指进行操作时就会生成触摸事件(轻拍屏幕、移动手指、将手指从屏幕移开)。UIEvent是最底层的对象,用于实现触摸事件与应用之间的通信。UIEvent基本上就是个时间戳(一个Double)外加上一个触摸事件(UITouch)的集合(Set)。动作机制对你屏蔽了触摸事件的复杂性,不过通过接收UIEvent,你依然可以处理这些复杂性。
奇怪的是,没有一个动作选择器参数提供了一种方式来获悉哪个控件事件触发了当前动作选择器的调用!比如,要想区分Touch Up Inside控件事件与Touch Up Outside控件事件,其相应的目标-动作对就必须指定两个不同的动作处理器;如果将其分发给相同的动作处理器,那么该处理器就无法判断发生的是哪个控件事件了。