从UIPageViewController中删除视图控制器 [英] Removing a view controller from UIPageViewController

查看:640
本文介绍了从UIPageViewController中删除视图控制器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

很奇怪,没有直接的方法来做到这一点。请考虑以下情况:


  1. 您有一个页面查看控制器,共有1页。


  2. 我想要的是,当用户滚动回到第一页时,第二页现在被删除,


  3. 我已尝试将视图控制器移除为子视图控制器在转换完成后,但它仍然让我回滚到空页面(它不会调整页面视图的大小)



    是我想做的可能?

    解决方案

    虽然这里的答案都是信息性的,但是还有另一种处理问题的方法, p>

    UIPageViewController使用Scroll转换样式导航到错误的页面



    当我第一次搜索这个问题的答案时,我在搜索我的搜索的方式伤害了我问题,而不是我刚刚链接的那个,所以我觉得有义务发布一个链接到这个其他问题的答案,现在我已经找到它,并且阐述了一点。



    问题描述得很清楚,这里


    这实际上是UIPageViewController中的一个错误。它只发生在滚动样式(UIPageViewControllerTransitionStyleScroll)和调用setViewControllers之后只有
    :direction:animated:completion:with
    animated:YES。因此有两种解决方法:



    不要使用UIPageViewControllerTransitionStyleScroll。



    或者,如果你调用setViewControllers:direction :animated:completion :, use
    only animated:NO。



    要查看错误,请调用
    setViewControllers:direction:animated:completion:然后在
    接口(作为用户)中,手动向左(后)导航到前一页
    。你将导航回到错误的页面:不是前面的
    页面,而是当你
    setViewControllers:direction:animated:completion:被调用时你所在的页面。



    错误的原因似乎是,当使用滚动
    样式时,UIPageViewController会执行某种内部缓存。因此,
    在调用setViewControllers:direction:animated:completion :,
    后无法清除其内部缓存。它认为它知道
    前面的页是什么。因此,当用户向左导航到
    前一页时,UIPageViewController无法调用dataSource
    方法pageViewController:viewControllerBeforeViewController :,或
    使用错误的当前视图控制器调用它。


    这是一个很好的描述,不是这个问题中注意到的问题,但非常接近。注意如果你用 setViewControllers animated:NO 做的话,你将强制UIPageViewController重新查询它的数据源下一次用户用手势摇动,因为它不再知道它在哪里或什么视图控制器在其当前视图控制器旁边。



    然而,这没有为我工作,因为有时,我需要通过

    动画来移动PageView。



    所以,我的第一个想法是用动画调用 setViewControllers ,然后在完成块中再次使用任何视图控制器显示,但没有动画调用该方法。



    不幸的是,当我尝试我开始遇到奇怪的断言错误时, 从页面视图控制器。它们看起来像这样:


    *** - - [UIPageViewController queuingScrollView:...


    不知道为什么会发生这种情况,我回溯并最终开始使用 Jai answer 作为解决方案,创建一个全新的UIPageViewController,将其推送到UINavigationController,然后弹出旧的UINavigationController。毛,但它的工作 - 大多。我一直在发现我仍然偶尔会从UIPageViewController的Assertion Failures,像这样:


    ***在 - UIPageViewController queuingScrollView:didEndManualScroll:toRevealView:direction:animated:didFinish:didComplete:],
    /SourceCache/UIKit_Sim/UIKit-2380.17/UIPageViewController.m:1820 $ 1 =
    154507824没有视图控制器管理可见视图> / p>

    应用程式崩溃。为什么?好吧,搜索,我发现这其他问题我提到了顶部,特别是主张我的最初想法的接受答案,只需调用 setViewControllers :animated:YES ,然后一旦它完成调用 setViewControllers:animated:NO ,使用相同的视图控制器重置UIPageViewController,缺少的元素:调用该代码回主队列!这里是代码:

      __ 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]; // uipageview控制器的错误修复
    });
    }
    }];

    哇!对我来说唯一的原因是因为我观看了WWDC 2012 Session 211,在iOS上构建并发用户界面(可以这里与开发人员帐户)。我记得现在,尝试修改UIKit对象(如UIPageViewController)依赖的数据源对象,并在辅助队列上执行它,可能会导致一些令人讨厌的崩溃。



    我从来没有见过特别记录,但现在必须假设是这种情况,并且读取,是动画的完成块是在次要队列上执行,而不是主要的。所以,为什么UIPageViewController在调用 setViewControllers的完成块中的原始尝试调用 setViewControllers animated:NO YES ,现在我只是使用UINavigationController推送一个新的UIPageViewController(但是,在 setViewController的完成块中再次执行它animated:YES )是因为它发生在第二个队列。



    这就是为什么那段代码在那里工作完美,因为你来自动画完成块和发送它回到主队列,所以你不使用UIKit交叉流。 Brilliant。



    无论如何,想分享这个旅程,万一有人遇到这个问题。



    Swift版本此处,如果有人感兴趣。


    It's odd that there's no straightforward way to do this. Consider the following scenario:

    1. You have a page view controller with 1 page.
    2. Add another page (total 2) and scroll to it.
    3. What I want is, when the user scrolls back to the first page, the 2nd page is now removed and deallocated, and the user can no longer swipe back to that page.

    I've tried removing the view controller as a child view controller after the transition is completed, but it still lets me scroll back to the empty page (it doesn't "resize" the page view)

    Is what I want to do possible?

    解决方案

    While the answers here are all informative, there is an alternate way of handling the problem, given here:

    UIPageViewController navigates to wrong page with Scroll transition style

    When I first searched for an answer to this problem, the way I was wording my search wound me up at this question, and not the one I've just linked to, so I felt obligated to post an answer linking to this other question, now that I've found it, and also elaborating a little bit.

    The problem is described pretty well by matt here:

    This is actually a bug in UIPageViewController. It occurs only with the scroll style (UIPageViewControllerTransitionStyleScroll) and only after calling setViewControllers:direction:animated:completion: with animated:YES. Thus there are two workarounds:

    Don't use UIPageViewControllerTransitionStyleScroll.

    Or, if you call setViewControllers:direction:animated:completion:, use only animated:NO.

    To see the bug clearly, call setViewControllers:direction:animated:completion: and then, in the interface (as user), navigate left (back) to the preceding page manually. You will navigate back to the wrong page: not the preceding page at all, but the page you were on when setViewControllers:direction:animated:completion: was called.

    The reason for the bug appears to be that, when using the scroll style, UIPageViewController does some sort of internal caching. Thus, after the call to setViewControllers:direction:animated:completion:, it fails to clear its internal cache. It thinks it knows what the preceding page is. Thus, when the user navigates leftward to the preceding page, UIPageViewController fails to call the dataSource method pageViewController:viewControllerBeforeViewController:, or calls it with the wrong current view controller.

    This is a good description, not quite the problem noted in this question but very close. Note the line about if you do setViewControllers with animated:NO you will force the UIPageViewController to re-query its data source next time the user pans with a gesture, as it no longer "knows where it is" or what view controllers are next to its current view controller.

    However, this didn't work for me because there were times when I need to programmatically move the PageView around with an animation.

    So, my first thought was to call setViewControllers with an animation, and then in the completion block call the method again with whatever view controller was now showing, but with no animation. So the user can pan, fine, but then we call the method again to get the page view to reset.

    Unfortunately when I tried that I started getting strange "assertion errors" from the page view controller. They look something like this:

    *** Assertion failure in -[UIPageViewController queuingScrollView: ...

    Not knowing exactly why this was happening, I backtracked and eventually started using Jai's answer as a solution, creating an entirely new UIPageViewController, pushing it onto a UINavigationController, then popping out the old one. Gross, but it works--mostly. I have been finding I'm still getting occasional Assertion Failures from the UIPageViewController, like this one:

    *** Assertion failure in -[UIPageViewController queuingScrollView:didEndManualScroll:toRevealView:direction:animated:didFinish:didComplete:], /SourceCache/UIKit_Sim/UIKit-2380.17/UIPageViewController.m:1820 $1 = 154507824 No view controller managing visible view >

    And the app crashes. Why? Well, searching, I found this other question that I mentioned up top, and particularly the accepted answer which advocates my original idea, of simply calling setViewControllers: animated:YES and then as soon as it completes calling setViewControllers: animated:NO with the same view controllers to reset the UIPageViewController, but it had the missing element: calling that code back on the main queue! Here's the code:

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

    Wow! The only reason this actually made sense to me is because I have watched the the WWDC 2012 Session 211, Building Concurrent User Interfaces on iOS (available here with a dev account). I recall now that attempting to modify data source objects that UIKit objects (like UIPageViewController) depend on, and doing it on a secondary queue, can cause some nasty crashes.

    What I have never seen particularly documented, but must now assume to be the case and read up on, is that the completion block for an animation is performed on a secondary queue, not the main one. So the reason why UIPageViewController was squawking and giving assertion failures, both when I originally attempted to call setViewControllers animated:NO in the completion block of setViewControllers animated:YES and also now that I am simply using a UINavigationController to push on a new UIPageViewController (but doing it, again, in the completion block of setViewControllers animated:YES) is because it's all happening on that secondary queue.

    That's why that piece of code up there works perfectly, because you come from the animation completion block and send it back over to the main queue so you don't cross the streams with UIKit. Brilliant.

    Anyway, wanted to share this journey, in case anyone runs across this problem.

    EDIT: Swift version here, if anyone's interested.

    这篇关于从UIPageViewController中删除视图控制器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