2016-06-01 18 views
7

Ho un'app per iPhone che sto aggiungendo a UISplitViewController. La parte iPad funziona come un fascino, ma ho un crash garantito con l'iPhone 6 (S) Plus.iPhone 6 Plus UISplitViewController si arresta in modo anomalo con ricorsivo _canBecomeDeepestUnambiguousResponder

Impostazione - Master è un UITabBarController. I dettagli iniziali sono una vista con una vista logo segnaposto. Una volta selezionato un oggetto, il dettaglio viene sostituito con un UITabBarController.

Ogni volta che seleziono un elemento e apro Dettagli in iPhone 6 Plus e lo faccio ruotare da verticale (visibile solo in dettaglio) a orizzontale (in cui il master sarebbe visibile), si blocca. Questo non si verifica in rotazione con la vista dettagli segnaposto.

Prima dell'arresto, chiama i metodi delegati primaryViewControllerForExpandingSplitViewController e splitViewController(splitViewController: UISplitViewController, separateSecondaryViewControllerFromPrimaryViewController. Tuttavia, tutto funziona perfettamente con l'iPad.

Ho già fatto un sacco di ricerche e ho visto solo un paio di messaggi su Twitter di questo tipo di incidente. Cose come l'impostazione o non impostare il displayModeButtonItem non aiutano.

Ho ricreato questo incidente in un progetto fresco - può essere scaricato qui: registro https://github.com/sschale/SplitViewCrash/

Crash:

Crashed Thread:  0 Dispatch queue: com.apple.main-thread 

Exception Type:  EXC_BAD_ACCESS (SIGSEGV) 
Exception Codes:  KERN_PROTECTION_FAILURE at 0x00007fff53609ff8 
Exception Note:  EXC_CORPSE_NOTIFY 

VM Regions Near 0x7fff53609ff8: 
    MALLOC_TINY   00007f8405000000-00007f8405300000 [ 3072K] rw-/rwx SM=PRV 
--> STACK GUARD   00007fff4fe0a000-00007fff5360a000 [ 56.0M] ---/rwx SM=NUL stack guard for thread 0 
    Stack     00007fff5360a000-00007fff53dff000 [ 8148K] rw-/rwx SM=COW thread 0 

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread 0 liboainject.dylib    0x000000010e5e59b2 
0 liboainject.dylib    0x000000010e5e59b2 

_writeEventToSharedMemory + 27 
1 liboainject.dylib    0x000000010e5e55d7 _OARecordFinalEvent + 1161 
2 liboainject.dylib    0x000000010e5e79f1 ___swapMethods_block_invoke_6 + 338 
3 libobjc.A.dylib     0x000000010f4f9b6b weak_read_no_lock + 89 
4 libobjc.A.dylib     0x000000010f4fa4c6 objc_loadWeakRetained + 104 
5 com.apple.UIKit     0x00000001110510b6 -[UIViewController presentedViewController] + 58 
6 com.apple.UIKit     0x0000000111033fc6 -[UIViewController _canBecomeDeepestUnambiguousResponder] + 31 
7 com.apple.UIKit     0x0000000111033fde -[UIViewController _canBecomeDeepestUnambiguousResponder] + 55 
8 com.apple.UIKit     0x0000000111033fde -[UIViewController _canBecomeDeepestUnambiguousResponder] + 55 
9 com.apple.UIKit     0x0000000111033fde -[UIViewController _canBecomeDeepestUnambiguousResponder] + 55 
10 com.apple.UIKit     0x0000000111033fde -[UIViewController _canBecomeDeepestUnambiguousResponder] + 55 
//(500 more of those) 
.... 

Thread 1:: Dispatch queue: com.apple.libdispatch-manager 
0 libsystem_kernel.dylib   0x0000000116e49ee2 kevent64 + 10 
1 libdispatch.dylib    0x0000000116ac57f0 _dispatch_mgr_invoke + 260 
2 libdispatch.dylib    0x0000000116ac558a _dispatch_mgr_thread + 54 

Thread 2: 
0 libsystem_kernel.dylib   0x0000000116e495e2 __workq_kernreturn + 10 
1 libsystem_pthread.dylib   0x0000000116e0d578 _pthread_wqthread + 1283 
2 libsystem_pthread.dylib   0x0000000116e0b341 start_wqthread + 13 

Thread 3: 
0 libsystem_kernel.dylib   0x0000000116e495e2 __workq_kernreturn + 10 
1 libsystem_pthread.dylib   0x0000000116e0d578 _pthread_wqthread + 1283 
2 libsystem_pthread.dylib   0x0000000116e0b341 start_wqthread + 13 

Thread 4: 
0 libsystem_kernel.dylib   0x0000000116e495e2 __workq_kernreturn + 10 
1 libsystem_pthread.dylib   0x0000000116e0d578 _pthread_wqthread + 1283 
2 libsystem_pthread.dylib   0x0000000116e0b341 start_wqthread + 13 

