UIScrollView拾遗

前几天遇到一个bug在一个collectionView上嵌套collectionView的页面, 下拉刷新以后会出现cell的点击事件全部不响应的问题. 这个问题查了很久才找到原因, 这里记录一下.


IMG_0050.PNG
思路过程:

最开始遇到这个问题,以为是collectionView的frame错误导致的, 因为我们知道如果subView的位置超出了parentView的frame, 就会出现subView的点击事件不响应的问题。

但是排查以后发现collectionViewframe并没有问题, 而且虽然刷新以后cell不可以点击, 但是collectionView仍然可以滑动, 并且在滑动以后cell就可以恢复响应点击事件了。

随后发现注释掉加在collectionView上面的自定义手势后, bug就不会出现了. 然后把问题归结为自定义手势和collectionView的手势冲突导致的. 这里把自定义手势的cancelsTouchesInView 设置为NO, 发现问题仍然存在,而且如果没有响应MJRefresh的刷新时间,单独的滑动并不会产生这个问题。

这里又把问题转向了MJRefresh的代码,查看源码发现当MJRefreshHeader的state 是 Refreshing的时候,在设置scrollView的contentInset以后又设置了contentOffset。 代码如下:

    dispatch_async(dispatch_get_main_queue(), ^{
        [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
            CGFloat top = self.scrollViewOriginalInset.top + self.mj_h;
            // 增加滚动区域top
            self.scrollView.mj_insetT = top;
            // 设置滚动位置
            CGPoint offset = self.scrollView.contentOffset;
            offset.y = -top;
            [self.scrollView setContentOffset:offset animated:NO];
        } completion:^(BOOL finished) {
            [self executeRefreshingCallback];
        }];
     });

然后注释掉 [self.scrollView setContentOffset:offset animated:NO]; 或者把这行代码替换为 self.scrollView.contentOffset = offset; 也不会出现这个bug了。
这里大概知道了,因为某种原因,scrollView在进入tracking状态以后,结束下拉刷新以后并没有退出tracking状态, 所以导致cell的点击事件都被暂停掉了,这时候再滑动scrollView, 才会让tracking状态改为NO。 cell响应恢复正常。

但是如果这里直接修改第三方的代码,会让后面第三方库的维护变的十分不方便。所以这里继续查是否还有其他方法可以解决这个问题。这时候发现scrollView有canCancelContentTouchesdelaysContentTouches 这两个属性,将这两个属性设置为NO以后cell恢复响应,这里以为问题到此结束,准备开开心心的提交代码。 结果在重新过了一遍其他相关功能的时候发现,在编辑状态拖拽cell结束的时候,cell会响应tap点击删除的动作。问题仍然存在。。。。
最后在下拉刷新结束以后调整contentSize, 发现可以改变scrollView的tracking状态。然后也不会造成其他的影响, 问题到这里算是暂时结束。

知识点

1.UIScrollViewtracking状态

tracking 表示scrollView正在被跟踪,从你的手指touch屏幕开始,scrollView开始一个timer,如果:

  1. 150ms内如果你的手指没有任何动作,消息就会传给subView。
  2. 150ms内手指有明显的滑动(一个swipe动作),scrollView就会滚动,消息不会传给subView,这里就是产生问题二的原因。
  3. 150ms内手指没有滑动,scrollView将消息传给subView,但是之后手指开始滑动,scrollView传送touchesCancelled消息给subView,然后开始滚动。

观察下tableView的情况,你先按住一个cell,cell开始高亮,手不要放开,开始滑动,tableView开始滚动,高亮取消。

delaysContentTouches的作用:

这个标志默认是YES,使用上面的150ms的timer,如果设置为NO,touch事件立即传递给subview,不会有150ms的等待。

cancelsTouches的作用:

这个标准默认为YES,如果设置为NO,这消息一旦传递给subview,这scroll事件不会再发生。

cancelsTouchesInView的作用:
文档上是这么描述的:

A Boolean value affecting whether touches are delivered to a view when a gesture is recognized.
通过设置这个布尔值,来设置手势被识别时触摸事件是否被传送到视图

通过设置这个布尔值,来设置手势被识别时触摸事件是否被传送到视图。
举个🌰

- (void)viewDidLoad {
    UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
    button.backgroundColor = [UIColor colorWithRed:0.1 green:0.5 blue:0.4 alpha:1];
    [supView addSubview:button];
    [button addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventTouchUpInside];
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapAction:)];
    tap.cancelsTouchesInView = NO;
    [button addGestureRecognizer:tap];
}

- (void)tapAction:(UITapGestureRecognizer *)sender {
    NSLog(@"tap");        
}

- (void)btnAction:(UIButton *)btn {
    NSLog(@"button");    
}

cancelsTouchesInViewNO的时候,会分别触发tapAction:btnAction:方法;而当cancelsTouchesInViewYES的时候,只会触发tapAction:方法。

所以开始的时候,尝试把cancelsTouchesInView设置为NO,希望让collectionView同时相应自定义的手势和系统的点击事件。

2. setContentOffset: animated:setContentOffset:的区别

  • 使用animated参数,可以获得正确的UIScrollViewDelegate的回调;而使用UIView动画则不能
    苹果的官方文档中,对setContentOffset:animated:这一方法会引起的回调有大概如下的解释:

如果animated这一参数设置为NO,或者直接设置contentOffset这个property,delegate会收到一个scrollViewDidScroll:消息。如果animated这一参数设置为YES,则在整个动画过程中,delegate会收到一系列的scrollViewDidScroll:消息,并且当动画完成时,还会收到一个scrollViewDidEndScrollingAnimation:消息。

实验证明,使用setContentOffset:animated:方法得到的回调行为和官方文档中描述的一致。而使用UIView动画,则只能收到一次scrollViewDidScroll:回调,不能收到scrollViewDidEndScrollingAnimation:回调。

  • 使用animated参数,可以获取到动画过程中contentOffset的值
  • 使用animated参数,即使animated:NOscrollView也会进入tracking状态,而直接设置setContentOffset: 则不会。

所以在把MJRefreshHeadersetContentOffset:animated:方法注释掉以后, 因为scrollViewtracking状态没有变化,也不会出现bug


参考资料:
UIScrollView的delaysContentTouches与canCencelContentTouches属性

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • 美好的自愈力,竟看不出一丝伤痕
    deadinside阅读 113评论 0 0
  • 相处之道对我来说真的好难,我只想轻轻松松的相处,坦然的相对,这样都这么难,小甑真的不愧是天蝎女,心机也是深厚,当然...
    L林吖金J阅读 217评论 0 0
  • 半夜醒来,老二感冒又低烧了,帮他换了短袖和薄被,过储藏柜拿圣诞老人的乐高礼物,怕早上醒来他们起床了,亲友问美国圣诞...
    颜如苏阅读 217评论 0 1