记录一个有趣的东西。

我们知道,对于iOS和浏览器来说,页面上的控件都可以有点击事件,而对于父子关系的组件他们的点击事件在两个平台的表现却不同。

<div class="a" id="a">
    <div class="b" id="b">
    </div>
</div>
<style>
.a {
    background: red;
    width: 100px;
    height: 100px;
}

.b {
    position: relative;
    // left: 100px; 位置错开
    top: 0;
    background: blue;
    width: 50px;
    height: 50px;
}
</style>
<script>
let a = document.getElementById('a')
let b = document.getElementById('b')

// 事件冒泡和捕获刚好是相反的的过程,
// 先由父级捕获并将捕获向子级传递,然后由子级将点击由子级向父级冒泡,
// 若父级捕获期间stopPropagation(),则后续事件全部中断,即子级无法捕获,无法响应点击,也无法向父级冒泡,
// 若子级捕获期间stopPropagation(),则后续事件全部中断,即子级无法响应点击,也无法向父级冒泡。

// 事件冒泡
a.addEventListener('click', (e) => {
    // e.preventDefault()
    console.log("click a")
})

b.addEventListener('click', (e) => {
	// 阻止事件向父级方向冒泡
    e.stopPropagation()
    console.log("click b")
})

// 事件捕获
a.addEventListener('click', (e) => {
	// 阻止捕获事件向子级方向传递
	e.stopPropagation()
    console.log('a catch')
}, true)

b.addEventListener('click', (e) => {
    console.log('b catch')
}, true)
</script>

注意,这里有一个问题。如果父子并不是重叠的,而是互相错开,那么上述事件还会如期发生吗? 答案是会。也就是说它只要是父子关系即可,不需要位置上的重叠。

同样的,iOS也有点击事件,默认点在哪个控件上,就触发哪个控件的点击事件。 如果我要实现点击某个控件,点击事件穿透它到它下面的控件,触发后者的点击事件,可以将上层控件的isUserInteractionEnabled设为false即可。 注意二者不必是父子关系,但必须是上下重叠关系 这跟js是不同的。

但是这样会带来一个问题,那就是上层控件内的所有子控件也将是不可点状态,这显然不能满足所有需求,此时完美的解决方案是,重写目标穿透控件的hitTest或者point方法,但要注意此时控件的isUserInteractionEnabled须为true:


class TestView: UIView {

	// 寻找此次事件最适合响应的view
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
		// 父view在这个点上点击,最适合的当然是父view
        let view = super.hitTest(point, with: event)
		// 父view显然不等于当前view,故走else,返回父view
        if view == self {
            return nil
        } else {
            return view
        }
    }

// 判断point在不在方法调用者上
// hitTest方法底层会调用point方法,判断点在不在控件上。
//    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
//        for subview in subviews {
//            if !subview.isHidden && subview.isUserInteractionEnabled && subview.point(inside: convert(point, to: subview), with: event) {
//                return true
//            }
//        }
//        return false
//    }
}

这样就可以满足所有需求了。 这里事实上涉及的是iOS的事件传递和响应者链,传递和响应的方向刚好相反,这跟js捕获和冒泡是不是很像? iOS事件传递和响应者链

可以参考iOS事件传递和响应者链

标签: swift, javascript

添加新评论

0%