【问题标题】:How can I reorder my UINavigationController items programatically?如何以编程方式重新排序我的 UINavigationController 项目?
【发布时间】:2016-02-15 02:54:29
【问题描述】:

在我的 TabBarViewController 中,我正在创建一个导航并以模态方式呈现它。

func viewDidLoad(){ 
    super.viewDidLoad();
    //Create a present this view controller in viewDidLoad
    self.navController =  UINavigationController() 
    self.presentViewController(self.navController, animated: true, completion: nil)
}

//This method gets called when TabBarVC gets a NSNotification
func showMessageForUser(user_id: Int){
    let mvc = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
    mvc.user_id = user_id //set this user

    //display this to the user.
    self.navController.pushViewController(mvc, animated: true)
}

这很简单。但是,我想这样做:

  • 如果 user_id 已经在堆栈中,请将其移动到堆栈的末尾,以便用户可以看到它。没有重复。我该怎么做?
  • 否则,只需创建视图控制器的新实例并将其添加到堆栈顶部。我想我已经在上面做了。

重新排序后,所有“后退”按钮都应按预期工作。

【问题讨论】:

    标签: ios objective-c swift uinavigationcontroller


    【解决方案1】:

    您可以使用setViewControllers(_:animated:) 重新排列视图控制器堆栈。您无需执行任何特殊操作即可使后退按钮正常工作。导航控制器根据其viewControllers 数组中的第二项(如果有第二项)设置后退按钮,并在更新viewControllers 数组时更新后退按钮。

    我会这样做。首先,我们向UIViewController 添加一个方法来询问它是否是特定userId 的视图控制器。由于大多数视图控制器不是(也不能是)正确的视图控制器,它只返回false

    extension UIViewController {
        func isViewControllerForUserId(userId: Int) -> Bool {
            return false
        }
    }
    

    然后我们重写MessagesViewController 中的这个方法,在适当的时候返回true

    extension MessagesViewController {
        override func isViewControllerForUserId(userId: Int) -> Bool {
            return self.userId == userId
        }
    }
    

    现在,为了显示特定用户的视图控制器,我们在导航控制器的堆栈中搜索现有视图控制器。我们采取的行动取决于我们是否找到它:

    func showMessageForUserId(userId: Int) {
        if let index = navController.viewControllers.indexOf({ $0.isViewControllerForUserId(userId) }) {
            navController.moveToTopOfNavigationStack(viewControllerAtIndex: index)
        } else {
            pushNewViewControllerForUserId(userId)
        }
    }
    

    如果我们没有找到,我们创建一个新的视图控制器并推送它:

        private func pushNewViewControllerForUserId(userId: Int) {
            let vc = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
            vc.userId = userId
            self.navController.pushViewController(vc, animated: true)
        }
    

    如果我们确实找到了,我们用这个方法把它移到导航栈的顶部:

    extension UINavigationController {
    
        func moveToTopOfNavigationStack(viewControllerAtIndex index: Int) {
            var stack = viewControllers
            if index == stack.count - 1 {
                // nothing to do because it's already on top
                return
            }
            let vc = stack.removeAtIndex(index)
            if (reorderingIsBuggy) {
                setViewControllers(stack, animated: false)
            }
            stack.append(vc)
            setViewControllers(stack, animated: true)
        }
    
        private var reorderingIsBuggy: Bool {
            // As of iOS 9.3 beta 3, `UINavigationController` drops the prior top-of-stack
            // when you use `setViewControllers(_:animated:)` to move a lower item to the
            // top with animation. The workaround is to remove the lower item from the stack
            // without animation, then add it to the top of the stack with animation. This
            // makes it display a push animation instead of a pop animation and avoids
            // dropping the prior top-of-stack.
            return true
        }
    
    }
    

    【讨论】:

    • 谢谢罗伯。我在索引处将其删除并附加后打印了堆栈。它以正确的顺序打印,所以看起来不错。当我打电话给setViewControllers 时,一切似乎都很好,所需的 VC 会显示在顶部。然而,在它获得deinit 之前的 VC 并消失了。你知道以前的VC为什么消失了吗? (我只测试了 2 个 VC)
    • 注意:我将这一行:navController.setViewControllers(stack, animated: false) 更改为动画:false,一切似乎都正常。知道为什么进行此更改可以解决问题吗?
    • 哪个视图控制器运行deinit
    • 在 2 个 VC 上进行测试时,不是顶部的那个会被 deinit。
    • 我已更新我的答案以解决 UINavigationController 错误。
    【解决方案2】:

    这里是 showMessageForUser(_:) 方法,用这个替换你的,就像一个魅力..

    func showMessageForUser(user_id: Int){
        var mvcItem: MessagesViewController?
        var controllers: [UIViewController] = (self.navController?.viewControllers)!
    
        var matchindex: Int? = -1
        for (index, viewItem) in controllers.enumerate() {
            if (viewItem is MessagesViewController) {
    
                if viewItem.user_id == user_id {
                    matchindex = index
                    break
                }
            }
        }
    
        if matchindex != -1 {       //jus making sure a matching index was found
            mvcItem = (controllers.removeAtIndex(matchindex!)) as? MessagesViewController
        }
    
        if mvcItem != nil {
            self.navController?.viewControllers = controllers
            self.navController?.pushViewController(mvcItem!, animated: true)
        } else {
            let mvc = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
            mvc.user_id = user_id //set this user
    
            //display this to the user.
            self.navController.pushViewController(mvc, animated: true)
        }
    }
    

    【讨论】:

      【解决方案3】:

      只是为了使代码更易于推理,我将创建您自己的 UINavigationController。

      类似这样的:

      final class MessagesNavigationController: UINavigationController {
      
          // Option 1: If you want to dismiss everything that was in the midle, you can just do this:
          func showMessageForUser(user_id: Int){
              guard let messagesViewController = findMessagesViewController(withUserId: user_id) else {
                  let mvc = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
                  mvc.user_id = user_id
                  pushViewController(mvc, animated: true)
                  return
              }
      
              popToViewController(messagesViewController, animated: false)
      
          }
      
          private func findMessagesViewController(withUserId userId: Int) -> MessagesViewController? {
              for viewController in viewControllers {
                  guard let messagesViewController = viewController as? MessagesViewController
                      where messagesViewController.user_id == userId else { continue }
                  return messagesViewController
      
              }
              return nil
          }
      
          // Option 2: If you want to move the viewcontroller from the position it currently is, to the end
          // but keeping everything in the middle, you can do this:
          func showMessageForUser2(user_id: Int){
              guard let index = findMessagesViewControllerIndex(withUserId: user_id) else {
                  let mvc = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
                  mvc.user_id = user_id
                  pushViewController(mvc, animated: true)
                  return
              }
      
              // This is the main change
              let viewController = viewControllers.removeAtIndex(index)
              pushViewController(viewController, animated: false)
          }
      
          private func findMessagesViewControllerIndex(withUserId userId: Int) -> Int? {
              for (index, viewController) in viewControllers.enumerate() {
                  guard let messagesViewController = viewController as? MessagesViewController
                      where messagesViewController.user_id == userId else { continue }
                  return index
      
              }
              return nil
          }
      }
      

      所以你的 TabBarController 看起来像这样:

      var navController: MessagesNavigationController!
      
      override func viewDidLoad(){
          super.viewDidLoad();
      
          navController = MessagesNavigationController()
          presentViewController(self.navController, animated: true, completion: nil)
      }
      
      func showMessageForUser(user_id: Int){
          navController.showMessageForUser(user_id)
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-04-11
        • 2014-02-16
        • 2012-08-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-14
        相关资源
        最近更新 更多