2014-05-06 5 views
18

Se si trascina intorno ad un UIViewController per iniziare una transizione interattivo comparsa all'interno di un UINavigationController, il UIViewController sotto quella attuale ha viewWillAppear: chiamato, seguito dal metodo UINavigationControllerDelegatenavigationController:willShowViewController:animated:Annullamento interattivo gesto UINavigationController pop non chiamare i metodi UINavigationControllerDelegate

Se si annulla la transizione, sia viewWillAppear: e viewDidAppear: ottenere chiamata sul controller vista superiore come previsto.

Tuttavia, nessuno dei metodi delegati navigationController:willShowViewController:animated: o navigationController:didShowViewController:animated: viene chiamato.

Sembra che almeno uno o entrambi dovrebbero essere chiamati considerando i metodi del ciclo di vita di visualizzazione di UIViewController. Mi chiedo se questo sia intenzionale o un bug in UINavigationController.

Ciò di cui ho veramente bisogno è poter vedere quando viene annullato un pop interattivo, all'interno della sottoclasse UINavigationController o UINavigationControllerDelegate. C'è un modo ovvio per farlo?

modificare

sto ancora cercando una soluzione a questo, ma vorrei ricordare che ho segnalato questo problema come un bug con Apple. Guardando la documentazione, non c'è motivo per cui questi metodi delegati non vengano richiamati, specialmente considerando che i metodi equivalenti di ciclo di vita della vista vengono richiamati.

oggi edit2

Il mio biglietto radar (16.823.313) è stato chiuso (21 Maggio 2015) e contrassegnato come previsto. :(

Engineering ha stabilito che questo problema si comporta come previsto in base sulle seguenti informazioni:.

Questo è in realtà il comportamento corretto La transizione navigazione che sta accadendo da B -> A, se si annulla è metà di transizione, si non sarà possibile ottenere il didShowViewController:. metodo a cancellazione di questa transizione non dovrebbe essere considerato una transizione da a -> B perché non hai mai realmente raggiunto A.

vista [Will/Did] App anche l'orecchio dovrebbe essere chiamato come previsto.

Piuttosto un fiasco questo è il caso in quanto è controintuitivo, ma la soluzione nella mia risposta qui di seguito dovrebbe funzionare bene per il prossimo futuro, almeno per il mio caso d'uso.

+0

ce l'hai un numero di bug report che posso ingannare? –

+0

Ciao Tim. Il mio numero di segnalazione bug è '16823313'. Hanno provato a contrassegnarlo come risolto con il primo iOS 8 beta dello scorso giugno e l'ho riaperto dopo aver confermato che era ancora rotto. Nessuna attività da allora. – Dima

risposta

32

Per chiunque sia interessato, ho trovato 2 modi per aggirare questo problema al livello UINavigationControllerDelegate.

  1. Usa KVO per osservare la proprietà state del interactivePopGestureRecognizer. Purtroppo, l'annullamento della transizione non cambia lo stato in UIGestureRecognizerStateFailed, ma invece solo UIGestureRecognizerStateEnded, quindi è necessario scrivere un po 'di codice aggiuntivo per tenere traccia di ciò che è accaduto se era necessario discernere tra un pop cancellato o completato.

  2. Dopo averlo provato, questa è probabilmente la soluzione migliore: utilizzare il metodo navigationController:willShowViewController:animated: per aggiungere un blocco di notifica al coordinatore della transizione. Sembra qualcosa di simile:

    - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated 
    { 
        [[self transitionCoordinator] notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> context) 
        { 
         if([context isCancelled]) 
         { 
          UIViewController *fromViewController = [context viewControllerForKey:UITransitionContextFromViewControllerKey]; 
          [self navigationController:navigationController willShowViewController:fromViewController animated:animated]; 
    
          if([self respondsToSelector:@selector(navigationController:didShowViewController:animated:)]) 
          { 
           NSTimeInterval animationCompletion = [context transitionDuration] * (double)[context percentComplete]; 
    
           dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((uint64_t)animationCompletion * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
            [self navigationController:navigationController didShowViewController:fromViewController animated:animated]; 
           }); 
          } 
    
    
         } 
        }]; 
    } 
    

ho esitato a utilizzare questa soluzione in un primo momento perché la documentazione era chiaro circa se o non è possibile impostare più di uno di questi (poiché in questo caso, se un anche il controller di visualizzazione inconsapevole imposta il proprio blocco di notifica, potrebbe potenzialmente sostituirlo o sostituirlo con questo). Dopo averlo testato, sembra che non sia una relazione 1: 1 e puoi aggiungere più blocchi di notifica in modo sicuro.

modificare

ho modificato il codice di cui sopra per ritardare la chiamata navigationController:didShowViewController:animated: essere chiamato solo quando si suppone che l'animazione per essere completato in modo che corrisponda più da vicino il comportamento predefinito previsto.

+0

Ottima soluzione! Stavo affrontando lo stesso problema. Ma per quanto riguarda viewControllers/topViewController? Nel momento in cui chiami questi metodi il controller di visualizzazione non viene aggiunto allo stack del controller di navigazione, quindi topViewController mantiene il vecchio valore. – deej

+0

Appena notato si attiva quando il gesto termina ma il controller della vista continua a muoversi – deej

+0

Inoltre puoi ottenere lo stato senza KVO utilizzando la soluzione da http://stackoverflow.com/questions/21298051 – deej

0

Ho tradotto @ Dima di answer a Swift per il mio progetto:

func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) { 

    transitionCoordinator()?.notifyWhenInteractionEndsUsingBlock { context in 
     guard context.isCancelled(), let fromViewController = context.viewControllerForKey(UITransitionContextFromViewControllerKey) else { return } 

     self.navigationController(self, willShowViewController: fromViewController, animated: animated) 

     let animationCompletion: NSTimeInterval = context.transitionDuration() * Double(context.percentComplete()) 

     let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC))) 
     dispatch_after(delayTime, dispatch_get_main_queue()) { 
      self.navigationController(self, didShowViewController: fromViewController, animated: animated) 
     }    
    } 

    /* Your normal behavior goes here */ 

} 

nota che io non verificare l'esistenza di un'implementazione di navigationController(_:didShowViewController:animated:), anche se credo che questa opzione è selezionata al momento della compilazione a Swift e che otterrai un errore del compilatore se tenti di chiamare questo quando non è implementato.

+1

Ottimo lavoro! Vorrei aggiungere, però, che questo errore di compilazione si verificherà anche in Objective-C. Il motivo per cui ho inserito questo check in nella mia risposta originale è che non dovresti modificare questo codice patch in base al fatto che tu non applichi o meno il metodo '... didShow ...'. Fondamentalmente, è una comodità in quanto la patch funzionerà sia che tu l'abbia o meno. – Dima

2

Swift 3:

func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) { 
    transitionCoordinator?.notifyWhenInteractionEnds { context in 
     guard context.isCancelled, let fromViewController = context.viewController(forKey: UITransitionContextViewControllerKey.from) else { return } 
     self.navigationController(self, willShow: fromViewController, animated: animated) 
     let animationCompletion: TimeInterval = context.transitionDuration * Double(context.percentComplete) 
     DispatchQueue.main.asyncAfter(deadline: .now() + animationCompletion) { 
      self.navigationController(self, didShow: fromViewController, animated: animated) 
     } 
    } 
} 
+0

Grazie per l'aggiunta! – Dima