(六十)触摸事件的传递和响应者链条、手势处理

发生触摸事件后,系统会将该事件加入UIApplication管理的事件队列中。


UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序主窗口。

触摸事件通过父控件传递到子控件。


例如有1->2->3->4->5这么几层View,点击事件需要通过UIApplication传给UIWindow,然后向子控件传递。

如果父控件拒绝接收事件,那么点击子控件是无效的。


1.不接受交互 2.隐藏 3.透明度为0-0.01


如果传递链在中间断开,则会交给断点前的控制器处理。

例如3->4处损坏,点击5会由3处理,如果5拒绝接收,会由4处理。


图片默认的userInteractionEnabled为NO,默认不接收事件,因此不会遮盖下面的视图接收触摸事件。


以后发现按钮不管用,检查父控件是否userInterationEnabled为YES,例如图片上的按钮。


imageView的响应为YES,会把事件回抛到父控件(响应者链条)。


事件顺着响应者链条向上传递,找到最合适的控件调用touchesXxx方法。


可以实现事件的回传。


1->2->3 三层

如果要实现2、3都监听触摸,调用3的super的touchesXxx方法即可。


事件处理的完整过程:递归

1.先将事件对象由下往上传递,由父控件传递给子控件,找到最合适的控件来处理这个事件(调用touchesXxx方法)。

2.如果调用了super的touches方法,就会将事件顺着响应者链条往上传递,传递给上一个响应者的touches方法。


上一个响应者是谁(专业术语叫nextResponder)?

1.看当前这个view是不是控制器的view,如果是,则控制器就是上一个响应者;否则,那么父控件为上一个响应者。

2.如果控制器继续回抛,会抛给UIWindow。

3.UIWindow继续回抛,则交给UIApplication。

和触摸事件的传递方向相反。


什么是响应者链条?一些响应者构成的链条。

作用?把事件往上抛,可以让多个对象处理事件。


如果没有实现touchesXxx方法,会自动调用super的touchesXxx方法,也就是说,不重写touchesXxx方法就会把事件一直往上传,直到实现这些方法的父类。


iOS3.2之后推出了手势识别功能,Gesture Recognizer,在触摸事件的处理方面,大大简化了开发者的开发难度。


Tip:如果使用多点触控注意设置相应属性。


手势识别器:UIGestureRecognizer能轻松识别用户的一些常见手势:

它是一个抽象类(不能直接使用),定义了一些公共属性。

只能使用它的子类:

UITapGestureRecognizer 敲击手势

UIPinchGestureRecognizer 捏合

UIPanGestureRecognizer 拖拽

UISwipeGestureRecognizer 轻扫

UIRotationGestureRecognizer 旋转

UILongPressGestureRecognizer 长按


手势创建:

// 1.创建手势识别器对象

UITapGestureRecognizer *tap = [[UITapGestureRecognizeralloc] init];


// 2.添加手势识别器对象到对应的view

[self.iconViewaddGestureRecognizer:tap];


// 3.添加监听方法(识别到了对应的手势,就会调用监听方法)

[tap addTarget:selfaction:@selector(tapView)];


多次点击触发:

tap的numberOfTapRequired属性代表几次点击才有效。

多指触发:

tap的numberOfTouchesRequired属性代表几个手指才有效。


监听点击的快速创建:

UIGestureRecognizer *tap = [[UITapGestureRecognizeralloc] initWithTarget:selfaction:@selector(tapView)];


手势识别器代理<UIGestureRecognizerDelegate>

有触摸时先调用这个方法,再调用tap绑定的action,注意不要忘记把tap的delegate设为相应的控制器。

实现对触摸点的过滤:例如只有左半边图片响应触摸,返回YES才响应。

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch

{

    CGPoint pos = [touchlocationInView:touch.view];

    if (pos.x <=self.iconView.frame.size.width * 0.5) {

        returnYES;

    }

    returnNO;


}


长按:

UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizeralloc] init];

[longPress addTarget:selfaction:@selector(longPressView)];

// 至少长按2

longPress.minimumPressDuration =2;

// 在触发手势之前,50px范围内长按有效,可以理解为识别之前允许挪动的范围

longPress.allowableMovement =50;

[self.redViewaddGestureRecognizer:longPress];


轻扫:可以设定方向

UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizeralloc] initWithTarget:selfaction:@selector(swipeView)];

swipe.direction =UISwipeGestureRecognizerDirectionUp;

[self.redViewaddGestureRecognizer:swipe];


旋转:

UIRotationGestureRecognizer *recognizer = [[UIRotationGestureRecognizeralloc] initWithTarget:selfaction:@selector(rotateView:)];

recognizer.delegate =self;

[self.iconViewaddGestureRecognizer:recognizer];


注意action接收手势对象来得到转角:正确方法是每次在原来的基础上修改transform,然后把手势的累计清零。

- (void)rotateView:(UIRotationGestureRecognizer *)recognizer

{

    recognizer.view.transform =CGAffineTransformRotate(recognizer.view.transform, recognizer.rotation);

    recognizer.rotation =0; // 注意清零,否则会积累


}


缩放:

UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizeralloc] initWithTarget:selfaction:@selector(pinchView:)];


[self.iconViewaddGestureRecognizer:pinch];

- (void)pinchView:(UIPinchGestureRecognizer *)pinch

{

    pinch.view.transform =CGAffineTransformScale(pinch.view.transform, pinch.scale, pinch.scale);

    pinch.scale =1; // 注意清零规模


}


多个手势有效:注意设置每个tap的delegate

/**

 *  是否允许多个手势识别器同时有效

 *  Simultaneously : 同时地

 */

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer

{

    returnYES;


}


Simultaneously


拖拽(pan):

可以实现高级UI,在主界面下面设置左右两个抽屉,在拖动主View时会露出下面的View。

UIPanGestureRecognizer *pan = [[UIPanGestureRecognizeralloc] initWithTarget:selfaction:@selector(panView:)];


[self.purpleViewaddGestureRecognizer:pan];

注意设置回调函数的参数来接收手势。


回调函数实现view的拖拽:

- (void)panView:(UIPanGestureRecognizer *)pan

{

    

    switch (pan.state) {

        caseUIGestureRecognizerStateBegan: // 开始触发手势

            

            break;

            

        caseUIGestureRecognizerStateEnded: // 手势结束

            

            break;

            

        default:

            break;

    }

    

    // 1.view上面挪动的距离

    CGPoint translation = [pantranslationInView:pan.view];

    CGPoint center = pan.view.center;

    center.x += translation.x;

    center.y += translation.y;

    pan.view.center = center;

    

    // 2.清空移动的距离

    [pan setTranslation:CGPointZeroinView:pan.view];


}

所有手势状态:注意排除开始和结束,在其他情况下移动。

UIPanGestureRecognizer类型的pan有个translationInView:方法,可以得到相对参数传入的view对应的偏移量,将这个值累加到要挪动的view的center即可实现挪动,注意清空pan的偏移量,使用setTranslation: inView:方法。

typedef NS_ENUM(NSInteger, UIGestureRecognizerState) {

    UIGestureRecognizerStatePossible,   // the recognizer has not yet recognized its gesture, but may be evaluating touch events. this is the default state

    

    UIGestureRecognizerStateBegan,      // the recognizer has received touches recognized as the gesture. the action method will be called at the next turn of the run loop

    UIGestureRecognizerStateChanged,    // the recognizer has received touches recognized as a change to the gesture. the action method will be called at the next turn of the run loop

    UIGestureRecognizerStateEnded,      // the recognizer has received touches recognized as the end of the gesture. the action method will be called at the next turn of the run loop and the recognizer will be reset to UIGestureRecognizerStatePossible

    UIGestureRecognizerStateCancelled,  // the recognizer has received touches resulting in the cancellation of the gesture. the action method will be called at the next turn of the run loop. the recognizer will be reset to UIGestureRecognizerStatePossible

    

    UIGestureRecognizerStateFailed,     // the recognizer has received a touch sequence that can not be recognized as the gesture. the action method will not be called and the recognizer will be reset to UIGestureRecognizerStatePossible

    

    // Discrete Gestures – gesture recognizers that recognize a discrete event but do not report changes (for example, a tap) do not transition through the Began and Changed states and can not fail or be cancelled

    UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded// the recognizer has received touches recognized as the gesture. the action method will be called at the next turn of the run loop and the recognizer will be reset to UIGestureRecognizerStatePossible

};


原文地址:https://www.cnblogs.com/aiwz/p/6154190.html