2015-09-15 23 views
6

Come posso modificare il controller di visualizzazione in cui mi porta il pulsante di navigazione predefinito? Il pulsante Indietro di solito riporta al controller della vista precedente. Ma cosa succede se voglio tornare indietro da due controller di vista? Voglio dire, voglio cambiare il controller della vista su cui il pulsante Indietro mi porta. Non preferisco creare un pulsante Indietro personalizzato. Quindi c'è un altro modo? Probabilmente una sequenza di svolgimento collegata al pulsante indietro o qualcosa del genere?Cambia destinazione di navigazione Pulsante Indietro

+0

Cosa intendi con "Ma cosa succede se voglio tornare indietro da due controller di visualizzazione"? –

+1

@CalebKleveter Intendo dire se voglio passare a un altro controller di visualizzazione e non a quello precedente? –

risposta

6

Probabilmente il modo più semplice per ottenere il comportamento desiderato è utilizzare navigationController.setViewControllers(controllers, animated: true).

Se volete andare 2 controller indietro dal regolatore A invece di una sola, utilizzare questo segue personalizzato in occasione della presentazione A:

class ReplaceControllerSegue: UIStoryboardSegue { 
    override func perform() { 
     if let navigationController = sourceViewController.navigationController as UINavigationController? { 
      var controllers = navigationController.viewControllers 
      controllers.removeLast() 
      controllers.append(destinationViewController) 
      navigationController.setViewControllers(controllers, animated: true) 
     } 
    } 
} 

Ecco bel tutorial su come impostare classe personalizzata per il vostro segue in Interface Builder : http://blog.jimjh.com/a-short-tutorial-on-custom-storyboard-segues.html

Se si sta presentando regolatore tramite codice, utilizzare questa categoria:

extension UINavigationController { 
    func replaceCurrentViewControllerWith(viewController: UIViewController, animated: Bool) { 
     var controllers = viewControllers 
     controllers.removeLast() 
     controllers.append(viewController) 
     setViewControllers(controllers, animated: animated) 
    } 
} 

poi basta usare self.navigationController!.replaceCurrentViewControllerWith(newController, animated: true) anziché self.navigationControllerPushViewController(newController, animated: true).

È facile regolare questo codice per rimuovere tutti i controller necessari.

+0

Devo creare una lezione Cocoa Touch UIStoryboardSegue separata per questo? E se sì, come dovrei usare questa lezione? Come devo assegnare la lezione ad alcuni passaggi o cosa? –

+1

Ho pubblicato il codice per la classe UIStoryboardSegue personalizzata sopra. Ecco il tutorial su come assegnare una classe personalizzata per i passaggi nel builder dell'interfaccia: http://blog.jimjh.com/a-short-tutorial-on-custom-storyboard-segues.html – glyuck

+0

OH GOD! Grazie mille! Ho cercato SO per questo, ma non l'ho capito! Molte grazie! –

0

È possibile eseguire questa operazione aggiungendo un pulsante indietro personalizzato come questo. Aggiungi il tuo gestore azioni personalizzato alla pressione del pulsante Indietro sulla barra di navigazione.

+ (void)addCustomBackButtonForController:(id <MyCustomBackButtonDelegate>)iViewController withNavBar:(UINavigationController *)iNavController andNavItem:(UINavigationItem *)iNavItem { 
    if ([self isIOS7orAbove]) { 
     UIImage *backArrow = [UIImage imageNamed:kMyIOS7BackButtonImage]; 
     UIButton *aBackButton = [UIButton buttonWithType:UIButtonTypeSystem]; 
     CGSize aBackButtonTextSize = [MyLocalizedBackButton sizeWithFont:[UIFont systemFontOfSize:17.0]]; 
     aBackButton.frame = CGRectMake(0.0, 0.0, aBackButtonTextSize.width + backArrow.size.width, iNavController.navigationBar.frame.size.height); 
     aBackButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; 
     SEL backSelector = NSSelectorFromString(@"backAction"); 
     [aBackButton addTarget:iViewController action:backSelector forControlEvents:UIControlEventTouchUpInside]; 
     [aBackButton setTitle:MyLocalizedBackButton forState:UIControlStateNormal]; 
     [aBackButton setImage:backArrow forState:UIControlStateNormal]; 
     [aBackButton setExclusiveTouch:YES]; 

     if ([self isIOS7orAbove]) { 
      aBackButton.titleLabel.font = [UIFont systemFontOfSize:17.0]; 
     } else { 
      aBackButton.titleLabel.font = [UIFont boldSystemFontOfSize:12.0]; 
     } 

     UIBarButtonItem *aLeftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:aBackButton]; 
     UIBarButtonItem *aNegativeSpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; 
     aNegativeSpacer.width = kMyIOS7BarButtonNegativeSpacerWidth; 
     iNavItem.leftBarButtonItems = @[aNegativeSpacer, aLeftBarButtonItem]; 
    } else { 
     UIButton *aCustomBackButton = [UIButton buttonWithType:101]; 
     SEL backSelector = NSSelectorFromString(@"backAction"); 
     [aCustomBackButton addTarget:iViewController action:backSelector forControlEvents:UIControlEventTouchUpInside]; 
     [aCustomBackButton setTitle:MyLocalizedBackButton forState:UIControlStateNormal]; 
     [aCustomBackButton setExclusiveTouch:YES]; 

     if ([self isIOS7orAbove]) { 
      aCustomBackButton.titleLabel.font = [UIFont systemFontOfSize:kFontSize17]; 
     } else { 
      aCustomBackButton.titleLabel.font = [UIFont boldSystemFontOfSize:kFontSize12]; 
     } 

     UIBarButtonItem *aLeftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:aCustomBackButton]; 
     iNavItem.leftBarButtonItem = aLeftBarButtonItem; 
    } 
} 
+0

