事件响应链的一个bug

源于同事遇到的一个问题,简化情况:

storyboard

红色的View是加在ViewController上的一个自定义View
代码如下:

import UIKit

class RedView: UIView {

    override func touchesBegan(_ touches: Set, with event: UIEvent?) {
        print("red touchesBegan")
        self.next?.touchesBegan(touches, with: event)
    }
    override func touchesMoved(_ touches: Set, with event: UIEvent?) {
        print("red touchesMoved")
        self.next?.touchesMoved(touches, with: event)
    }
    override func touchesEnded(_ touches: Set, with event: UIEvent?) {
        print("red touchesEnded")
        self.next?.touchesEnded(touches, with: event)
    }
}

事件响应的原理是,如果这个View没有处理这个事件(即没有实现touchesXXX等方法),那么这个事件直接传递给superView处理,这里的superView当然就是ViewController里面的View,如果这个View没有处理,那么交给所属的ViewController处理。具体的事件响应链这里不做讨论。

那么这里的情况是,RedView做了事件处理,并且在处理之后,又原封不动的把事件抛给事件响应链里的下一个接收者。这里的self.next就是获取下一个事件接收者。

ViewController代码:

import UIKit

class ViewController: UIViewController {

    override func touchesBegan(_ touches: Set, with event: UIEvent?) {
        print("vc touchesBegan")
    }
    override func touchesMoved(_ touches: Set, with event: UIEvent?) {
        print("vc touchesMoved")
    }
    override func touchesEnded(_ touches: Set, with event: UIEvent?) {
        print("vc touchesEnded")
    }

}

我点击red View会有什么打印?
的确如预期的那样。

点击输出

如果我想触发touchesMoved,也就是在red View滑动,会是什么结果?

滑动输出

只有red view会持续接收到touchesMoved事件。vc接收到一次就终止了,就连最后的touchesEnded事件也没有接收到。

这种情况只会发生在ViewController里的View上。也就是如果是两个自定义view的话,不会有这个问题?

那么究竟是什么问题?从事件响应链上来说,并没有什么特殊的地方,难道是ViewController里的View有什么特殊处理吗?

通过下面stackoverflow讨论的结果是:

Passing touchesMoved events to superview only passes first event

IOS 5: UIScrollView not pass touches to nextResponder

self.nextResponder vs super for touchesMoved

touchesMoved not firing in iOS5 for the iPhone for UIScrollView

  • 1.iOS5之后出现这个问题,iOS之前是正常的。

  • 2.官方已经给出答复,这个是iOS的bug。

  • 3.只能用其他方式替代。

最直接的解决方法是,将上面的red View代码改为

import UIKit

class RedView: UIView {

    override func touchesBegan(_ touches: Set, with event: UIEvent?) {
        print("red touchesBegan")
        self.next?.next?.touchesBegan(touches, with: event)
    }
    override func touchesMoved(_ touches: Set, with event: UIEvent?) {
        print("red touchesMoved")
        self.next?.next?.touchesMoved(touches, with: event)
    }
    override func touchesEnded(_ touches: Set, with event: UIEvent?) {
        print("red touchesEnded")
        self.next?.next?.touchesEnded(touches, with: event)
    }
}

self.next?改为self.next?.next?,意思是,将事件传递到下下个事件接收者,也就是ViewController,而不再是ViewController里的View。我们可以得到想要的结果:

解决方法

你可能感兴趣的:(事件响应链的一个bug)