2015-04-14 2 views
17

Quando mio figlio esegue una sequenza di svolgimento, viene richiamato viewDidAppear del mio controller.Nel mio viewDidAppear, come faccio a sapere quando viene svolto da un bambino?

In questo metodo (e questo metodo da solo, ho bisogno di sapere se era da uno svolgimento o meno)

Nota: il bambino viene svolgimento al primo controller della vista, quindi questo è un intermedio guarda il controller, non la vera radice.

+0

che arriviamo a. – TIMEX

+0

Ottiene 'viewWillAppear:' e 'viewDidAppear:'. – matt

+0

Ma come faccio a sapere che è stato da una pausa invece di qualcos'altro? – TIMEX

risposta

6

Si dovrebbe essere in grado di utilizzare quanto segue per rilevare in ciascun controller se l'esposizione del controller di visualizzazione è stata il risultato di essere spinto/presentato, o come risultato di essere esposti a seguito di pop/licenziamento/svolgimento .

Questo può o potrebbe essere sufficiente per le vostre esigenze.

- (void) viewDidAppear:(BOOL)animated{ 
    [super viewDidAppear:animated]; 

    // Handle controller being exposed from push/present or pop/dismiss 
    if (self.isMovingToParentViewController || self.isBeingPresented){ 
     // Controller is being pushed on or presented. 
    } 
    else{ 
     // Controller is being shown as result of pop/dismiss/unwind. 
    } 
} 

Se volete sapere che viewDidAppear fu chiamato a causa di un segue rilassarsi come diverso da un pop convenzionale/respingere essere chiamato, allora avete bisogno di aggiungere del codice per rilevare che un rilassarsi accaduto. Per fare questo si può fare come segue:

Per qualsiasi controller intermedia si desidera rilevare puramente un distendersi in, aggiungere una proprietà della forma:

/** BOOL property which when TRUE indicates an unwind occured. */ 
@property BOOL unwindSeguePerformed; 

Quindi l'override del metodo di svolgimento segue metodo canPerformUnwindSegueAction:fromViewController:withSender: come segue :

- (BOOL)canPerformUnwindSegueAction:(SEL)action 
       fromViewController:(UIViewController *)fromViewController 
         withSender:(id)sender{ 
    // Set the flag indicating an unwind segue was requested and then return 
    // that we are not interested in performing the unwind action. 
    self.unwindSeguePerformed = TRUE; 

    // We are not interested in performing it, so return NO. The system will 
    // then continue to look backwards through the view controllers for the 
    // controller that will handle it. 
    return NO; 
} 

Ora avete una bandiera di rilevare uno svolgimento e un mezzo per rilevare la svolgimento poco prima che accada. Quindi regola il metodo viewDidAppear per includere questo flag.

- (void) viewDidAppear:(BOOL)animated{ 
    [super viewDidAppear:animated]; 

    // Handle controller being exposed from push/present or pop/dismiss 
    // or an unwind 
    if (self.isMovingToParentViewController || self.isBeingPresented){ 
     // Controller is being pushed on or presented. 
     // Initialize the unwind segue tracking flag. 
     self.unwindSeguePerformed = FALSE; 
    } 
    else if (self.unwindSeguePerformed){ 
     // Controller is being shown as a result of an unwind segue 
    } 
    else{ 
     // Controller is being shown as result of pop/dismiss. 
    } 
} 

Speriamo che questo soddisfi le vostre esigenze.

Per documenti sulla gestione della catena di svolgimento segue vedi: https://developer.apple.com/library/ios/technotes/tn2298/_index.html

+0

Risposta aggiornata con la soluzione completa per includere il rilevamento dello svolgimento rispetto al pop/espulsione. –

+1

Questa risposta non è corretta. 'self.isMovingToParentViewController || self.isBeingPresented' è vero quando viene visualizzato 'viewDidAppear:' mentre scatta il controller della vista. (sì, sono sicuro che è il controller di visualizzazione popping, non quello dietro) –

