2016-02-15 63 views
5

在我的TabBarViewController中,我创建了一个导航并以模态方式呈现它。如何以编程方式重新排序我的UINavigationController项目?

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是已经在堆栈中,将其移动到堆栈的末尾,以便它是对用户可见。没有重复。我怎样才能做到这一点?
  • 否则,只需创建视图控制器的新实例并将其添加到堆栈的顶部。我想我已经在上面这样做了。

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

回答

4

您可以使用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 
    } 

} 
+0

感谢罗布。我在索引处删除它并将其附加后打印出堆栈。它以正确的顺序打印,所以看起来很好。当我调用'''setViewControllers'''时,一切似乎都很好,并且希望VC显示在顶部。然而,VC在获得''deinit'''之前就消失了。你知道为什么以前的VC消失吗? (我只在2个VC上进行测试) – TIMEX

+0

注意:我改变了这一行:'''navController.setViewControllers(stack,animated:false)'''为animated:false,并且全部似乎都正常工作。任何想法为什么做这个改变解决了这个问题? – TIMEX

+0

哪个视图控制器运行'deinit'? –

0

这里的方法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) 
    } 
} 
0

只是为了让代码更容易推理我想创建自己的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) 
} 
相关问题