1. 程式人生 > >正確處理iOS從下方滑出滾動檢視

正確處理iOS從下方滑出滾動檢視

在iOS 11開始,從最早的地圖應用到最近的捷徑,陸續有系統應用使用從下方滑出列表的形式,這種系統提供的圓角風格檢視用手勢劃出和隱藏時非常自然流暢。國內的一些應用也跟進了這種互動方式,但是我發現很大一部分APP都沒有正確的處理ScrollView滾動和檢視滾動的銜接,以至於相比於系統應用不夠自然。比如知乎的評論列表頁,需要手指拿開一下才能切換檢視移動和scroll滾動,銜接不夠連續。

此文希望幫到你能正確處理類似這種響應物件的切換,讓你的應用和系統應用一樣自然流暢。

 

(系統應用的滑出檢視)

(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瞭解,這裡就不一一列舉了。