在事件响应和传递这篇文章中,讲了iOS中的事件响应和传递,今天在做项目的时候,正好碰到了一个应用的场景,因此记录下来。
需求
首页头部需要添加这样一个视图
点击左右两个按钮,底部的滑块跟着滑动,同时也可以拖动滑块到选中的按钮位置。
思路和问题
看到这个设计图一开始的想法就是,左右两个按钮,底部一个滑块视图,然后到做的时候,发现如果要做手势拖拽的话,那么由于层级关系,滑块肯定是在两个按钮下面的,如果要触发滑动手势,势必会被按钮的点击事件阻止。
通过重写hitTest方法解决
首先判断点击点是位于左侧按钮区域还是右侧按钮区域,在根据按钮的选中状态来判断。
初始状态时左侧按钮选中,右侧没选中,同时滑块位于左侧。
如果触摸点在右侧按钮区域,此时应该走的流程是响应右侧按钮事件。
如果触摸点在左侧区域,此时左侧区域是选中状态,应该响应的是滑块的拖动事件。
依照这个思路,就有了下面的这段代码
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let superHitPoint = self.convert(point, to: self.superview)
if superHitPoint.x <= self.frame.width {
if self.frame.minX > 0 { // 点击左侧按钮,但是现在响应的是右侧按钮,原样传递上去
return super.hitTest(point, with: event)
}
if self.isSelected {
return strikeView
} else {
return super.hitTest(point, with: event)
}
} else {
if self.frame.minX == 0 { // 同理
return super.hitTest(point, with: event)
}
if self.isSelected {
return strikeView
} else {
return super.hitTest(point, with: event)
}
}
}
这里要注意的一点是,由于事件响应链是系统会从后往前遍历子视图,我们点击任何一个区域,两个按钮都会响应hitTest方法,在判断触摸点的区域之后,还要判断一下当期响应的按钮是不是我们期望的按钮,如果不是,则不作任何改动。