前言
閑來(lái)無(wú)事翻著公司的項(xiàng)目總覺(jué)得用UIPageViewControlle封裝的分頁(yè)控制器既不能側(cè)滑返回又有彈簧效果很不爽蛙吏,于是開(kāi)始折騰一下,期間遇到了一些坑,但終于柳暗花明。在此記錄一下填坑過(guò)程,分享給大家颅湘。
目標(biāo)
- 去掉UIPageViewController在Scroll樣式下的彈簧效果。
- 實(shí)現(xiàn)分頁(yè)控制器的側(cè)滑返回栗精。
開(kāi)始
網(wǎng)上搜索了下禁止彈簧效果的相關(guān)這個(gè)問(wè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;
}
加上代碼果然可以,但是好景不長(zhǎng)悲立,當(dāng)我點(diǎn)擊分段控制器分頁(yè)后鹿寨,滾動(dòng)手勢(shì)居然失效了這顯然不是我想要的效果。并且直接改變UIScrollView的代理這種方法顯然不好薪夕,雖然delegate初始值為空脚草,但這種做法不安全,加上還要更改非公開(kāi)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下有一個(gè)叫UIQueuingScrollView的view,他的父類是UIScrollView,并且有三個(gè)手勢(shì)其中UIScrollViewPanGestureRecognizer手勢(shì)就是UIQueuingScrollView的父類UIScrollView公開(kāi)的panGestureRecognizer手勢(shì),大家可以打印地址查看姑隅。既然可以直接拿到控制UIPageViewController翻頁(yè)的手勢(shì)那問(wèn)題就清晰了写隶,只需要在適當(dāng)?shù)臅r(shí)候禁止掉這個(gè)手勢(shì)不就可以不讓用戶繼續(xù)滾動(dòng)了嗎。
解決問(wèn)題
1.關(guān)鍵方法
//UIGestureRecognizerDelegate 返回YES才響應(yīng)手勢(shì) 返回NO手勢(shì)失效
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer;
//UIGestureRecognizer 當(dāng)otherGestureRecognizer手勢(shì)失效時(shí)才相應(yīng)調(diào)用此方法的手勢(shì)
- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer;
2.手勢(shì)
- 側(cè)滑返回手勢(shì)
- UIPageViewController的滾動(dòng)手勢(shì)
- 為UIQueuingScrollView新添加一個(gè)pan手勢(shì)(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)
{
//新添加的手勢(shì)讲仰,起手勢(shì)鎖的作用
_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.說(shuō)明
側(cè)滑返回我是用的UINavigationController+FDFullscreenPopGesture分類慕趴,fd_fullscreenPopGestureRecognizer就是側(cè)滑返回的手勢(shì)。當(dāng)_fakePan和fd_fullscreenPopGestureRecognizer手勢(shì)不響時(shí)分頁(yè)視圖的滾動(dòng)手勢(shì)才響應(yīng)。在最后一頁(yè)左滑或第一頁(yè)右滑時(shí)_fakePan才響應(yīng)秩贰。fd_fullscreenPopGestureRecognizer的響應(yīng)條件是UINavigationController+FDFullscreenPopGesture內(nèi)部實(shí)現(xiàn)的我們這里只需要設(shè)置在顯示第一頁(yè)時(shí)才開(kāi)啟這個(gè)手勢(shì)即可霹俺。
總結(jié)
- 通過(guò)手勢(shì)之間的優(yōu)先級(jí)(個(gè)人覺(jué)得“手勢(shì)鎖”更形象)柔吼,實(shí)現(xiàn)了UIPageViewController的無(wú)彈簧效果和側(cè)滑返回毒费。
- 缺陷:快速滑動(dòng)或一直拖動(dòng)不放松時(shí)會(huì)出現(xiàn)彈簧效果,因?yàn)槭鞘謩?shì)實(shí)現(xiàn)的不可避免的會(huì)出現(xiàn)這樣的問(wèn)題愈魏,個(gè)人覺(jué)得不太影響效果觅玻。
- 如果不需要側(cè)滑返回只要?jiǎng)h除與側(cè)滑手勢(shì)有關(guān)的代碼。
- 在實(shí)現(xiàn)公開(kāi)API無(wú)法直接實(shí)現(xiàn)的效果是應(yīng)該盡量的不去改動(dòng)非公開(kāi)屬性培漏,而是在此基礎(chǔ)上增加實(shí)現(xiàn)溪厘。