+0

In effetti, la risposta è sbagliata.Sembra che questo sia cambiato da quando è stato scritto nel 2015? – Nestor

1

La tua domanda è stato davvero interessante per me, perché non ho mai usato IB e sfocia prima (non giudicarmi per quello) e volevo imparare qualcosa di nuovo . Come lei ha descritto nei vostri commenti:

viewDidAppear sarà invitato B quando C riavvolge A

Così io vengo con una soluzione personalizzata facile a questo:

protocol ViewControllerSingletonDelegate: class { 

    func viewControllerWillUnwind(viewcontroller: UIViewController, toViewController: UIViewController) 
} 

class ViewControllerSingleton { 

    static let sharedInstance = ViewControllerSingleton() 

    private var delegates: [ViewControllerSingletonDelegate] = [] 

    func addDelegate(delegate: ViewControllerSingletonDelegate) { 

     if !self.containsDelegate(delegate) { 

      self.delegates.append(delegate) 
     } 
    } 

    func removeDelegate(delegate: ViewControllerSingletonDelegate) { 

     /* implement any other function by your self :) */ 
    } 

    func containsDelegate(delegate: ViewControllerSingletonDelegate) -> Bool { 

     for aDelegate in self.delegates { 

      if aDelegate === delegate { return true } 
     } 

     return false 
    } 

    func forwardToDelegate(closure: (delegate: ViewControllerSingletonDelegate) -> Void) { 

     for aDelegate in self.delegates { closure(delegate: aDelegate) } 
    } 
} 


class SomeViewController: UIViewController, ViewControllerSingletonDelegate { 

    let viewControllerSingleton = ViewControllerSingleton.sharedInstance 

    func someFunction() { // some function where you'll set the delegate 

     self.viewControllerSingleton.addDelegate(self) 
    } 

    /* I assume you have something like this in your code */ 
    @IBAction func unwindToSomeOtherController(unwindSegue: UIStoryboardSegue) { 

     self.viewControllerSingleton.forwardToDelegate { (delegate) -> Void in 

      delegate.viewControllerWillUnwind(unwindSegue.sourceViewController, toViewController: unwindSegue.destinationViewController) 
     } 

     /* do something here */ 
    } 

    // MARK: - ViewControllerSingletonDelegate 
    func viewControllerWillUnwind(viewcontroller: UIViewController, toViewController: UIViewController) { 

     /* do something with the callback */ 
     /* set some flag for example inside your view controller so your viewDidAppear will know what to do */ 
    } 
} 

È anche possibile modificare la funzione di callback per restituire qualcos'altro, come l'identificatore del controller invece del controller stesso.

Faccio tutto in modo programmatico, quindi per favore non giudicarmi anche per questo. ;)

Se questo frammento di codice non ti aiuterà, mi piacerebbe comunque vedere qualche feedback.

1

Supponiamo che la navigazione di seguito sia ViewController -> FirstViewController -> SecondViewController.C'è uno svolgimento da SecondViewController a ViewController. È possibile aggiungere all'intermediario FirstViewController il seguente codice per rilevare le azioni di svolgimento.

import UIKit 

class FirstViewController: UIViewController { 

    var unwindAction:Bool = false 
    override func viewDidAppear(animated: Bool) { 
     if unwindAction { 
      println("Unwind action") 
      unwindAction = false 
     } 
    } 
    override func viewControllerForUnwindSegueAction(action: Selector, fromViewController: UIViewController, withSender sender: AnyObject?) -> UIViewController? { 
     self.unwindAction = true 

     return super.viewControllerForUnwindSegueAction(action, fromViewController: fromViewController, withSender: sender) 
    } 

} 

