UIView是UIResponder的子类,所以覆盖以下四个方法就可以处理四种不同的触摸事件:
-(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;
UITouch对象和事件响应方法的工作机制:
•一个UITouch对象对应屏幕上的一根手指。只要手指没有离开屏幕,相应的UITouch对象就会一直存在。这些UITouch对象都会保存对应的手指在屏幕上的当前位置。
•在触摸事件的持续过程中,无论发生什么,最初发生触摸事件的那个视图都会在各个阶段收到相应的触摸事件消息。即使手指在移动时离开了这个视图的frame区域,系统还是会向该视图发送touchesMoved:withEvent:和touchesEnded:withEvent:消息。也就是说,当某个视图发生触摸事件后,该视图将永远“拥有”当时创建的所有UITouch对象。
•读者自己编写的代码不需要也不应该保留任何UITouch对象。当某个UITouch对象的状态发生变化时,系统会向指定的对象发送特定的事件消息,并传入发生变化的UITouch对象。
当应用发生某个触摸事件后(例如触摸开始、手指移动、触摸结束),系统都会将该事件添加至一个由UIApplication单例管理的事件队列。通常情况下,很少会出现满队列的情况,所以UIApplication会立刻分发队列中的事件。分发某个触摸事件时,UIApplication会向“拥有”该事件的视图发送特定的UIResponder消息(如果读者在执行触摸操作时感觉应用的反应迟缓,就很有可能是因为应用的某个方法占用了大量CPU时间,导致队列堵塞。)
当多根手指在同一个视图、同一个时刻执行相同的触摸动作时,UIApplication会用单个消息、一次分发所有相关的UITouch对象。UIApplication在发送特定的UIResponder消息时,会传入一个NSSet对象,该对象将包含所有相关的UITouch对象(一个UITouch对象对应一根手指)。
默认情况下,视图在同一时刻只能接受一个触摸事件。如果一根手指已经触发了touchesBegan:withEvent:方法,那么在手指离开屏幕之前(触发touchesEnded:withEvent:方法之前),其他触摸事件都会被忽略——对于BNRDrawView来说,“忽略”是指touchesBegan:withEvent:或其他UIResponder消息都不会再发送给BNRDrawView。
为了使BNRDrawView同时接受多个触摸事件,需要添加下面代码:
self.multipleTouchEnabled=YES;
在处理多点触控事件中,用字典保存响应的Touch对象,进行每个touchs点的针对更新,需要使用valueWithNonretainedObject:方法将UITouch对象的内存地址封装为NSValue对象,作为该字典的键。使用内存地址分辨UITouch对象的原因是,在触摸事件开始、移动、结束的整个过程中,其内存地址是不会改变的,内存地址相同的UITouch对象一定是同一个对象。
为什么UITouch对象自身不能用作NSMutableDictionary的键?这是由于NSDictionary及其子类NSMutableDictionary的键必须遵守NSCopying协议——键必须可以复制(可以响应copy消息)。UITouch并不遵守NSCopying协议,因为每一个触摸事件都是唯一的,不应该被复制。相反,NSValue遵守NSCopying协议,同一个UITouch对象会在触摸过程中创建包含相同内存地址的NSValue对象。
最后还需要处理触摸取消事件。如果系统中断了应用,触摸事件就会被取消(例如iPhone接到电话)。需要在touchesCancelled:withEvent:方法中处理。
响应对象链
UIResponder对象可以接收触摸事件,而UIView是典型的UIResponder子类。除了UIView,还有很多其他的UIResponder子类,其中包括UIViewController、UIApplication和UIWindow。UIViewController不是视图对象,既不能触摸也无法显示,为什么也是UIResponder子类?这是因为虽然不能向UIViewController对象直接发送触摸事件,但是该对象能够通过响应对象链接收事件。
UIResponder对象拥有一个名为nextResponder的指针,相关的UIResponder对象可以通过该指针组成一个响应对象链:
响应对象链
响应流程
当UIView对象属于某个UIViewController对象时,其nextResponder指针就会指向包含该视图的UIViewController对象。当UIView对象不属于任何UIViewController对象时,其nextResponder指针就会指向该视图的父视图。UIViewController对象的nextResponder通常会指向其视图的父视图。最顶层的父视图是UIWindow对象,而UIWindow对象的nextResponder指向的是UIApplication单例。
如果UIResponder对象没有处理传给它的事件,会发生什么?该对象会将未处理的消息转发给自己的nextResponder。这也是touchesBegan:withEvent:这类方法的默认实现。因此,如果没有为某个UIResponder对象覆盖特定的事件处理方法,那么该对象的nextResponder会尝试处理相应的触摸事件。最终,该事件会传递给UIApplication(响应对象链的最后一个对象),如果UIApplication也无法对其处理,系统就会丢弃该事件。
UIControl
对于UIControl对象,每个可能触发的控件事件(controlevent)都有一个对应的常量。以UIButton对象为例,该对象的常用控件事件是UIControlEventTouchUpInside。
iOS12.9,不是很懂?!!!
网友评论