Thread 5: 
0 libsystem_kernel.dylib   0x0000000116e495e2 __workq_kernreturn + 10 
1 libsystem_pthread.dylib   0x0000000116e0d578 _pthread_wqthread + 1283 
2 libsystem_pthread.dylib   0x0000000116e0b341 start_wqthread + 13 

risposta

4

Questo si blocca su iPad troppo. Usa Multitasking per ridimensionare l'app a larghezza compatta (ad esempio 1/3o schermo), premi il pulsante Avvia dettaglio, quindi ridimensiona a Larghezza normale.

Quando si è in larghezza compatta, il controller della vista divisa è "compresso". Ciò significa che non mostra più controllori di vista primaria e secondaria separati allo stesso tempo, ma li "collassa" in una gerarchia di controller a vista singola. Quando è in quell'ambiente, spesso ha bisogno del tuo aiuto per agire in modo sensato. Il comportamento predefinito funziona bene quando entrambi i controller di visualizzazione primari e secondari sono UINavigationControllers, ma non in altri casi.

(Nella tua app, il tuo primario è un UITabBarController, e dopo aver eseguito "Launch Detail" una volta, anche il secondario è un UITabBarController. Puoi riconsiderare tale design, perché rende le cose più difficili.

tasto "Launch Particolare" della tua app esegue un segue "Mostra Dettagli", che chiama in modo efficace questo metodo su UISplitViewController:

public func showDetailViewController(vc: UIViewController, sender: AnyObject?) 

nota il commento nell'intestazione:

// In a horizontally-compact environment the master view controller 
// or detail view controller is sent the showViewController:sender: 
// message. If neither one of them provide an implementation for this 
// method then it will fall back to a full screen presentation. 

Per "controller di visualizzazione principale o controller di visualizzazione dettagli", indica il controller di visualizzazione attualmente visualizzato, che nel tuo caso è un UITabBarController. Ma UITabBarController non implementa nulla per showViewController() perché non ha abbastanza informazioni - dove mostrerebbe un controller di visualizzazione? Aggiungerebbe una nuova scheda o sostituirà una vecchia o cosa?

Quindi, come risultato, si ottiene quel fallback, presentazione a schermo intero. Dubito che tu voglia davvero quell'esperienza utente.

Successivamente, quando la dimensione torna a regolare e il controller della vista divisa si espande, viene confuso dalla presentazione e alla fine si arresta in modo anomalo. (Vedi cosa intendo per le impostazioni predefinite che non sono molto buone?)

Un modo per risolvere questo problema è implementare il metodo delegato per gestire showDetail. Quando la larghezza è Compatta, trova esplicitamente il controller della vista su cui desideri posizionare il nuovo controller della vista e fallo. Penso che probabilmente vuole spingere sul controller di navigazione nella prima scheda:

func splitViewController(splitViewController: UISplitViewController, showDetailViewController vc: UIViewController, sender: AnyObject?) -> Bool 
{ 
    if splitViewController.traitCollection.horizontalSizeClass == .Compact { 
     // The default implementation will not handle this properly. 
     // Find the appropriate navigation controller and push onto it. 
     // It would be better to have a direct outlet to the appropriate navigation controller, 
     // but this will work for an example... 

     if let tabBarController = splitViewController.viewControllers.first as? UITabBarController { 
      if let navController = tabBarController.viewControllers?.first as? UINavigationController { 
       navController.pushViewController(vc, animated: true) 

       // we handled the "show detail", so split view controller, 
       // please don't do anything else 
       return true 
      } 
     } 
    } 

    // we did not handle the "show detail", so split view controller, 
    // please do your default behavior 
    return false 
} 

Se lo fai, vorrete anche per implementare questo metodo delegato. Quando la dimensione viene cambiata in Regular, dovrai gestire l'espansione espandendo il controller di visualizzazione dallo stesso controller nav, quindi restituendolo:

optional public func splitViewController(
    splitViewController: UISplitViewController 
    separateSecondaryViewControllerFromPrimaryViewController 
     primaryViewController: UIViewController) -> UIViewController? 
+0

Grazie per la tua risposta dettagliata. La mia UI era originariamente un 'modale' seguito a' UITabBarController' dopo che un record era stato selezionato dall'originale 'UITabBarController' (ma altre schede necessarie per elementi non specifici del record) - e la maggior parte della mia app è contenuta in esso, quindi il la transizione è stata cambiata in 'presentDetail'. Sento ancora che funziona relativamente bene come un controller di visualizzazione divisa, ma non sono un esperto di interfaccia utente. Attenderò i tuoi suggerimenti questo pomeriggio. Grazie ancora. – sschale

+0

Per dare seguito, i vostri suggerimenti mi hanno messo sulla strada giusta: alla fine ho dovuto implementare 4 proprietà 'delegate' per ottenere ciò che voglio. Ora funziona (quasi) perfettamente! – sschale

+0

Felice che tu abbia funzionato! –