正确处理 iOS 从下方滑出滚动视图

本文提供 Demo下载

在 iOS 11开始,从最早的地图应用到最近的捷径,陆续有系统应用使用从下方滑出列表的形式,这种系统提供的圆角风格视图用手势划出和隐藏时非常自然流畅。国内的一些应用也跟进了这种交互方式,但是我发现很大一部分APP都没有正确的处理 ScrollView 滚动和视图滚动的衔接,以至于相比于系统应用不够自然。比如知乎、抖音的评论列表页,需要手指拿开一下才能切换视图移动和 scroll 滚动,衔接不够连续。

此文没什么技术含量,只提供了一种处理技巧正确处理类似这种嵌套滚动时响应对象的切换,让你的应用和系统应用一样自然流畅。我更多的目的是一种呐喊,希望国内这些常用app能够注意到这些使用细节。

 

(系统应用的滑出视图)

(Demo跑出来的效果,压缩了分辨率,保留原始帧率)

因为此场景下有2种滑动,一种是视图向上移动 ScrollView 不滚动,另一种模式是 ScrollView 滑动而视图不移动,我们可能很自然的想到另外添加一个手势,禁止 ScrollView 滚动,然后再去驱动视图向上移动,在不需要的时候再去禁止这个手势和开启 ScrollView 滚动。如果这么处理,可能就会发现永远也得不到一个衔接自然的滚动体验。

我的方法是不要禁止 ScrollView 的手势,但是可以取消 ScrollView 滚动:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
  scrollView.contentOffset = CGPointMake(0, 0);
}

 

另外如果你理解了 ScrollView 的实现,那么就不用再另外添加手势了,因为本身 ScrollView 也是由手势驱动的,而我们可以直接得到 scrollView.panGestureRecognizer:

[scrollView.panGestureRecognizer addTarget:self action:@selector(panGestureHandle:)];

 

大部分事物处理就将交给 panGestureHandle 处理了:

// 核心函数:手势处理
- (void)panGestureHandle:(UIPanGestureRecognizer *)tap {
    static CGPoint startPoint;
    static CGPoint viewPoint;
    static BOOL isBegan;
    CGPoint endPoint;
    if ((self.tableView.contentOffset.y > 0 && self.sizeState == SlideScrollViewStateFull)
        || self.top < FULL_TOP) {
        isBegan = NO;
        [self panGestureEndWithViewPoint:viewPoint];
        return;
    }
    _scrollDecelerat = NO;
    self.tableView.showsVerticalScrollIndicator = NO;
    if (tap.state == UIGestureRecognizerStateBegan || isBegan == NO) {
        isBegan = YES;
        startPoint = [tap locationInView:self.superview];
        viewPoint = self.origin;
    }
    switch (tap.state) {
        case UIGestureRecognizerStateChanged: {
            endPoint = [tap locationInView:self.superview];
            CGFloat toPointY = viewPoint.y + (endPoint.y - startPoint.y);
            self.top = toPointY;
        }
            break;
        case UIGestureRecognizerStateEnded:
        case UIGestureRecognizerStateCancelled:
        case UIGestureRecognizerStateFailed: {
            isBegan = NO;
            [self panGestureEndWithViewPoint:viewPoint];
        }
        default:
            break;
    }
}

这里有需要注意的地方就是我们需要手动计算一次手势移动的距离,因为一个手势可能前一部分在响应 ScrollView 的滚动,而后一部分又切换到了移动视图,所以开始移动的点可能并不是手势开始的时候。

如果还有非 ScrollView 区域,这时候就可以另外添加一个手势到这个区域视图上了,如 demo 中的搜索框部分,处理函数和上面一样即可。

有更多细节比如使 ScrollView 取消惯性滚动等,可以马上查看Demo了解,这里就不一一列举了。

 

posted on 2018-10-23 22:22  刘继新  阅读(1350)  评论(11编辑  收藏  举报

导航