UIPageViewController兼顧無彈簧效果和側(cè)滑返回


效果圖.gif

前言

閑來無事翻著公司的項目總覺得用UIPageViewControlle封裝的分頁控制器既不能側(cè)滑返回又有彈簧效果很不爽,于是開始折騰一下,期間遇到了一些坑,但終于柳暗花明。在此記錄一下填坑過程,分享給大家。

目標(biāo)

  1. 去掉UIPageViewController在Scroll樣式下的彈簧效果。
  2. 實(shí)現(xiàn)分頁控制器的側(cè)滑返回。

開始

網(wǎng)上搜索了下禁止彈簧效果的相關(guān)這個問題,發(fā)現(xiàn)了一段代碼:

__block UIScrollView *scrollView = nil;
[_pageViewController.view.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    if ([obj isKindOfClass:[UIScrollView class]])
    {
        scrollView = (UIScrollView *)obj;
    }
}];
if (scrollView ) scrollView.delegate = self;
        
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    scrollView.bounces = NO;
}

加上代碼果然可以,但是好景不長,當(dāng)我點(diǎn)擊分段控制器分頁后,滾動手勢居然失效了這顯然不是我想要的效果。并且直接改變UIScrollView的代理這種方法顯然不好,雖然delegate初始值為空,但這種做法不安全,加上還要更改非公開view的屬性,就更加的不安全了。然后我打印了UIPageViewController內(nèi)的一些信息:

(lldb) po _pageViewController.view.subviews
<__NSArrayM 0x608000245550>(
<_UIQueuingScrollView: 0x7fcaad02d400; frame = (0 0; 375 667); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x608000247050>; layer = <CALayer: 0x60800023fcc0>; contentOffset: {375, 0}; contentSize: {1125, 667}>
)

(lldb) po [_pageViewController.view.subviews[0] superclass]
UIScrollView

(lldb) po [_pageViewController.view.subviews[0] valueForKey:@"gestureRecognizers"]
<__NSArrayI 0x60000025b900>(
<UIScrollViewDelayedTouchesBeganGestureRecognizer: 0x6080001abb40; state = Possible; delaysTouchesBegan = YES; view = <_UIQueuingScrollView 0x7fcaad02d400>; target= <(action=delayed:, target=<_UIQueuingScrollView 0x7fcaad02d400>)>>,
<UIScrollViewPanGestureRecognizer: 0x7fcaac608b10; state = Possible; delaysTouchesEnded = NO; view = <_UIQueuingScrollView 0x7fcaad02d400>; target= <(action=handlePan:, target=<_UIQueuingScrollView 0x7fcaad02d400>)>; must-fail = {
        <UIScrollViewPagingSwipeGestureRecognizer: 0x6080003c2490; state = Possible; view = <_UIQueuingScrollView 0x7fcaad02d400>; target= <(action=_handleSwipe:, target=<_UIQueuingScrollView 0x7fcaad02d400>)>>
    }>,
<UIScrollViewPagingSwipeGestureRecognizer: 0x6080003c2490; state = Possible; view = <_UIQueuingScrollView 0x7fcaad02d400>; target= <(action=_handleSwipe:, target=<_UIQueuingScrollView 0x7fcaad02d400>)>; must-fail-for = {
        <UIScrollViewPanGestureRecognizer: 0x7fcaac608b10; state = Possible; delaysTouchesEnded = NO; view = <_UIQueuingScrollView 0x7fcaad02d400>; target= <(action=handlePan:, target=<_UIQueuingScrollView 0x7fcaad02d400>)>>
    }>
)

發(fā)現(xiàn)UIPageViewController的view下有一個叫UIQueuingScrollView的view,他的父類是UIScrollView,并且有三個手勢其中UIScrollViewPanGestureRecognizer手勢就是UIQueuingScrollView的父類UIScrollView公開的panGestureRecognizer手勢,大家可以打印地址查看。既然可以直接拿到控制UIPageViewController翻頁的手勢那問題就清晰了,只需要在適當(dāng)?shù)臅r候禁止掉這個手勢不就可以不讓用戶繼續(xù)滾動了嗎。

