2016-02-15 7 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

..これであなたを置き換える、:) _メソッドshowMessageForUser(だ魔法のように動作します。戻るボタンを正しく動作させるために特別な操作を行う必要はありません。ナビゲーションコントローラは、viewControllersアレイ(2番目のアイテムがある場合)の2番目のアイテムに基づいて戻るボタンを設定し、viewControllersアレイを更新するたびに戻るボタンを更新します。

これはどうやってやるの?最初に、UIViewControllerにメソッドを追加して、それが特定のuserIdのビューコントローラであるかどうかを確認します。ほとんどのビューコントローラは正しいビューコントローラではありません(とすることはできません)ので、それだけでfalseを返します。

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

その後、我々は適切なときにtrueを返すためにMessagesViewControllerでこのメソッドをオーバーライド:今すぐ

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

、特定のユーザのビューコントローラを表示するために、ナビゲーションコントローラのスタックで既存のビューコントローラを検索します。我々は、

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

我々はそれを見つけた場合は:私たちは新しいビューコントローラを作成し、それを押して、それを見つけていない場合は

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

:私たちが取る行動は、我々はそれを見つけるかどうかに依存しますただ、私は自分自身のUINavigationControllerを作成したいについて推論するためのコードを容易にするために

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が "脱"して消える前のVC。前のVCが消えた理由を知っていますか? (私は2つのVCだけをテストしています) – TIMEX

+0

注:この行を変更しました: '' 'navController.setViewControllers(stack、animated:false)' ''をアニメーション化:falseにして、すべてうまくいくようです。この変更を行うことが問題を解決する理由は何ですか? – TIMEX

+0

どのビューコントローラが 'deinit'を実行していますか? –

0

は、ここでは、ビュー・コントローラ・スタックを再配置するsetViewControllers(_:animated:)を使用することができます

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

:この方法では、ナビゲーションスタックの最上位に移動します。このような

何か:

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) 
}