UIPageViewController兼顾无弹簧效果和侧滑返回

最终效果图


简书地址

前言

闲来无事翻着公司的项目总觉得用UIPageViewControlle封装的分页控制器既不能侧滑返回又有弹簧效果很不爽,于是开始折腾一下,期间遇到了一些坑,但终于柳暗花明。在此记录一下填坑过程,分享给大家。

目标

  1. 去掉UIPageViewController在Scroll样式下的弹簧效果。
  2. 实现分页控制器的侧滑返回。

开始

网上搜索了下禁止弹簧效果的相关这个问题,发现了一段代码:

__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;
}

加上代码果然可以,但是好景不长,当我点击分段控制器分页后,滚动手势居然失效了这显然不是我想要的效果。并且直接改变UIScrollView的代理这种方法显然不好,虽然delegate初始值为空,但这种做法不安全,加上还要更改非公开view的属性,就更加的不安全了。然后我打印了UIPageViewController内的一些信息:

(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>)>>
    }>
)

发现UIPageViewController的view下有一个叫UIQueuingScrollView的view,他的父类是UIScrollView,并且有三个手势其中UIScrollViewPanGestureRecognizer手势就是UIQueuingScrollView的父类UIScrollView公开的panGestureRecognizer手势,大家可以打印地址查看。既然可以直接拿到控制UIPageViewController翻页的手势那问题就清晰了,只需要在适当的时候禁止掉这个手势不就可以不让用户继续滚动了吗。

解决问题

1.关键方法

//UIGestureRecognizerDelegate 返回YES才响应手势 返回NO手势失效
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer;

//UIGestureRecognizer 当otherGestureRecognizer手势失效时才相应调用此方法的手势
- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer;

2.手势

  1. 侧滑返回手势
  2. UIPageViewController的滚动手势
  3. 为UIQueuingScrollView新添加一个pan手势(fakePan)

3.关键代码

 __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.说明

侧滑返回我是用的UINavigationController+FDFullscreenPopGesture分类,fd_fullscreenPopGestureRecognizer就是侧滑返回的手势。当_fakePan和fd_fullscreenPopGestureRecognizer手势不响时分页视图的滚动手势才响应。在最后一页左滑或第一页右滑时_fakePan才响应。fd_fullscreenPopGestureRecognizer的响应条件是UINavigationController+FDFullscreenPopGesture内部实现的我们这里只需要设置在显示第一页时才开启这个手势即可。

总结

  1. 通过手势之间的优先级(个人觉得“手势锁”更形象),实现了UIPageViewController的无弹簧效果和侧滑返回。
  2. 缺陷:快速滑动或一直拖动不放松时会出现弹簧效果,因为是手势实现的不可避免的会出现这样的问题,个人觉得不太影响效果。
  3. 如果不需要侧滑返回只要删除与侧滑手势有关的代码。
  4. 在实现公开API无法直接实现的效果是应该尽量的不去改动非公开属性,而是在此基础上增加实现。

Demo地址

Demo-GitHub

产品公测

休闲咖,一款基于移动互联网的技能分享平台, 与国民老公王思聪主打的”hello 女神”节目,手机app《鱼泡泡》同款类型的技能分享社交平台。
休闲咖第一版本今天正式开始公测. 手机直播,电商购物功能也即将发布,敬请期待. 希望大家注册支持,多提意见,谢谢。

安卓下载

苹果下载

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值