2016-06-01 13 views
7

Ich habe eine bestehende iPhone App, der ich eine UISplitViewController hinzufüge. Der iPad-Teil funktioniert wie ein Zauber, aber ich habe einen garantierten Absturz mit dem iPhone 6 (S) Plus.iPhone 6 Plus Absturz mit rekursivem UISplitViewController _canBecomeDeepestUnibiguousResponder

Setup - Master ist ein UITabBarController. Das erste Detail ist eine Ansicht mit einer Platzhalter-Logoansicht. Sobald ein Objekt ausgewählt ist, wird das Detail durch eine UITabBarController ersetzt.

Immer, wenn ich ein Objekt auswähle und Detail im iPhone 6 Plus öffne und es vom Hochformat (Detail nur sichtbar) in das Querformat (wo der Master sichtbar wäre) dreht, stürzt es ab. Dies tritt nicht bei der Rotation mit der Platzhalter-Detailansicht auf.

Vor dem Absturz ruft es die Delegiertenmethoden primaryViewControllerForExpandingSplitViewController und splitViewController(splitViewController: UISplitViewController, separateSecondaryViewControllerFromPrimaryViewController auf. Auf dem iPad funktioniert jedoch alles einwandfrei.

Ich habe schon eine Menge gesucht und nur ein paar Twitter Erwähnungen von dieser Art von Absturz gesehen. Dinge wie Einstellung oder Nichteinstellung der displayModeButtonItem helfen nicht.

ich diesen Absturz in einem frischen Projekt neu erstellt - es kann hier heruntergeladen werden: https://github.com/sschale/SplitViewCrash/

Crash-Log:

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 

Antwort

4

Auch diese stürzt auf dem iPad. Verwenden Sie Multitasking, um die Größe der App auf Kompakte Breite zu ändern (z. B. 1/3 Bildschirm), drücken Sie die Schaltfläche Launch Detail, und ändern Sie dann die Größe auf Reguläre Breite.

Wenn Sie in der kompakten Breite sind, ist der Split-View-Controller "zusammengebrochen". Das bedeutet, dass es nicht länger separate primäre und sekundäre View-Controller gleichzeitig anzeigt, sondern sie in eine einzige View-Controller-Hierarchie "zusammenfaltet". Wenn es in dieser Umgebung ist, braucht es oft deine Hilfe, um vernünftig zu handeln. Das Standardverhalten funktioniert gut, wenn sowohl der primäre als auch der sekundäre Ansichtscontroller UINavigationControllers sind, in anderen Fällen jedoch nicht.

(In Ihrer App ist Ihr primärer ein UITabBarController, und nachdem Sie "Launch Detail" einmal gestartet haben, ist der sekundäre auch ein UITabBarController. Vielleicht sollten Sie dieses Design überdenken, weil es die Dinge schwieriger macht. Lesen Sie weiter.)

Ihre App "Launch Detail" Taste führt eine "Show Detail" segue, die effektiv diese Methode auf UISplitViewController ruft:

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

Notieren Sie sich den Kommentar in der Kopfzeile:

// 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. 

Mit "Master View Controller oder Detail View Controller" wird der aktuell angezeigte View Controller bezeichnet, in Ihrem Fall ein UITabBarController. Aber UITabBarController implementiert nichts für showViewController(), weil es nicht genug Informationen hat - wo würde es einen View-Controller zeigen? Würde es eine neue Registerkarte hinzufügen oder eine alte ersetzen oder was?

Also, als Ergebnis erhalten Sie diese Fallback, Vollbild-Präsentation. Ich bezweifle, dass Sie diese Benutzererfahrung wirklich wollen.

Später, wenn die Größe wieder zu Regular wechselt und der Split-View-Controller erweitert wird, wird er durch die Präsentation verwirrt und stürzt schließlich ab. (Sehen Sie, was ich meine, wenn die Vorgaben nicht sehr gut sind?)

Eine Möglichkeit, das zu beheben, besteht darin, die Delegate-Methode zu implementieren, um die showDetail zu behandeln. Wenn die Breite kompakt ist, suchen Sie explizit den Ansichtscontroller, dem Sie den neuen Ansichtscontroller hinzufügen möchten, und tun Sie es. Ich glaube, Sie wahrscheinlich auf die nav-Controller in der ersten Registerkarte schieben wollen:

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 
} 

Wenn Sie das tun, werden Sie auch dieses Delegatmethode implementieren möchten. Wenn die Größe wieder in "Normal" geändert wird, sollten Sie die "Erweiterung" bearbeiten, indem Sie diesen Ansichtscontroller aus dem gleichen Nav-Controller herausholen und dann zurücksenden:

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

Vielen Dank für Ihre ausführliche Antwort. Meine UI war ursprünglich ein 'modaler' Übergang zum 'UITabBarController', nachdem ein Datensatz vom ursprünglichen 'UITabBarController' ausgewählt wurde (aber andere Registerkarten für nicht-recordspezifische Elemente) - und die meisten meiner App sind darin enthalten, so dass der Der Übergang wurde in "PresentDetail" geändert. Ich denke immer noch, dass es als Split-View-Controller relativ gut funktioniert, aber ich bin kein UI-Experte. Ich werde Ihre Vorschläge heute Nachmittag umsetzen. Danke noch einmal. – sschale

+0

Um weiterzumachen, haben mich Ihre Vorschläge auf den richtigen Weg gebracht - ich musste schließlich 4 der "Delegierten" -Eigenschaften implementieren, um zu erreichen, was ich will. Es funktioniert jetzt (fast) perfekt! – sschale

+0

Froh, dass es funktioniert! –