UIGestureRecognizer与UIMenuController

UIGestureRecognizer对象会截取本应由视图处理的触摸事件。当某个UIGestureRecognizer对象识别出特定的手势后,就会向指定的对象发送指定的消息。
 在为应用添加手势识别功能时,需要针对特定的手势创建相应的UIGestureRecognizer子类对象,而不是直接使用UIGestureRecognizer对象。
 使用UIGestureRecognizer子类对象时,除了要设置目标动作对,还要将该子类对象“附着”在某个视图上。当该子类对象根据当前附着的视图所发生的触摸事件识别出相应的手势时,就会向指定的目标对象发送指定的动作消息。由UIGestureRecognizer对象发出的动作消息都会遵守以下规范:

-(void)action:(UIGestureRecognizer*)gestureRecognizer;

UIGestureRecognizer对象在识别手势时,会截取本应由其附着的视图自行处理的触摸事件。附着了UIGestureRecognizer对象的视图可能不会收到常规的UIResponder消息,例如touchesBegan:withEvent:。

UIGestureRecognizer对象会截取UITouch对象

由于UIGestureRecognizer对象会通过截取触摸事件来识别手势,因此,在UIGestureRecognizer对象识别出手势之前,UIView会收到所有UIResponder消息。对于UITapGestureRecognizer来说,在识别出点击手势(在屏幕中的一小块区域触摸并迅速抬起手指)之前,UIView会收到touchesBegan:withEvent:消息;在识别出点击手势之后,UITapGestureRecognizer会自行处理相关触摸事件,由这些触摸事件所引发的UIResponder消息将不会再发送给UIView。直到UITapGestureRecognizer检测出点击手势已经结束,UIView才会重新收到UIResponder消息(touchesCancelled:withEvent:)。

//延迟touchesBegan的接收
 double  TapRecognizer.delaysTouchesBegan=YES;

如果需要为视图添加多种手势,就需要考虑这些手势之间的关系。双击手势包含两次单击,为了避免UITapGestureRecognizer将双击事件分拆为两个单击事件,可以设置UITapGestureRecognizer在单击后暂时不进行识别,直到确定不是双击手势后再识别为单击手势。

 [tapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer];
UIMenuController

UIMenuController对象可以包含一组UIMenuItem对象(菜单项),并能在现有的视图上显示这些UIMenuItem对象。
每个iOS应用只有一个UIMenuController对象。当应用要显示该对象时,要先为它设置一组UIMenuItem对象,然后设置显示位置(矩形区域),最后将其设置为可见。
要显示UIMenuController对象,还要满足一个条件:显示UIMenuController对象的UIView对象必须是当前UIWindow对象的第一响应对象。
这是因为该对象在显示前会枚举所有的UIMenuItem对象,检查第一响应对象是否实现了指定的动作方法。如果没有实现该方法,UIMenuController对象就不会显示相应的UIMenuItem对象;如果所有UIMenuItem对象的动作方法都没有实现,应用就不会显示UIMenuController对象。
如果要将某个自定义的UIView子类对象设置为第一响应对象,就必须覆盖该对象的canBecomeFirstResponder方法。

-(BOOL)canBecomeFirstResponder
{
    return YES;
}

显示前会枚举所有的UIMenuItem对象,检查第一响应对象是否实现了指定的动作方法。如果没有实现该方法,UIMenuController对象就不会显示相应的UIMenuItem对象;如果所有UIMenuItem对象的动作方法都没有实现,应用就不会显示UIMenuController对象。
  当某个UIGestureRecognizer子类对象触发特定的事件后,其state属性会发生变化。以UILongPressGestureRecognizer对象为例,和上述三种事件相对应的state属性分别为:UIGestureRecognizerStatePossible、UIGestureRecognizerStateBegan和UIGestureRecognizerStateEnded。
  当某个UIGestureRecognizer子类对象的state属性发生变化时(除了切换至UIGestureRecognizerStatePossible的情况),该对象就会向其目标对象发送指定的动作消息。所以当某个长按手势开始和结束时,相应的UILongPressGestureRecognizer对象都会向其目标对象发送同一个消息。和该消息匹配的方法可以通过UIGestureRecognizer对象的state属性来判断当前的事件类型,然后根据不同的事件类型执行不同的代码。
  通常情况下,UIGestureRecognizer对象不会将其处理过的触摸事件再交给其他对象来处理。一旦某个UIGestureRecognizer子类对象识别出了相应的手势,就会“吃掉”所有相关的触摸事件,导致其他UIGestureRecognizer对象没有机会再处理这些触摸事件。对TouchTracker,这种特性会导致BNRDrawView对象无法处理拖动手势,这是因为整个拖动手势都是在长按手势中发生的。要解决这个问题,需要让UILongPressGestureRecognizer对象和UIPanGestureRecognizer对象能够同时识别手势。
  UIGestureRecognizerDelegate协议声明了很多方法,gestureRecognizer: shouldRecognizeSimultaneouslyWithGestureRecognizer:(如下代码)。当某个UIGestureRecognizer子类对象识别出特定的手势后,如果发现其他的UIGestureRecognizer子类对象也识别出了特定的手势,就会向其委托对象发送gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:消息。如果相应的方法返回YES,那么当前的UIGestureRecognizer子类对象就会和其他UIGestureRecognizer子类对象共享UITouch对象。

-(BOOL)gestureRecognizer: (UIGestureRecognizer*) gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)other
{
    if(gestureRecognizer==self.moveRecognizer)
    {
        return YES;
        
    }
        return NO;
}
//将当前手指的位置设置为初始手势的位置
[gr setTranslation:CGPointZero inView:self];

现UIPanGestureRecognizer对象的动作方法moveLine:。在moveLine:中,要调用UIPanGestureRecognizer对象的translationInView:方法。该方法会根据传入的UIView对象的坐标系,以CGPoint结构的形式返回手指的拖动距离。当拖动手势开始时,拖动距离是0点(x和y都是0)。拖动的过程中,拖动距离会不断发生变化(将手指移动至窗口的最右端,x的值就会很高。将手指移回手势的起始位置,拖动距离就会变回0点)。
  当UIGestureRecognizer对象的cancelsTouchesInView属性为NO时,这个对象所依附的UIView对象仍然会收到相应的UIResponder消息,从而有机会处理相关的UITouch对象。

UIMenuController与UIResponderStandardEditActions

如果某个UIResponder子类需要实现特定的“编辑”功能,就可以实现相应的方法。以UITextField为例,为了能够剪切当前选中的文字,UITextField实现了cut:方法。UIResponderStandardEditActions协议声明了所有这类“编辑”方法。
  如果某个UIView子类实现了UIResponderStandardEditActions协议中的方法,那么当这个子类对象显示UIMenuController对象时,就会出现和这些方法相对应的菜单项。这是因为该对象会在显示前枚举所有的“编辑”菜单项,然后根据其动作消息向视图发送canPerformAction:withSender:消息。如果视图实现了指定的方法,该消息就会返回YES,否则返回NO。UIMenuController对象会根据canPerformAction:withSender:的返回结果判断是否应该显示相应的菜单项。

-(BOOL)canPerformAction:(SEL) actionwithSender:(id)sender  
{  
if(action == @selector(copy:)) 
{
  returnNO;
}
 //父类的实现会根据目标对象是否实现了特定的动作方法,返回YES或NO 
 return  [super  canPerformAction:action  withSender:sender];
}
再谈UIGestureRecognizer

13.8

你可能感兴趣的:(UIGestureRecognizer与UIMenuController)