EDIT Dopo aver dato questo qualche pensiero, ho deciso la soluzione a questo dipende dal tipo di complessità che si sta trattando qui. Che cosa fai esattamente quando segui i successivi? Le soluzioni fornite qui sono valide e funzionano - solo se si desidera rilevare se si tratta di un'azione di svolgimento. Cosa succede se si desidera passare i dati tra il punto in cui lo svolgimento si svolge alla radice? E se ci fosse un insieme complesso di preparativi che vuoi fare in uno dei controller di visualizzazione intermedi? E se volessi fare entrambe queste cose?

In tali scenari complessi, escluderei immediatamente l'override dei metodi di svolgimento del controller di visualizzazione. Facendo queste operazioni, funzionerà, ma non sarà pulito. Un metodo farà ciò che non dovrebbe fare. L'hai sentito? Questo è odore di codice.

Cosa succede se, in qualche modo, un controller di visualizzazione può informare il controller di visualizzazione successivo nella gerarchia dell'evento che si verifica? Meglio ancora, come lo facciamo senza accoppiare strettamente questi due?

Protocollo.

avere una definizione qualcosa di protocollo come:

protocol UnwindResponding { 
    prepareForUnwindSegue(segue:UISegue , formViewController:UIViewController, withImportantInfo info:[String,AnyObject]) 
} 

Utilizzando il protocollo si può mantenere la relazione tra gli oggetti - la gerarchia dei controller di vista, in questo caso - esplicita. Al momento dell'occorrenza di un particolare evento, delegherete la chiamata al successivo controller nella gerarchia che informa sull'evento di un particolare evento in un altro controller di vista. Ecco un esempio:

override func prepareForSegue(segue:UIStoryboardSegue, sender:AnyObject?) { 

    if let unwindResponder = self.presentingViewController as? UnwindResponding where segue.identifier = "unwindSegue" { 
     unwindResponder.prepareForUnwindSegue(segue:UISegue, fromViewController:self,info:info) 
    } 


} 

Nel controller della vista intermediario si può fare qualcosa di simile:

extension IntermediaryViewController : UnwindResponding { 
    prepareForUnwindSegue(segue:UISegue , fromViewController:UIViewController, withImportantInfo info:[String,AnyObject]) { 
     if let unwindResponder = self.presentingViewController { 
      unwindResponder.prepareForUnwindSegue(segue,fromViewController:fromViewController, info:info) 
     } 
     unwindSegue = true 
    } 
} 

Certo, non si vuole fare questo se si vuole solo rilevare segues di svolgimento. Forse lo fai, non saprai mai cosa succederà in futuro. Non fa mai male a mantenere pulito il tuo codice. Metodo

+0

Non credo che 'viewControllerForUnwindSegueAction' sarà chiamato in FirstViewController nel caso di Push-> Push. La documetazione afferma che questo metodo viene chiamato sul controller che avvia lo svolgimento e per un controller spinto, questo restituirà il genitore che è il controller di navigazione. Quindi 'canPerformUnwindSegueAction' viene chiamato sui suoi figli. L'unico meccanismo affidabile è utilizzare 'canPerformUnwindSegueAction' per rilevare lo svolgimento in tutti i controller. Vedi la mia risposta. –

+0

Va bene. Sì. Ma perché dovresti seguire un percorso nei sistemi di controller di navigazione? Sembra un eccesso per me. – avismara

+0

Il pezzo importante è che se si apre un controller, è più difficile passare le informazioni e si deve fare tutto il codice. I seguiti di Svolgimento ti danno un meccanismo per passare le informazioni al punto in cui fai un salto indietro prima che il salto avvenga e tutto viene fatto da IB. Ci sono molti esempi in cui potresti voler selezionare un elemento che richiede più di una spinta del controller per raggiungere: quando selezionato, si ritorna al controller che ha iniziato la selezione. L'altro vantaggio è che funziona anche su sequenze push e modali senza dover scrivere codice. –

1

Aggiunta ai genitori View Controller

@IBAction func unwindToParent(unwindSegue: UIStoryboardSegue) { 
    if let childViewController = unwindSegue.sourceViewController as? ChildViewController { 
     println("unwinding from child") 
    } 
} 

