美文网首页我爱编程
事件响应链的一个bug

事件响应链的一个bug

作者: 桔子听 | 来源:发表于2018-04-16 14:36 被阅读13次

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

    storyboard

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

    import UIKit
    
    class RedView: UIView {
    
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            print("red touchesBegan")
            self.next?.touchesBegan(touches, with: event)
        }
        override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
            print("red touchesMoved")
            self.next?.touchesMoved(touches, with: event)
        }
        override func touchesEnded(_ touches: Set<UITouch>, 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<UITouch>, with event: UIEvent?) {
            print("vc touchesBegan")
        }
        override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
            print("vc touchesMoved")
        }
        override func touchesEnded(_ touches: Set<UITouch>, 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<UITouch>, with event: UIEvent?) {
            print("red touchesBegan")
            self.next?.next?.touchesBegan(touches, with: event)
        }
        override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
            print("red touchesMoved")
            self.next?.next?.touchesMoved(touches, with: event)
        }
        override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
            print("red touchesEnded")
            self.next?.next?.touchesEnded(touches, with: event)
        }
    }
    

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

    解决方法

    相关文章

      网友评论

        本文标题:事件响应链的一个bug

        本文链接:https://www.haomeiwen.com/subject/vwqekftx.html