在页面视图控制器中使用导航控制器时重复视图控制器

Posted

技术标签:

【中文标题】在页面视图控制器中使用导航控制器时重复视图控制器【英文标题】:Duplicate view controllers when using nav controllers inside page view controller 【发布时间】:2015-07-07 03:23:23 【问题描述】:

感谢Using UIPageViewController with swift and multiple view controllers,我在UIPageViewController 中嵌入了导航控制器,因此我可以水平滚动浏览它们(如滑动导航)。问题:

当我到达第一个或最后一个导航控制器,然后向相反方向滑动时,第二个到第一个/最后一个导航控制器将复制。所以我将有 5 个导航控制器。例如:

从 FirstNav 开始,一直向左滑动到 FourthNav。当我从 FourthNav 向右滑动控制器阵列时,顺序将是:ThirdNav、ThirdNav、SecondNav、FirstNav。有谁能知道发生了什么?

 class PageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate 

        var index = 0
        var identifiers: NSArray = ["FirstNav", "SecondNav", "ThirdNav", "FourthNav"]

        override func viewDidLoad() 
            super.viewDidLoad()

            self.dataSource = self
            self.delegate = self

            let startingViewController = self.viewControllerAtIndex(self.index)
            let viewControllers: NSArray = [startingViewController]
            self.setViewControllers(viewControllers as [AnyObject], direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)

        

        func viewControllerAtIndex(index: Int) -> UINavigationController! 

            if index == 0 

                return self.storyboard!.instantiateViewControllerWithIdentifier("FirstNav") as! UINavigationController
            


            if index == 1 

               return self.storyboard!.instantiateViewControllerWithIdentifier("SecondNav") as! UINavigationController

            

           if index == 2 

               return self.storyboard!.instantiateViewControllerWithIdentifier("ThirdNav") as! UINavigationController
           

           if index == 3 

               return self.storyboard!.instantiateViewControllerWithIdentifier("FourthNav") as! UINavigationController
           

           return nil
    
    func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? 

        let identifier = viewController.restorationIdentifier
        let index = self.identifiers.indexOfObject(identifier!)

        //if the index is the end of the array, return nil since we dont want a view controller after the last one
        if index == identifiers.count - 1 

            return nil
        

        //increment the index to get the viewController after the current index
        self.index = self.index + 1
        return self.viewControllerAtIndex(self.index)

    

    func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? 

        let identifier = viewController.restorationIdentifier
        let index = self.identifiers.indexOfObject(identifier!)

        //if the index is 0, return nil since we dont want a view controller before the first one
        if index == 0 

            return nil
        

        //decrement the index to get the viewController before the current one
        self.index = self.index - 1
        return self.viewControllerAtIndex(self.index)

    


    func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int 
        return 0
    

    func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int 
        return 0
    



【问题讨论】:

可以分享你的项目吗? @ndmeiri 我在一个新项目中重新测试了代码,使用完全相同的代码。如果你想做同样的事情,创建一个页面视图控制器和 4 个视图控制器。将每个 VC 嵌入到它们自己的导航控制器中,并确保为每个导航控制器提供故事板 ID。然后将页面视图控制器链接到它的正确类名。 好的,我现在会尝试重现该问题。您使用的是什么版本的 Xcode,以及您正在构建的 ios 版本是什么? 分别是 6.4 和 8.4 :) 需要注意的是,如果页面视图控制器设置为页面卷曲过渡样式,则没有重复项。只是滚动样式的问题。 【参考方案1】:

问题与您在pageViewController(_:viewControllerBeforeViewController:)pageViewController(_:viewControllerAfterViewController:) 方法中计算index 变量有关。为了简化页面视图控制器的逻辑和整体逻辑,您应该将实现更改为以下内容:

class PageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate 

    private var pages: [UIViewController]!

    override func viewDidLoad() 
        super.viewDidLoad()

        self.dataSource = self
        self.delegate = self

        self.pages = [
            self.storyboard!.instantiateViewControllerWithIdentifier("FirstNav") as! UINavigationController,
            self.storyboard!.instantiateViewControllerWithIdentifier("SecondNav") as! UINavigationController,
            self.storyboard!.instantiateViewControllerWithIdentifier("ThirdNav") as! UINavigationController,
            self.storyboard!.instantiateViewControllerWithIdentifier("FourthNav") as! UINavigationController
        ]

        let startingViewController = self.pages.first! as UIViewController
        self.setViewControllers([startingViewController], direction: .Forward, animated: false, completion: nil)
    

    func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? 
        let index = (self.pages as NSArray).indexOfObject(viewController)

        // if currently displaying last view controller, return nil to indicate that there is no next view controller
        return (index == self.pages.count - 1 ? nil : self.pages[index + 1])
    

    func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? 
        let index = (self.pages as NSArray).indexOfObject(viewController)

        // if currently displaying first view controller, return nil to indicate that there is no previous view controller
        return (index == 0 ? nil : self.pages[index - 1])
    


你甚至不需要维护一个index 实例变量,而且这个实现也不需要这样做。但如果你愿意,你可以。此解决方案还实例化每个 UINavigationController 的单个实例,而不是每次用户尝试滚动到不同页面时都实例化一个新实例,这样可以节省内存并在用户在它们之间滚动时保留视图控制器的状态。

请原谅非描述性的pages 变量名。我不想与UIPageViewControllerviewControllers 属性发生冲突。

【讨论】:

你,先生......你很好。此外,在我以前的方法中,内存保存是一个大问题。每次出现时页面都在重新加载。这非常有效。 如何以编程方式更改当前页面的索引?假设我有一个条形按钮图标,它转换到 self.pages 数组中的不同导航控制器。我可以这样做吗? 是的。在self 上调用UIPageViewController 方法setViewControllers(_:direction:animated:completion:) 并传入单个视图控制器的数组:[self.pages[newIndex]]。传递.Forward.Reverse 作为方向,取决于newIndex 是大于还是小于当前索引(这需要维护一个currentIndex 变量)。您也可以将其分解为单独的方法。如果您需要更多说明,请告诉我。 我在视图控制器中设置了函数func showDiscover() PageViewController.setViewControllers([PageViewController.pages[1]], direction: .Forward, animated: false, completion: nil) ,但出现错误:PageViewController.type does not have a member named 'pages'。我是否执行不正确? 您需要将此方法放在您的PageViewController 类中,或者在每个“子”视图控制器中维护对“父”PageViewController 实例的引用。如果您选择第一个选项,请将PageViewController.pages[1] 替换为self.pages[1]。如果选择第二个选项,请将PageViewController.pages[1] 替换为self.parentPageViewController.pages[1]

以上是关于在页面视图控制器中使用导航控制器时重复视图控制器的主要内容,如果未能解决你的问题,请参考以下文章

导航控制器未显示在 TableView 中

Swift 仅在使用导航控制器首次启动时显示视图

滚动视图,如导航控制器的过渡[重复]

在目标视图控制器 viewWillAppear 中检测向后/弹出导航 [重复]

使用导航控制器、页面视图控制器和标签栏控制器

将单独的 xib 视图控制器加载到情节提要导航中[重复]