解決問題

1.關(guān)鍵方法

//UIGestureRecognizerDelegate 返回YES才響應(yīng)手勢 返回NO手勢失效
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer;

//UIGestureRecognizer 當(dāng)otherGestureRecognizer手勢失效時才相應(yīng)調(diào)用此方法的手勢
- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer;

2.手勢

  1. 側(cè)滑返回手勢
  2. UIPageViewController的滾動手勢
  3. 為UIQueuingScrollView新添加一個pan手勢(fakePan)

3.關(guān)鍵代碼

 __block UIScrollView *scrollView = nil;
[_pageViewController.view.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    if ([obj isKindOfClass:[UIScrollView class]]) scrollView = (UIScrollView *)obj;
        
}];
if(scrollView)
{
//新添加的手勢,起手勢鎖的作用
    _fakePan = [UIPanGestureRecognizer new];
    _fakePan.delegate = self;
    [scrollView addGestureRecognizer:_fakePan];
    [scrollView.panGestureRecognizer requireGestureRecognizerToFail:self.navigationController.fd_fullscreenPopGestureRecognizer];
    [scrollView.panGestureRecognizer requireGestureRecognizerToFail:_fakePan];
    [_fakePan requireGestureRecognizerToFail:self.navigationController.fd_fullscreenPopGestureRecognizer];
}

//UIGestureRecognizerDelegate
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer
{
    CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view];
    if (translation.x <= 0)
    {
        return (_currentIndex == _vcArray.count - 1 && transitionFinish);
    }
    else
    {
        return (_currentIndex ==0 && transitionFinish);
    }
}

4.說明

側(cè)滑返回我是用的UINavigationController+FDFullscreenPopGesture分類,fd_fullscreenPopGestureRecognizer就是側(cè)滑返回的手勢。當(dāng)_fakePan和fd_fullscreenPopGestureRecognizer手勢不響時分頁視圖的滾動手勢才響應(yīng)。在最后一頁左滑或第一頁右滑時_fakePan才響應(yīng)。fd_fullscreenPopGestureRecognizer的響應(yīng)條件是UINavigationController+FDFullscreenPopGesture內(nèi)部實(shí)現(xiàn)的我們這里只需要設(shè)置在顯示第一頁時才開啟這個手勢即可。

總結(jié)

  1. 通過手勢之間的優(yōu)先級(個人覺得“手勢鎖”更形象),實(shí)現(xiàn)了UIPageViewController的無彈簧效果和側(cè)滑返回。
  2. 缺陷:快速滑動或一直拖動不放松時會出現(xiàn)彈簧效果,因為是手勢實(shí)現(xiàn)的不可避免的會出現(xiàn)這樣的問題,個人覺得不太影響效果。
  3. 如果不需要側(cè)滑返回只要刪除與側(cè)滑手勢有關(guān)的代碼。
  4. 在實(shí)現(xiàn)公開API無法直接實(shí)現(xiàn)的效果是應(yīng)該盡量的不去改動非公開屬性,而是在此基礎(chǔ)上增加實(shí)現(xiàn)。

Demo地址

Demo-GitHub

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,840評論 4 61
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,366評論 25 708
  • 春與秋之間有夏相隔 秋的顏色是春的守望 問春你是眷戀生機(jī)盎然的綠? 還是愛著秋為你呈現(xiàn)的金黃? 春在我的耳邊輕聲細(xì)...
    獨(dú)白隨心閱讀 350評論 1 2
  • 說明 本文是作者Lefe所創(chuàng),轉(zhuǎn)載請注明出處,如果你在閱讀的時候發(fā)現(xiàn)問題歡迎一起討論。本文會不斷更新。 正文 由于...
    Lefe閱讀 654評論 0 0
  • 端午節(jié)放假三天 回家擼了三天娃 然后被三波人分別催著談戀愛。 依次是我舅媽、閨蜜、叔叔伯伯姑姑嬸嬸。 理由是到了2...
    噫呀_August閱讀 984評論 2 0

友情鏈接更多精彩內(nèi)容