Come Un esempio se la segue rilassarsi è legato a un pulsante, nella storyboard collegare il tuo pulsante alla sua uscita controller di vista

enter image description here

Proporrà di collegarsi al metodo unwindToParent

enter image description here

Poi ogni volta che viene eseguita la segue di svolgimento, il metodo unwindToParent saranno chiamati

2

Ecco un semplice categoria sul UIViewController che è possibile utilizzare per tenere traccia se il controller di vista presentato è nel bel mezzo di una segue rilassarsi. Suppongo che potrebbe essere svuotato di più, ma credo che funzioni molto per il tuo caso.

Per utilizzarlo è necessario registrare il segue rilassarsi dal tuo metodo di azione rilassarsi sul controller vista di destinazione:

- (IBAction) prepareForUnwind:(UIStoryboardSegue *)segue 
{ 
    [self ts_registerUnwindSegue: segue]; 
} 

Questo è tutto. Dal vostro controller della vista intermedio, è possibile verificare se si è nel bel mezzo di una segue si svolge:

- (void) viewDidAppear:(BOOL)animated 
{ 
    [super viewDidAppear: animated]; 

    BOOL unwinding = [self ts_isUnwinding]; 

    NSLog(@"%@:%@, unwinding: %@", self.title, NSStringFromSelector(_cmd), unwinding ? @"YES" : @"NO"); 
} 

Non c'è bisogno di pulire nulla fino; il seguito si auto-cancella quando finisce.

Ecco la categoria completa:

@interface UIViewController (unwinding) 

- (void) ts_registerUnwindSegue: (UIStoryboardSegue*) segue; 
- (BOOL) ts_isUnwinding; 

@end 


static NSMapTable* g_viewControllerSegues; 

@implementation UIViewController (unwinding) 

- (void) ts_registerUnwindSegue: (UIStoryboardSegue*) segue 
{ 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 

     g_viewControllerSegues = [NSMapTable weakToWeakObjectsMapTable]; 
    }); 

    for (UIViewController* vc = segue.sourceViewController ; vc != nil ; vc = vc.presentingViewController) 
    { 
     [g_viewControllerSegues setObject: segue forKey: vc]; 
    } 
} 

- (BOOL) ts_isUnwinding 
{ 
    return [g_viewControllerSegues objectForKey: [self ts_topMostParentViewController]] != nil; 
} 

- (UIViewController *)ts_topMostParentViewController { 
    UIViewController *viewController = self; 
    while (viewController.parentViewController) { 
     viewController = viewController.parentViewController; 
    } 
    return viewController; 
} 

@end 
+1

vc.presentingViewController sarà nullo eccetto per la presentazione modale o quando un antenato è stato presentato come modale. per esempio, ciò non funzionerà per NavRoot-> A-> PushB-> PushC con unwind da C a A, rileva in B poiché non c'è 'presentationViewController'. 'ts_registerUnwindSegue' avrebbe bisogno di utilizzare lo stesso algoritmo utilizzato dal codice di ricerca di unwind per localizzare il controller di destinazione che includerebbe la ricerca dei bambini del controller di navigazione dei controller di origine, i genitori di navigazione, le modali e le combinazioni di ciascuno. –

+0

@RoryMcKinnel - IMO questa domanda riguarda i controller (modali) presentati, non i controller inseriti in uno stack UINavigationController. Perché? Perché il riavvolgimento da uno stack nav non richiama i metodi viewDidAppear del controller intermedio. – TomSwift

+0

Guardando di nuovo, la tua ipotesi sembra corretta. –

0

per chiunque altro alla ricerca di questo, in rapida è possibile utilizzare la funzione di override "rilassarsi", questo è chiamato quando il ViewController è sulla strada di una segue rilassarsi , pensato per riconfigurare ViewController.

esempio:

override func unwind(for unwindSegue: UIStoryboardSegue, towardsViewController subsequentVC: UIViewController) { 
}