最近做项目遇见一个问题:UIScrollView上有许多UIButton,要实现既能点击UIButton,又能滑动UIScrollView,这个实现没有问题,不需要做额外的touch管理,UIScrollView能识别是点击的UIButton还是滑动本身,但是有一个效果上的问题,就是点击UIButton时间短的话,不会高亮,但是确实是触发点击了,这样就造成了假象,给人一种UIButton没有被点击的感觉。如果点击时间长的话没有问题,会高亮。
这么看来,导致这样现象应该就是时间长短的问题,顺着这个问题想下去,就追寻到UIScrollView的touch原理,当UIScrollView接收到一个touch时,它会在一段时间(好像是150ms)内监听该touch是否移动了,假如移动了(应该有一个移动范围),则取消将touch发送到其子视图(例如UIButton),UIScrollView自身接受该touch,进行滑动。看来应该就是这段判断时间惹的祸了(这里其实我还想不明白为什么明明点击了Button,也确实触发了点击,却没有高亮状态,哪位大神知道的话请指教),那要解决问题是不是就应该把这段时间取消掉呢,不要这个判断时间,那么UIButton应该就会立即响应并产生高亮状态吧。正好UIScrollView中有一个属性叫delaysContentTouches,官方文档对它的解释是:If the value of this property is YES, the scroll view delays handling the touch-down gesture until it can determine if scrolling is the intent. If the value is NO , the scroll view immediately calls touchesShouldBegin:withEvent:inContentView:. The default value is YES.意思就是设置为NO就不会存在那个150ms的判断时间了,直接执行后续操作。那么咱们设置为NO来试试呗。结果确实如所想那样,UIButton立即响应并高亮。
但是…..别高兴太早,虽然这个问题解决了,但是新的麻烦又产生了,螳螂捕蝉黄雀在后!你会发现UIScrollView滑动不像以前那样了,假如touch down的那点落在UIButton上然后再滑动手指,UIScrollView不会滑动,但是UIButton仍然触发,当然,这样的结果也是应该的,你想想,设置delaysContentTouches为NO后,只要手指点在UIButton上,UIScrollView就会立即判定为这是点击UIButton,而不会再等待看手指是否移动来决定是否要滑动本身了。
那么如何解决这个问题呢?你会想,有没有什么方法可以在touch到UIbutton上并滑动时不触发UIButton而让UIScrollView自己滑动呢,强大的iOS没有让你失望,UIScrollView中有一个方法:touchesShouldCancelInContentView:,来看它的解释:The scroll view calls this method just after it starts sending tracking messages to the content view. If it receives NO from this method, it stops dragging and forwards the touch events to the content subview. The scroll view does not call this method if the value of the canCancelContentTouches property is NO.意思就是当UIScrollView将touch事件交给子view后,当手指发生滑动时,调用此方法,假如返回NO,则将touch事件交给子view,如果返回YES,则交给UIScrollView处理,产生滑动。(但是前提是UIScrollView的canCancelContentTouches属性是YES才会调用这个方法,只要不是UIControll的子类,这个属性默认是YES。)所以只要重写UIScrollView的这个方法并返回YES就可以啦,到此问题就解决了。既能立即响应UIButton,也能自由滑动UIScrollView。