一妖碉、場景
最近在做一個(gè)包含閱讀內(nèi)容的項(xiàng)目狞膘,在電子書部分使用了 UIPageViewController 來滿足仿真翻頁與滑動(dòng)翻頁讯蒲,在滑動(dòng)翻頁的過程遇到了各種奇葩的頁號(hào)跳動(dòng)問題掸鹅。
也在百度上找了很多相關(guān)資料童谒,都說它是一個(gè) bug单旁,缺陷。
大部分要么說放棄這個(gè)組件惠啄,使用 UIScraollView 來模擬慎恒。還有一個(gè)解決方案就是使用一段異步重賦值的代碼:
__block XXViewController *blocksafeSelf = self;
[self.pageViewController setViewControllers:[NSArray arrayWithObject:[self viewControllerAtIndex:selectIndex]] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:^(BOOL finished) { //這里的YES是提供一個(gè)動(dòng)畫效果
if (finished) {
dispatch_async(dispatch_get_main_queue(), ^{
[blocksafeSelf.pageViewController setViewControllers:[NSArray arrayWithObject:[blocksafeSelf viewControllerAtIndex:selectIndex]] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:NULL]; //這是實(shí)際起到跳轉(zhuǎn)作用的代碼,animated:NO
});
}
}];
二撵渡、重新理解代理方法
UIPageViewController 有兩個(gè)前一頁與后一頁的代理方法融柬,在仿真翻頁中可以正確工作。
- (nullable UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController;
- (nullable UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController;
我以前理解這兩個(gè)方法就當(dāng)前頁的上一頁與下一頁趋距,這種在仿真翻頁中是沒有問題的粒氧,但在滑動(dòng)模式中,這種理解偏差大了节腐。
正確的理解是外盯,上一頁下一頁是相對(duì)于代理方法傳入的那個(gè)視圖控制器而言的,不能直接使用當(dāng)前的章節(jié)與頁數(shù)來計(jì)算翼雀。
三饱苟、解決方案
1、在指定當(dāng)前頁面時(shí)狼渊,不要使用動(dòng)畫模式箱熬。
[_pageViewController setViewControllers:[self makeCurrentReadViewControllers]
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:nil];
如果使用動(dòng)畫模式,會(huì)導(dǎo)致第一次向左滑動(dòng)的時(shí)候狈邑,重復(fù)顯示第一頁城须。
2、在上一頁與下一頁的代理方法中米苹,使用相對(duì)計(jì)算章節(jié)與頁數(shù)
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerBeforeViewController:(UIViewController *)viewController
{
_pageChange = _page;
_chapterChange = _chapter;
if (_isScrollModel) {
// 非常重要糕伐,否則在滾動(dòng)模式下拿不到正確的頁號(hào),因?yàn)樗⒉皇侵府?dāng)前頁的上一頁
LSYReadViewController *pageVC = (LSYReadViewController *) viewController;
_pageChange = pageVC.recordModel.page;
_chapterChange = pageVC.recordModel.chapter;
}
// 第一章第一頁沒有前一頁了
if (_chapterChange == 0 &&_pageChange == 0) {
// 返回 nil 只會(huì)出現(xiàn)背景蘸嘶,沒有新的vc
NSLog(@"已經(jīng)是第一頁了A记啤!?鹘稀]喊!");
return nil;
}
// 第一頁回去就到了前一章
if (_pageChange == 0) {
_chapterChange--;
LSYChapterModel *chapterModel = _model.chapters[_chapterChange]; // 前一章的model
chapterModel.isReady = YES;
_pageChange = chapterModel.pageCount - 1;// 頁號(hào)為前一章的最大頁數(shù)
} else{
// 否則正常的減一頁
_pageChange--;
}
return [self readViewWithChapter:_chapterChange page:_pageChange];
}
// 后一頁
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerAfterViewController:(UIViewController *)viewController
{
// 當(dāng)前章節(jié)與頁號(hào)
_pageChange = _page;
_chapterChange = _chapter;
if (_isScrollModel) {
// 非常重要雪情,否則在滾動(dòng)模式下拿不到正確的頁號(hào),因?yàn)樗⒉皇侵府?dāng)前頁的下一頁
LSYReadViewController *pageVC = (LSYReadViewController *) viewController;
_pageChange = pageVC.recordModel.page;
_chapterChange = pageVC.recordModel.chapter;
}
// 如果到了最后一章的最后一頁你辣,返回nil
if (_chapterChange == _model.chapters.count - 1 &&
_pageChange == _model.chapters.lastObject.pageCount - 1
) {
return nil;
}
// 下一章
LSYChapterModel *chapterModel = _model.chapters[_chapterChange];
chapterModel.isReady = YES;
if (_pageChange == chapterModel.pageCount - 1) {
_chapterChange += 1;
_pageChange = 0;
} else {
// 本章下一節(jié)
_pageChange += 1;
}
return [self readViewWithChapter:_chapterChange page:_pageChange];
}
四巡通、手動(dòng)翻頁
完成點(diǎn)擊左右頁面自動(dòng)滑動(dòng)翻頁
// 動(dòng)畫向前翻頁
- (void)moveToPrevPage
{
UIViewController *vc = [self pageViewController:self.pageViewController
viewControllerBeforeViewController:self.pageViewController.viewControllers.firstObject];
if (vc == nil) {
return;
}
__weak typeof(self) weakself = self;
[_pageViewController setViewControllers:@[vc]
direction:UIPageViewControllerNavigationDirectionReverse
animated:YES // 滑動(dòng)
completion:^(BOOL finished) {
if (finished) {
// 完成后要重置一下尘执,避免有 bug
dispatch_async(dispatch_get_main_queue(), ^{
[weakself.pageViewController
setViewControllers:@[vc]
direction:UIPageViewControllerNavigationDirectionReverse
animated:NO
completion:NULL];
});
}
}];
}
五、總結(jié)
這個(gè)組件其實(shí)是沒有 bug 的宴凉,按蘋果嚴(yán)謹(jǐn)?shù)娘L(fēng)格來說誊锭,這個(gè)類似問題出現(xiàn)在 iOS6的時(shí)候就有人提出來了,如果是問題弥锄,早就修復(fù)了丧靡。