我的UIPageViewController在iOS 5中运行良好。但是当iOS 6出现时,我想使用新的滚动转换样式(UIPageViewControllerTransitionStyleScroll)而不是页面卷曲样式。这导致我的UIPageViewController破坏。
除了我打电话给setViewControllers:direction:animated:completion:
之后,它才能正常工作。之后,下次用户手动滚动一页时,我们会得到错误的页面。这有什么不对?
答案 0 :(得分:76)
我对此错误的解决方法是在完成设置相同的viewcontroller但没有动画时创建一个块
__weak YourSelfClass *blocksafeSelf = self;
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:^(BOOL finished){
if(finished)
{
dispatch_async(dispatch_get_main_queue(), ^{
[blocksafeSelf.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:NULL];// bug fix for uipageview controller
});
}
}];
答案 1 :(得分:56)
这实际上是UIPageViewController中的一个错误。它仅在使用滚动样式(UIPageViewControllerTransitionStyleScroll)时发生,并且仅在使用动画:YES 调用setViewControllers:direction:animated:completion:
之后。因此有两种解决方法:
请勿使用UIPageViewControllerTransitionStyleScroll。
或者,如果您致电setViewControllers:direction:animated:completion:
,请仅使用animated:NO
。
要清楚地看到错误,请拨打setViewControllers:direction:animated:completion:
,然后在界面(作为用户)中,手动向左(向后)导航到上一页。您将导航回错误的页面:根本不是前一页,而是调用setViewControllers:direction:animated:completion:
时您所在的页面。
错误的原因似乎是,当使用滚动样式时,UIPageViewController会执行某种内部缓存。因此,在调用setViewControllers:direction:animated:completion:
之后,它无法清除其内部缓存。它认为它知道前一页是什么。因此,当用户向左导航到前一页时,UIPageViewController 无法调用dataSource方法pageViewController:viewControllerBeforeViewController:
,或使用错误的当前视图控制器调用它。
我发布了一部电影,清楚地演示了如何查看错误:
http://www.apeth.com/PageViewControllerBug.mov
编辑在iOS 8中,此错误可能已修复。
编辑有关此错误的另一个有趣的解决方法,请参阅以下答案:https://stackoverflow.com/a/21624169/341994
答案 2 :(得分:3)
Here是我整理的“粗暴”主旨。它包含一个受阿尔茨海默病影响的UIPageViewController替代方案(即:它没有Apple实现的内部缓存)。
这个类不完整,但它适用于我的情况(即:水平滚动)。
答案 3 :(得分:1)
从iOS 12开始,原始问题中描述的问题似乎已基本解决。我之所以提出这个问题,是因为我在自己的特定设置中仍然遇到过这种情况,但这种情况仍然会发生,因此这里使用了“几乎”一词。
我遇到此问题的设置是:
1)该应用是通过深层链接打开的
2)基于链接,应用程序必须切换到特定选项卡并通过推入在那里打开给定项目
3)仅当用户先前未选择目标选项卡(因此UIPageViewController应该设置为该选项卡的动画)并且仅当setViewControllers:direction:animated:completion:
具有animated = true
时,才描述问题
4)推送返回到包含UIPageViewController的视图控制器后,发现后者是一团糟-即使调试显示在逻辑级别上一切正常,它也呈现了完全错误的视图控制器
我认为问题的根源是在setViewControllers:direction:animated:completion:
调用之后我很快地推动了视图控制器,因此UIPageViewController没有机会完成某些操作(可能是动画,缓存或其他操作)。
通过延迟我在UI中的程序化导航,只需给UIPageViewController一些空闲时间
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { ... }
为我解决了这个问题。而且,它还使从视觉上对用户友好的链接项的程序化打开成为可能。
希望这对处于类似情况的人有所帮助。
答案 4 :(得分:0)
此错误仍存在于iOS9中。我使用的是与上面发布的George Tsifrikas相同的解决方法,但是使用的是Swift版本:
pageViewController.setViewControllers([page], direction: direction, animated: true) { done in
if done {
dispatch_async(dispatch_get_main_queue()) {
self.pageViewController.setViewControllers([page], direction: direction, animated: false, completion: {done in })
}
}
}
答案 5 :(得分:0)
因为pageviewVC滑动时会调用多个childVC。但是我们只需要最后一页可见即可。
对于我来说,我需要在更改pageView时更改分段控件的索引。
希望这对某人有所帮助:)
extension ViewController: UIPageViewControllerDelegate {
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
guard let pageView = pageViewController.viewControllers?.first as? ChildViewController else { return }
segmentedControl.set(pageView.index)
}
}
答案 6 :(得分:0)
Swift中另一个简单的解决方法:只需重置UIPageViewController的数据源。这显然清除了其缓存并解决了该错误。这是一种直接进入页面而不中断后续滑动的方法。在下面,m_pages是视图控制器的数组。我将在下面显示如何找到currPage(当前页面的索引)。
func goToPage(_ index: Int, animated: Bool)
{
if m_pages.count > 0 && index >= 0 && index < m_pages.count && index != currPage
{
var dir: UIPageViewController.NavigationDirection
if index < currPage
{
dir = UIPageViewController.NavigationDirection.reverse
}
else
{
dir = UIPageViewController.NavigationDirection.forward
}
m_pageViewController.setViewControllers([m_pages[index]], direction: dir, animated: animated, completion: nil)
delegate?.tabDisplayed(sender: self, index: index)
m_pageViewController.dataSource = self;
}
}
如何查找当前页面:
var currPage: Int
{
get
{
if let currController = m_pageViewController.viewControllers?[0]
{
return m_pages.index(of: currController as! AtomViewController) ?? 0
}
return 0
}
}
答案 7 :(得分:-4)
<强> 声明: 强>
似乎Apple已经发现开发人员正在使用UIPageViewController,这些应用程序远远超出了 原本打算用苹果公司首先根据他们的设计选择。而不是以手势驱动的线性方式使用它 PVC通常用于以编程方式跳转到随机 结构化环境中的职位。所以他们已经增强了对UIPageViewController的实现,并且该类现在调用了两个DataSource 回调
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
在UIPageViewController上使用
设置新的contentViewController之后[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
即使是动画的翻页而是建议在例如页面层次结构,如书籍或连续页面的PDF。虽然 - 我怀疑苹果从HIG的角度来看 非常喜欢看到PVC以这种方式使用,但是 - 它并没有打破向后兼容性,这是一个简单的解决方案,所以 - 他们最终做到了。实际上,只是再一次调用两个DataSource方法中的一个,这在线性环境中是绝对不必要的,其中页面(ViewControllers)已经被兑现以供以后使用。
但是,即使这种增强对某些用例来说非常方便,该类的初始行为也不会被视为错误。很多开发人员都这样做 - 在其他方面也是如此 SO上的帖子指责UIPageViewController的不当行为 - 而是强调对其设计,目的和功能的广泛误解。
在这个伟大的设施中,我没有试图冒犯我的同事开发人员,但我仍然决定不删除我最初的研究报告。这清楚地向OP解释了PVC的机制以及为什么他的假设是错误的,他必须在这里处理一个错误。
对于在UIPageViewController实现中遇到一些错综复杂的其他开发人员来说,这也可能有用!
原始答案:
一遍又一遍地阅读所有答案后 - 包括了 接受了一个 - 还有一件事要说......
UIPageViewController
的设计绝对是 FLAWLESS 以及所有
为了规避所谓的bug,你提交的黑客攻击只不过是
你自己错误的假设的补救办法,因为你在这个假设中蠢蠢欲动
第一名!!!
根本没有任何错误!你只是在与框架作斗争。我将解释原因!
有很多关于页码和索引的讨论!这些是概念
控制器知道 NOTHING !它唯一知道的是 - 它正在显示
一些内容(顺便提一下,由你提供的 dataViewController ),它可以
做一些像右/左动画一样的模仿翻页。
CURL
或SCROLL
... !!!
在 pageViewController 的世界中,只存在当前的SPACE
(让我们来电
只是这样可以避免与页面和索引混淆。)
当您最初设置 pageViewController 时,它只关注这个SPACE
。
只有当你开始平移它的视图时,它才会开始询问它的DataSource
是什么
最终应该显示以防左/右翻转发生。你几时开始
向左平移,PVC首先询问BEFORE-SPACE
,然后是
AFTER-SPACE
,如果你从右边开始,它会反过来。
完成动画后(PVC的视图显示新SPACE
)
PVC认为这个SPACE
是它的新宇宙中心,而它就是它的核心
询问DataSource
关于它仍然不知道的那个。的情况下
向右转,它想知道新的AFTER
空间和
完成向左转弯的情况下,它会要求新的BEFORE
空格。
旧的BEFORE
空格(在动画之前)是在完成转弯的情况下
权利完全过时,并尽快解除分配。旧center
现在是新BEFORE
,而前AFTER
是新的center
。一切都好
向右移了一步。
所以 - 没有谈论&#39; 哪个页面&#39;或&#39; 无论索引&#39; - 只是简单 - 有一个BEFORE
或
一个AFTER
空间。如果你将NIL返回到DataSource
回调中的一个回复
假设它位于 range of SPACES
的一个极端。如果你将NIL返回到两者
回调它假设它显示 one and only SPACE
有,并且永远不会
再次呼叫DataSource
回调了!逻辑取决于你!你定义
代码中的页面和索引!不是PVC !!!
对于班级用户,有两种与PVC互动的方式。
A pan-gesture that indicates whether a turn to the BEFORE/AFTER space is desired
A method - namely setViewControllers:direction:animated:completion:
此方法与平移手势完全相同。你指的是
方向(例如UIPageViewControllerNavigationDirectionBackward/Forward
)
对于动画 - 如果有一个意图 - 换句话说只是意味着 - &gt;即将
BEFORE
或AFTER
...
再次 - 没有提及索引,页码等...... !!!
这只是一种以编程方式实现同样手势的方法!
通过向后移动时再次显示旧内容,PVC正在做得很好
在第一个位置向右移动后向左移动。记得
- 它只是以结构化的方式显示内容(您提供的内容) - 这是设计中的 'single page turn'
!
如果您更喜欢这个词,那就是翻页的概念 - 或者BOOK!
仅仅因为你在第1页之后提交PAGE 8并不是指PVC
关心你对一本书如何运作的扭曲观点。和你的用户
应用程序既不向右和向后翻转肯定会导致达到
原始页面 - 如果用动画完成。由你来纠正这个问题取决于你
找到灾难的解决方案。不要责怪 UIPageViewController
。它正在做
它的工作完美!
问问自己 - 你会用PAGE-CURL
动画做同样的事吗?不是吗?
那么,你不应该用SCROLL
动画!动画页面翻页是翻页而且只翻页!
无论哪种模式!
如果你决定撕掉你的书的第2页到第7页,这完全没问题!
但是,当你回到最近的页面时,不要期望 UIPageViewController
发明一个不存在的第7页,除非你告诉它事情已经发生了变化......
如果你真的想要在其他地方实现不协调的跳跃,那就好吧 - 没有一个 动画!在大多数情况下,这不会很优雅但是 - 它可能...... -
PVC甚至可以很好地发挥作用!跳转到没有动画的新SPACE
时
它会向你询问两个方面 - BEFORE
和AFTER
控制器。因此,您的应用程序逻辑可以跟上PVC ......
但是通过动画,你总是在传递 - 移动到上一个/下一个空间(BEFORE -
AFTER
)。因此从逻辑上讲,PVC根本不需要再次询问它已经存在的空间
知道动画页面何时转动!!!
如果你想在第1页动画后向左翻转时看到第7页 在右边 - 好吧,我会说 - 这绝对是你自己的问题!
如果您正在寻找一个比完成块更好的解决方案&#39;来自 接受的答案(因为有了它,你事先就可能会做的事情 可能甚至没有得到进一步使用)使用手势识别器代表:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
如果你真的打算回去,请在这里设置你的PVC的DataViewController(没有动画)
离开第7页,DataSource
将被要求BEFORE
和AFTER
,您可以提交
你喜欢什么页面!在你不受控制的情况下,你应该藏着旗帜或者伊娃
从第1页跳到第8页这应该没问题......
当人们继续抱怨PVC中的一个错误时 - 做了2页翻转 应该只做1转 - 将它们指向本文。
相同问题 - 在转换手势中触发未动画的 setViewControllers:方法
将造成完全相同的破坏。你认为你设置了新的中心 - 询问DataSource
对于新的BEFORE - AFTER
dataController - 你重置索引计数...... - 好吧,这似乎没问题......
但是 - 在所有这些业务之后,PVC结束了它的过渡/动画,并希望了解它
next(仍然不为它)dataViewController(BEFORE
或AFTER
)并触发DataSource
。这是完全合理的!它需要知道它的小BEFORE - CENTER - AFTER
的位置
它是世界,并为下一轮做好准备。
但是你的程序逻辑为它的逻辑添加了另一个索引++计数,突然变成了2页! 那是你认为自己所在的地方之一。
你必须考虑到这一点!不是 UIPageViewController
!!!
这正是DataSourceProtocol只有两种方法的要点!它希望尽可能地通用 - 让你有空间和自由来定义自己的逻辑,而不是被其他人的特殊想法和用例所困扰!逻辑完全取决于你。只是因为你找到了像
这样的功能- (DataViewController *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard position:(GSPositionOfDataViewController)position;
- (NSUInteger)indexOfViewController:(DataViewController *)viewController;
云中的所有复制/粘贴样本应用程序并不一定意味着你必须吃那些预煮食物!以你喜欢的方式扩展它们!请看上面 - 在我的签名中,您会找到 'position:'
参数!我将此扩展为稍后知道完成的页面转弯是右转还是左转弯。因为代表不幸只是告诉你轮到你完成了!它没有告诉你方向!但这有时对索引计数很重要,具体取决于您的应用需求......
发疯了 - 他们是你的......
快乐的编码!!!