View

Application透过-sendEvent:将事件送到window,window也一样透过-sendEvent:把事件送到view上,而在view里头,则是透过-hitTest:withEvent:,在一层又一层的view heirarchy里头寻找应该处理事件的sub view。

比方说,我们现在有个view,里头有两个subview:button A与button B,在-hitTest:withEvent:这个method里头,就会先去判断触控事件的坐标是否在这两个subview里头,如果触控坐标在button A的frame里头,那就该由button A来处理事件,-hitTest:withEvent:就会回传button A。

-hitTest:withEvent:不但会问触控事件的座标是否在某个subview里,也会问这个subview是否打算处理事件。假如反过来,我们现在有个button,而里头有两个subview,一个是文字label、一个是个image view,这两个view都只负责处理显示内容,而不处理事件,那么,这个button里头的-hitTest:withEvent:最后就会把自己回传回去,表示是由这个button自己处理事件,而不交由任何的subview处理。

有时候我们会藉由修改-hitTest:withEvent:达成一些稀奇古怪的效果,例如,我们现在有一个view,里头有一个scroll view,scroll view只占他的super view的一部分而已,但是我们希望在这个view里头的任何地方点选,都可以卷动里头的scroll view。我们这个时候就可以考虑改写-hitTest:withEvent:,不管触控的位置是否真的在scroll view里头,-hitTest:withEvent:都回传这个scroll view。

当事件从application、window传递到view之后,就会成为一些我们之前就已经熟悉的API,在Mac上会触发-keyDown:-mouseDown:这些在NSView里头的method,至于在iOS上,触控事件传递到view之后就会触发一系列跟触控事件有关的method:

  • -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
  • -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
  • -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
  • -(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event

从这些method中,我们会知道触控事件是否开始、结束、中间手指是否移动,用户在萤幕硬体上到底用了几支手指,每根手指的位置又如何变化,进而知道用户到底做了怎样的手势:是指用一根手指做了点选(tap)还是横划(swipe)、还是用了两根以上的手制作了缩放(pinch),甚至是更复杂的手势。在苹果官方文件Multitouch Events当中,就说明了怎样用上面这些UIResponder method,处理各些复杂手势。

要了解事件是怎样从Application 一路送到View,最简单的方法,就是写一个简单的App:这个App 只有一个按钮,然后我们在这个按钮的action 打上一个break point,然后来看一下这个时候的back trace:

  • frame 16是 main() ,里头执行UIApplicationMain
  • frame 10-14 便是在执行Runloop
  • 在frame 7-9,可以看到Runloop 把事件送给UIApplication
  • 在frame 5-6,可以看到UIWindow 在处理来自UIApplication 的事件
  • frame 4 在处理我们刚刚讲到的 touchesEnded:withEvent:
  • 最后在frame 0-3,便是在处理这个按钮上的target/action

results matching ""

    No results matching ""