2016-02-15 62 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) 
} 
相關問題