Voglio implementarlo senza un pulsante Indietro personalizzato altrimenti non potrò scorrere indietro e l'animazione posteriore andrà persa. Molte app sono state in grado di implementare questo senza un pulsante back personalizzato. C'è un modo per farlo anche io? –

+0

Cosa intendi per non essere in grado di scorrere indietro e l'animazione posteriore andrà persa? – Abhinav

+0

Sapete che l'utente può scorrere dal bordo e passare lentamente al controller della vista precedente? –

6

È possibile eseguire l'override di willMoveToParentViewController. Questo metodo viene chiamato prima che il controller di visualizzazione venga aggiunto o rimosso da un controller di visualizzazione contenitore (come un UINavigationController). Quando viene aggiunto, il parametro parent contiene il controller della vista contenitore, quando viene rimosso, il parametro parent è nullo.

A questo punto è possibile rimuovere (senza animazione) alla penultima controller della vista nella pila di navigazione in questo modo:

Objective-C

- (void)willMoveToParentViewController:(UIViewController *)parent 
{ 
    [super willMoveToParentViewController:parent]; 

    if (parent == nil) { 
     NSArray *viewControllers = self.navigationController.viewControllers; 
     NSUInteger viewControllersCount = viewControllers.count; 
     if (viewControllersCount > 2) { 
      NSMutableArray *mutableViewControllers = [NSMutableArray arrayWithArray:viewControllers]; 
      [mutableViewControllers removeObjectAtIndex:(viewControllersCount - 2)]; 
      [self.navigationController setViewControllers:[NSArray arrayWithArray:mutableViewControllers] animated:NO]; 
     } 
    } 
} 

Swift

override func willMoveToParentViewController(parent:UIViewController?) 
{ 
    super.willMoveToParentViewController(parent) 

    if (parent == nil) { 
     if let navigationController = self.navigationController { 
      var viewControllers = navigationController.viewControllers 
      var viewControllersCount = viewControllers.count 
      if (viewControllersCount > 2) { 
       viewControllers.removeAtIndex(viewControllersCount - 2) 
       navigationController.setViewControllers(viewControllers, animated:false) 
      } 
     } 
    } 
} 

Si potrebbe anche rimuovere più di uno o aggiungerne di nuovi. Assicurati solo che quando hai finito la matrice contenga almeno 2 controller di vista con l'ultimo immutato (l'ultimo è quello rimosso e verrà rimosso automaticamente dall'array dopo che questo metodo è stato chiamato).

Si noti inoltre che questo metodo può essere chiamato più volte con un parametro nullo. Ad esempio, se provi a far scattare il controller della vista usando lo swipe del bordo ma interrompi nel mezzo, il metodo verrà chiamato ogni volta che proverai. Assicurati di aggiungere ulteriori controlli per assicurarti di non rimuovere più controller di visualizzazione di quanti ne vuoi.

+1

Era così difficile trovare questa risposta. Ma l'hai fatto! Grazie! –

+0

Sembra funzionare in primo luogo (non conosco ancora tutti i miei casi limite). Funziona meglio di ['viewWillDisappear'] (http://stackoverflow.com/a/3445994/426227). – testing

0

Questo può semplicemente ripetere ciò che è stato detto sopra, ma ho risolto questo modificando viewDidLoad nell'ultimo VewController. "theControllers" è un array di tipo UIViewController. Nel mio caso, volevo solo tornare al controller della vista radice, quindi l'array contiene solo il viewcontroller root (prima) e il viewcontroller corrente (ultimo).Quindi "setViewControllers" sostituisce lo stack corrente con quello che ho creato.

override func viewDidLoad() { 
    super.viewDidLoad() 
    var theControllers = [UIViewController]() 
    theControllers.append(self.navigationController!.viewControllers.first!) 
    theControllers.append(self.navigationController!.viewControllers.last!) 
    self.navigationController?.setViewControllers(theControllers, animated: false) 
}