2016-02-15 9 views
5

Nel mio TabBarViewController, sto creando una navigazione e presentandola in modo modale.Come posso riordinare i miei articoli UINavigationController in modo programmatico?

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

Questo è piuttosto semplice. Tuttavia, vorrei fare questo:

  • Se user_id è già nello stack, spostarlo nella fine dello stack in modo che sia visibile all'utente. Nessun duplicato Come posso fare questo?
  • Altrimenti, è sufficiente creare una nuova istanza del controller della vista e aggiungerla all'inizio della pila. Penso che lo sto già facendo sopra.

Tutti i pulsanti "indietro" dovrebbero funzionare come previsto dopo il riordino.

risposta

4

È possibile utilizzare setViewControllers(_:animated:) per riorganizzare lo stack View Controller. Non devi fare nulla di speciale per far funzionare correttamente il pulsante Indietro. Il controller di navigazione imposta il pulsante Indietro in base al secondo elemento nell'array viewControllers (se esiste un secondo elemento) e aggiorna il pulsante Indietro ogni volta che aggiorna l'array viewControllers.

Ecco come farei questo. Innanzitutto, aggiungiamo un metodo a UIViewController per chiedere se è il controller di visualizzazione per uno specifico userId. Poiché la maggior parte controller di vista non sono (e non possono essere) il controller della vista corretta, semplicemente ritorna false:

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

Poi abbiamo ignorare questo metodo in MessagesViewController per tornare true se del caso:

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

Ora, per mostrare il controller di visualizzazione per un utente specifico, cerchiamo lo stack del controller di navigazione per un controller di visualizzazione esistente. La nostra azione dipende dal fatto che lo troviamo:

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

Se non l'abbiamo trovato, facciamo un nuovo controller della vista e spingerlo:

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

Se l'abbiamo trovato, abbiamo spostarlo in cima alla pila di navigazione con questo metodo:

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

Grazie Rob. Ho stampato la pila dopo averla rimossa all'indice e aggiunta. È stampato nell'ordine corretto, quindi sembra a posto.Quando chiamo '' 'setViewControllers''', tutto sembra a posto e il VC desiderato viene mostrato in alto. Tuttavia, il VC prima di ottenere '' 'deinit''' e scompare. Sai perché il precedente VC scompare? (Sto solo testando su 2 VC) – TIMEX

+0

Nota: Ho modificato questa riga: '' 'navController.setViewControllers (stack, animato: false)' '' per l'animazione: false, e tutto sembra funzionare correttamente. Qualche idea sul perché questo cambiamento ha risolto il problema? – TIMEX

+0

Quale controller di visualizzazione esegue 'deinit'? –

0

Ecco il metodo showMessageForUser (_ :), sostituire la vostra con questo, funziona come un fascino ..

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

solo per rendere il codice più facile ragionare su mi piacerebbe creare il proprio UINavigationController.

Qualcosa di simile a questo:

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

Così il vostro TabBarController sarebbe simile a questa:

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