iOS全屏侧滑返回及支持UIScrollView和UIPageViewController返回手势

3,924 阅读2分钟

老实人先放链接GithubDemo

关键词

  • 解决自定义导航栏按钮后侧滑失效的问题
  • 解决UIScrollView及其子类与侧滑返回冲突的问题
  • FDFullscreenPopGesture/UIPageViewController/UIScrollView
  • Pop Gesture/Pan Gesture

使用

  1. 下载FDFullscreenPopGesture,无论是源码还是pod管理
  2. 添加UIScroll+TGPopGesture,可以再FDFullscreenPopGesture添加,也可单独文件
  3. 在包含嵌套的页面内,根据设置scrollViewpageControllerCurrentIndex来判定是否支持侧滑返回

基本内容和FDFullscreenPopGesture一样

隐藏导航栏

// viewController -> viewDidLoad
self.fd_prefersNavigationBarHidden = YES;

禁止拖拽手势

// ViewController -> viewDidLoad
self.fd_interactivePopDisabled = YES;

设置可拖拽边界

self.fd_interactivePopMaxAllowedInitialDistanceToLeftEdge = 50;

追加UIScroll + PageScrollIndex类别

为什么追加类别?

我们知道UIScrollView是通过Pan手势进行的拖拽处理,而我们的Navigation的返回手势也同样是Pan。所以在导航栏栈内的Controller如果包含横向可拖拽的UIScrollView及其子类或扩展类那么基本上都会造成手势的冲突,从而导致拖拽手势只会被滚动视图相应,而导航栏的返回拖拽手势无效的问题。那么我们如何处理这个问题呢?

FDFullScreenPopGesture提供的方案

如果要在ViewController中将全屏弹出手势与scrollView或scrollView的子类一起使用,则应自定义scrollView或scrollView的子类并重载gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:这个类。

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    if (self.contentOffset.x <= 0) {
        if ([otherGestureRecognizer.delegate isKindOfClass:NSClassFromString(@"_FDFullscreenPopGestureRecognizerDelegate")]) {
            return YES;
        }
    }
    return NO;
}

博主提供的方案

绑定属性,根据属性判断位置;根据位置判断是否响应手势

为什么使用自己绑定的属性?

如框架内的方案可以支持绝大多数情况。但是像一些复用的情况。如UIPageViewController内,无论拖拽到哪里,它的内部的scrollViewcontentOfset都会重置到width, 0,导致无法根据offset.x来判断手势的响应。

所以楼主觉得,如果使用页面page进行标识,是不是就可以处理这种了。当页面是第一个页面的时候,左滑相应Pop的手势,当页面不在0 - width 内,则由scrollView的拖拽手势响应拖拽操作;

具体操作如下:


@interface UIScrollView (TGPopGesture)

/// 在滚动结束后或滚动时计算当前是否停留在最左侧界面,即0的时候。支持全屏侧滑返回
@property(nonatomic, assign) NSUInteger pageControllerCurrentIndex;

@end

@implementation UIScrollView (TGPopGesture)

- (NSUInteger)pageControllerCurrentIndex {
    return [objc_getAssociatedObject(self, _cmd) integerValue];
}

- (void)setPageControllerCurrentIndex:(NSUInteger)pageControllerCurrentIndex {
    objc_setAssociatedObject(self, @selector(pageControllerCurrentIndex), @(pageControllerCurrentIndex), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    if (self.pageControllerCurrentIndex == 0) {
        if ([otherGestureRecognizer.delegate isKindOfClass:NSClassFromString(@"_FDFullscreenPopGestureRecognizerDelegate")]) {
            return YES;
        }
    }
    return NO;
}

@end

如何支持UIPageViewController

如何支持UIPageViewController

  • 获取UIPageViewControlelr的scrollView
for (UIScrollView *scrollView in self.pageVC.view.subviews) {
    if ([scrollView isKindOfClass:[UIScrollView class]]) {
        self.scrollView = scrollView;
        self.scrollView.delegate = self;
        break;
    }
}
  • 在适当的时候设置扩展属性,如pageController的切换代理,设置scroll的属性,当pageControllerCurrentIndex为0的时候支持拖拽返回
//滚动切换时调用
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray<UIViewController *> *)previousViewControllers transitionCompleted:(BOOL)completed {
    if (self.scrollView) {
        self.scrollView.pageControllerCurrentIndex = _currentIndex;
    }
}
  • 在其它适当的时候更新pageControllerCurrentIndex