一般的多层视图中,事件会首先到达最上层的 UIView,该视图可以对事件进行处理,决定是拦截还是继续分发到子试图中。
UIScrollView 也有这样的属性,它的原理是当一个点击事件到来时,会启动一个计时器,当计时器结束期间如果没有手指产生滚动操作,那么点击事件会继续传递给子试图;
否则会产生滚动效果,并且取消消息的继续传递。
UIScrollView 中有两个函数:
// called before touches are delivered to a subview of the scroll view. if it returns NO the touches will not be delivered to the subview
// this has no effect on presses
public func touchesShouldBegin(touches: Set<UITouch>, withEvent event: UIEvent?, inContentView view: UIView) -> Bool
该函数先于子试图的touchesBegin,返回NO 的话点击消息不回传递到子试图,且没有任何点击效果
// called before scrolling begins if touches have already been delivered to a subview of the scroll view. if it returns NO the touches will continue to be delivered to the subview and scrolling will not occur
// not called if canCancelContentTouches is NO. default returns YES if view isn't a UIControl
// this has no effect on presses
public func touchesShouldCancelInContentView(view: UIView) -> Bool
该函数在滚动效果产生效果之前调用,如果返回 NO 的话该消息会继续传递给子试图并且没有滚动效果
那么实现我的需求就比较简单,只需要两个函数在遇到 UIButton 点击时均返回 YES 即可
Swift 版代码:
//返回 false 时子视图不响应点击消息
override func touchesShouldBegin(touches: Set<UITouch>, withEvent event: UIEvent?, inContentView view: UIView) -> Bool {
if view is UIButton{
return true;
}else{
return super.touchesShouldBegin(touches, withEvent: event, inContentView: view);
}
}
//返回 false 时点击子视图无滚动效果
override func touchesShouldCancelInContentView(view: UIView) -> Bool {
if view is UIButton{
return true;
}else{
return super.touchesShouldCancelInContentView(view);
}
}
当然,这两个函数可以随意搭配来实现不同的效果。
另外UIScrollView 有一个属性
public var contentOffset: CGPoint // default CGPointZero
通过代码修改其值,可以确定滚动条的位置,但是它的变化是瞬间完成,没有动画效果,可以结合public var decelerationRate: CGFloat//手指放开时的减速率
后续再研究吧。