美文网首页
从点击屏幕无响应问题说起

从点击屏幕无响应问题说起

作者: ch32053 | 来源:发表于2020-03-26 20:34 被阅读0次

某天收到个问题反馈,由于群组新增红包功能,抢红包时点击红包偶现卡顿问题,现象是点击屏幕无响应。

首先分析场景,抢红包的同时不断的接收到新消息,大家可能体会过,眼看一个大红包被新消息顶出屏幕,等到点开的时候就被抢完了。

查看业务逻辑,接受到新消息后会自动滚屏到最新的一条消息。于是不断的收到消息,不断的滚屏。这个过程中用户触摸屏幕,就无响应了。

相关核心代码如下:

/// 实现该方法来监控消息的接收
- (void)receiveNewMessage
{
    [self.tableView scrollToBottomAnimated:YES];
}

/// scrollToBottomAnimated: 方法实现如下
- (void)scrollToBottomAnimated:(BOOL)animated {
    NSUInteger finalRow = MAX(0, [self numberOfRowsInSection:0] - 1);
    NSIndexPath *finalIndexPath = [NSIndexPath indexPathForRow:finalRow inSection:0];
    
    [self scrollToRowAtIndexPath:finalIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:animated];
}

问题来了,是什么原因造成无响应的?

一开始以为是滚屏的动作大量占用CPU时间,造成无法响应用户的触控事件。用Mock代码模拟了下不断收到新消息时点击红包的场景,发现除了触摸 tableView 不能响应外,其它非tableView的区域还是可以响应触控事件的。可见并非是这个原因。

由于收到消息滚屏到底部的过程是有动画效果的,不断的滚屏,动画是持续生效的。UIView默认执行动画时不响应触控事件的。试着去掉动画效果,直接滑动底部,就能响应用户的触控事件了。

- (void)scrollToBottomAnimated:(BOOL)animated {
    NSUInteger finalRow = MAX(0, [self numberOfRowsInSection:0] - 1);
    NSIndexPath *finalIndexPath = [NSIndexPath indexPathForRow:finalRow inSection:0];
    
    [UIView animateKeyframesWithDuration:animated?0.3:0 delay:0 options:UIViewKeyframeAnimationOptionAllowUserInteraction | UIViewKeyframeAnimationOptionBeginFromCurrentState animations:^{
        [self scrollToRowAtIndexPath:finalIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];
    } completion:^(BOOL finished) {
        
    }];
}

按上述滚屏逻辑即可响应用户的触控事件,可见是这个原因。

修复了这个问题,测试中发现偶尔还是点击后无响应。用户的触控事件应该已经传给了UIWindow,难道是没有传给应该响应事件的View?

Hook下UIWindow的sendEvent:方法,观察下事件的传递:

@implementation UIWindow (KeyWindow)

+ (void)load
{
    [self hookSwizzleSelector:@selector(sendEvent:) withSelector:@selector(y_sendEvent:)];
}

- (void)y_sendEvent:(UIEvent *)event
{
    [self y_sendEvent:event];
}

@end
image.png

点击后event如上图所示,可见事件有传递给View,观察到同时有Tap和Long事件,猜测是由于Long事件致使Tap事件失效导致的。查看代码后发现在父类中有添加Long事件:

        UILongPressGestureRecognizer *contentLongPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
        contentLongPress.minimumPressDuration = 0.5;
        [self.contentView addGestureRecognizer:contentLongPress];

修改后如下:

// 添加单击手势:
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureResponse:)];
    tapGesture.delegate = self;
    [[self.contentView gestureRecognizers] enumerateObjectsUsingBlock:^(__kindof UIGestureRecognizer * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj isKindOfClass:[UILongPressGestureRecognizer class]]) {
            [tapGesture requireGestureRecognizerToFail:obj];
        }
    }];
    [self.bubbleImageView addGestureRecognizer:tapGesture];
    
// 处理手势冲突,响应单击手势时,不响应其它手势
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
        return YES;
    }
    return NO;
}

还有优化的空间吗?指定执行滚屏时的runloop为NSDefaultRunLoopModel,用户触摸屏幕时runloop会切换为EventTracking,理论上这样应该会优先响应触控事件。但测试中发现区别不大,欢迎大家讨论 这样做是否能优先响应触控事件?

- (void)receiveNewMessage
{
    [self performSelector:@selector(receiveOnlineMessageScrollToBottom) withObject:nil afterDelay:0 inModes:@[NSDefaultRunLoopMode]];
    
}

- (void)receiveOnlineMessageScrollToBottom
{
    [self.tableView scrollToBottomAnimated:YES];
}

相关文章

  • 从点击屏幕无响应问题说起

    某天收到个问题反馈,由于群组新增红包功能,抢红包时点击红包偶现卡顿问题,现象是点击屏幕无响应。 首先分析场景,抢红...

  • UIButton点击无响应问题总结

    UIButton点击没有响应的问题有很多种,现在总结已知的以下几种:1.UIButton的父控件不具备响应事件的能...

  • 响应链

    响应链:顾名思义,是一个链条,响应传递的链条。 当我们在屏幕上点击了一个地方发生了什么? A.用户点击屏幕,系统的...

  • mac uiautomatorview无法使用

    问题描述: Mac 电脑上uiautomatorview无法使用:按钮点击无响应 解决办法 1、open ~/...

  • BAT大咖助力 全面升级Android面试-3android基础

    一,Activity面试详解 1,activity的四种状态 running 活动状态。用户点击屏幕,屏幕做出响应...

  • iOS面试题-第五页

    41.介绍响应者链. 当用户点击屏幕,能够产生响应的对象组成的链. 继承自NSResponder,响应者链能够中断...

  • UI响应链,事件分发,以及修改响应控件

    UI响应链,事件传递 一、事件分发(为了寻找响应事件的控件) 1.当我们点击屏幕的时候,系统会将我们的点击事件添加...

  • iOS 事件响应原理:从点击屏幕到作出反馈

    先来大致说一下原理: 1、第一步,定位点击了哪个view 一个应用,有那么多的界面元素,应用怎么就知道我点击了哪里...

  • 消息响应链

    当用户点击了屏幕中的button,事件是如何响应的呢,为什么点击其他区域没有事件响应呢?事件又是怎么触发的呢? 首...

  • 面试题

    响应链: 用户点击屏幕产生事件 -> UIApplication 开始事件分发-> UIWindow-> Subv...

网友评论

      本文标题:从点击屏幕无响应问题说起

      本文链接:https://www.haomeiwen.com/subject/vpbsyhtx.html