Managing View Controllers with Container View Controllers

  • Managing View Controllers with Container View Controllers

    Hallo zusammen,

    ich bin gerade dabei den Seitenaufbau meiner App zu gestalten, doch bei einer Konstruktion schmiert mir die App ab.

    Ich habe einen Tab Bar VC, welcher auf einen bestimmten VC (GuVInitVC) verweist. Dieser VC hat oben links ein Segmented Control mit 3 Buttons.
    Diese drei Buttons verweisen jeweils auf einen VC (PaymentsInitVC, YieldInitVC, OtherInitVC), welcher wiederum ein Segmented Control, dieses mal oben rechts mit 2 Buttons hat.
    Diese beiden Buttons verweisen auf je einen VC unterhalb der Inits.

    Habe ich die Verbindungen falsch gesetzt oder warum geht das nicht?
    Oder liegt es daran, daß man dieses System nicht verschachteln kann?
    Mich wundert es auch, daß sich der Segmented Control auf der GuV Seite nicht an meine Programmierung anpasst und noch die Standarddarstellung aufweist. Liegt hier der Hund begraben?

    Ein paar Screenshots des UI sind im Anhang, der Code am Ende der Nachricht.

    Besten Dank für einen Lösungshinweis!

    mac

    P.s.: Wie ist es denn am sinnvollsten Code zur Diskussion zur Verfügung zu stellen? So wie geschehen in den Text kopieren?

    Code des GuVInitVC:


    Quellcode

    1. //
    2. import UIKit
    3. class GuVInitVC: UIViewController {
    4. @IBOutlet var segmentedControl: UISegmentedControl!
    5. lazy var PaymentsInitVC: PaymentsInitVC = {
    6. let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
    7. var viewController = storyboard.instantiateViewController(withIdentifier: "PaymentsInitVC") as! PaymentsInitVC
    8. self.addViewControllerAsChildViewController(childViewController: viewController)
    9. return viewController
    10. }()
    11. lazy var YieldInitVC: YieldInitVC = {
    12. let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
    13. var viewController = storyboard.instantiateViewController(withIdentifier: "YieldInitVC") as! YieldInitVC
    14. self.addViewControllerAsChildViewController(childViewController: viewController)
    15. return viewController
    16. }()
    17. lazy var OtherInitVC: OtherInitVC = {
    18. let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
    19. var viewController = storyboard.instantiateViewController(withIdentifier: "OtherInitVC") as! OtherInitVC
    20. self.addViewControllerAsChildViewController(childViewController: viewController)
    21. return viewController
    22. }()
    23. override func viewDidLoad() {
    24. super.viewDidLoad()
    25. setupView()
    26. }
    27. //Mark: - View Methods
    28. private func setupView() {
    29. setupSegmentedControl()
    30. updateView()
    31. }
    32. private func updateView() {
    33. PaymentsInitVC.view.isHidden = segmentedControl.selectedSegmentIndex != 0
    34. YieldInitVC.view.isHidden = segmentedControl.selectedSegmentIndex != 1
    35. OtherInitVC.view.isHidden = segmentedControl.selectedSegmentIndex != 2
    36. }
    37. //Mark: -
    38. private func setupSegmentedControl() {
    39. segmentedControl.removeAllSegments()
    40. segmentedControl.insertSegment(withTitle: "Aufwände", at: 0, animated: false)
    41. segmentedControl.insertSegment(withTitle: "Erträge", at: 1, animated: false)
    42. segmentedControl.insertSegment(withTitle: "Andere A&E", at: 2, animated: false)
    43. segmentedControl.addTarget(self, action: #selector(selectionDidChange(sender:)), for: .valueChanged)
    44. segmentedControl.selectedSegmentIndex = 0
    45. }
    46. //Mark: - Actions
    47. @ObjC func selectionDidChange(sender: UISegmentedControl) {
    48. updateView()
    49. }
    50. //Mark: - Helper Methods
    51. private func addViewControllerAsChildViewController(childViewController: UIViewController){
    52. addChildViewController(childViewController)
    53. view.addSubview(childViewController.view)
    54. childViewController.view.frame = view.bounds
    55. childViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    56. childViewController.didMove(toParentViewController: self)
    57. }
    58. private func removeViewControllerAsChildViewCntroller(ChildViewController: UIViewController) {
    59. ChildViewController.willMove(toParentViewController: nil)
    60. ChildViewController.view.removeFromSuperview()
    61. ChildViewController.removeFromParentViewController()
    62. }
    63. }
    Alles anzeigen





    Code eines darunter liegenden VC:


    Quellcode

    1. //
    2. import UIKit
    3. class PaymentsInitVC: UIViewController {
    4. @IBOutlet var segmentedControl: UISegmentedControl!
    5. lazy var PaymentsMonthlyVC: PaymentsMonthlyVC = {
    6. let storyboard = UIStoryboard(name: "PaymentsInit", bundle: Bundle.main)
    7. var viewController = storyboard.instantiateViewController(withIdentifier: "PaymentsMonthlyVC") as! PaymentsMonthlyVC
    8. self.addViewControllerAsChildViewController(childViewController: viewController)
    9. return viewController
    10. }()
    11. lazy var PaymentsOnceVC: PaymentsOnceVC = {
    12. let storyboard = UIStoryboard(name: "PaymentsInit", bundle: Bundle.main)
    13. var viewController = storyboard.instantiateViewController(withIdentifier: "PaymentsOnceVC") as! PaymentsOnceVC
    14. self.addViewControllerAsChildViewController(childViewController: viewController)
    15. return viewController
    16. }()
    17. override func viewDidLoad() {
    18. super.viewDidLoad()
    19. setupView()
    20. }
    21. //MARK: - View Methods
    22. private func setupView() {
    23. setupSegmentedControl()
    24. updateView()
    25. }
    26. private func updateView() {
    27. PaymentsMonthlyVC.view.isHidden = !(segmentedControl.selectedSegmentIndex == 0)
    28. PaymentsOnceVC.view.isHidden = (segmentedControl.selectedSegmentIndex == 0)
    29. }
    30. // MARK: -
    31. private func setupSegmentedControl() {
    32. segmentedControl.removeAllSegments()
    33. segmentedControl.insertSegment(withTitle: "PaymentsMonthly", at: 0, animated: false)
    34. segmentedControl.insertSegment(withTitle: "PaymentsOnce", at: 1, animated: false)
    35. segmentedControl.addTarget(self, action: #selector(selectionDidChange(sender:)), for: .valueChanged)
    36. segmentedControl.selectedSegmentIndex = 0
    37. }
    38. // MARK: - Actions
    39. @ObjC func selectionDidChange(sender: UISegmentedControl) {
    40. updateView()
    41. }
    42. // MARK: - Helper Methods
    43. private func addViewControllerAsChildViewController(childViewController: UIViewController){
    44. addChildViewController(childViewController)
    45. view.addSubview(childViewController.view)
    46. childViewController.view.frame = view.bounds
    47. childViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    48. childViewController.didMove(toParentViewController: self)
    49. }
    50. private func removeViewControllerAsChildViewController(childViewController: UIViewController){
    51. childViewController.willMove(toParentViewController: nil)
    52. childViewController.view.removeFromSuperview()
    53. childViewController.removeFromParentViewController()
    54. }
    55. }
    Alles anzeigen
    Bilder
    • Bildschirmfoto 2018-04-13 um 07.31.12.png

      46,46 kB, 974×877, 15 mal angesehen
    • Bildschirmfoto 2018-04-13 um 07.31.48.png

      25,49 kB, 587×860, 11 mal angesehen
    • Bildschirmfoto 2018-04-13 um 07.32.08.png

      21,24 kB, 905×531, 10 mal angesehen
    • Bildschirmfoto 2018-04-13 um 07.35.53.png

      104,32 kB, 991×854, 10 mal angesehen

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Bertone105 ()

  • Bertone105 schrieb:


    P.s.: Wie ist es denn am sinnvollsten Code zur Diskussion zur Verfügung zu stellen? So wie geschehen in den Text kopieren?
    Jein ... wenn Du dann noch das "Code-Tag" des Editors benutzt, wird der Code (1.) lesbarer und (2.) standardmäßig komprimiert dargestellt, das hilft den Lesern ungemein. Zumindest bei mir schwindet die Lust sofort, wenn ich Code-Segmente aufgrund der verwendeten Proportionalschrift kaum lesen kann. Apropos "Code-Segemente": Es ist ausserdem sinnvoll, Dein Problem auf einen überschaubaren Aspekt einzugrenzen und dann auch nur relevanten Code zu posten. Ich zumindest tue mich zeitlich damit schwer, komplette Code-Reviews mit der Fragestellung "funktioniert nicht" durchzuführen. Was genau funktioniert nicht? "Schmiert ab" ist eher unqualifiziert. Wie lautet der Fehler, wann tritt er auf, wie sieht die Konsolen-Ausgabe aus? Etwas mehr gezielte Informationen sind schon nötig.

    Grundsätzlich würde ich ein Konstrukt verschachtelter Views immer über einen Navigation-Stack lösen, auf den ich nur pushe (bzw. von dem wieder entfernt wird). Ob ich dafür dann die Standard-UI verwende - was ich persönlich bevorzuge - oder eigene Schaltflächen verwende, ist für die Funktion irrelevant. Für Dialoge würde ich dann UIPresentationController verwenden, evt. als Popover. Prinzipiell also alles machbar...

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • Bertone105 schrieb:



    ich bin gerade dabei den Seitenaufbau meiner App zu gestalten, doch bei einer Konstruktion schmiert mir die App ab.

    Hm, welche Ausgabe gibt es in der Xcode Console, wenn die App abstürzt. So können wir leider nur Raten, was das Problem ist. Evtl. hat es gar nichts mit dem UI sondern eher mit dem Code zu tun.

    Und nein, den Code habe ich mir nicht komplett durchgelesen, da die Consolen Ausgabe einen besseren Hinweis geben sollte, was/wo klemmt. ;)

    Mit diesen Infos, kann man sich dann gezielt den Code anschauen. :)
  • MCDan schrieb:

    Bertone105 schrieb:

    ich bin gerade dabei den Seitenaufbau meiner App zu gestalten, doch bei einer Konstruktion schmiert mir die App ab.
    Hm, welche Ausgabe gibt es in der Xcode Console, wenn die App abstürzt. So können wir leider nur Raten, was das Problem ist. Evtl. hat es gar nichts mit dem UI sondern eher mit dem Code zu tun.

    Und nein, den Code habe ich mir nicht komplett durchgelesen, da die Consolen Ausgabe einen besseren Hinweis geben sollte, was/wo klemmt. ;)

    Mit diesen Infos, kann man sich dann gezielt den Code anschauen. :)
    Die Console sagt: error: attach by pid '1227' failed -- unable to attach
  • MyMattes schrieb:

    Bertone105 schrieb:

    P.s.: Wie ist es denn am sinnvollsten Code zur Diskussion zur Verfügung zu stellen? So wie geschehen in den Text kopieren?
    Jein ... wenn Du dann noch das "Code-Tag" des Editors benutzt, wird der Code (1.) lesbarer und (2.) standardmäßig komprimiert dargestellt, das hilft den Lesern ungemein. Zumindest bei mir schwindet die Lust sofort, wenn ich Code-Segmente aufgrund der verwendeten Proportionalschrift kaum lesen kann. Apropos "Code-Segemente": Es ist ausserdem sinnvoll, Dein Problem auf einen überschaubaren Aspekt einzugrenzen und dann auch nur relevanten Code zu posten. Ich zumindest tue mich zeitlich damit schwer, komplette Code-Reviews mit der Fragestellung "funktioniert nicht" durchzuführen. Was genau funktioniert nicht? "Schmiert ab" ist eher unqualifiziert. Wie lautet der Fehler, wann tritt er auf, wie sieht die Konsolen-Ausgabe aus? Etwas mehr gezielte Informationen sind schon nötig.
    Grundsätzlich würde ich ein Konstrukt verschachtelter Views immer über einen Navigation-Stack lösen, auf den ich nur pushe (bzw. von dem wieder entfernt wird). Ob ich dafür dann die Standard-UI verwende - was ich persönlich bevorzuge - oder eigene Schaltflächen verwende, ist für die Funktion irrelevant. Für Dialoge würde ich dann UIPresentationController verwenden, evt. als Popover. Prinzipiell also alles machbar...

    Mattes
    Hier noch eine genauere Beschreibung des Fehlers. Sobald ich in der Tab Bar auf den Button mit dem eingangs verschachtelten Seitenaufbau klicke, beendet der Simulator die App. Alle anderen Buttons mit den jeweiligen Seiten darunter (diese haben einen anderen Aufbau und Code) funktionieren. Es muß also meiner Meinung nach (Anfänger) entweder am Code oder an einer fehlenden/fehlerhaften verlinkung liegen.
    Jedoch hab ich keine Anhung, wie ich dem Problem auf die Schliche komme. Und der Code sagt mir nichts.
  • Ja, ich bin auf Build and Run gegangen. Was mich jedoch wundert ist, daß mein Mac ca. 3 Minuten braucht, bis das IPhone 8 Plus im Simulator gestartet hat.

    Hab es gerade nochmal gemacht, da der Simulator ja jetzt geladen war. Jetzt kam folgende Meldung:

    2018-04-13 21:23:27.246166+0200 Life Simulator[2273:307193] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Could not find a storyboard named 'PaymentsInit' in bundle NSBundle </Users/florianmuller/Library/Developer/CoreSimulator/Devices/4DFDA6FC-3898-4553-BC0B-C31C07ECC8CF/data/Containers/Bundle/Application/74F9AE9C-2C6C-4631-A5AB-7E6A5AE33387/Life Simulator.app> (loaded)'
    *** First throw call stack:
    (
    0 CoreFoundation 0x00000001131be12b __exceptionPreprocess + 171
    1 libobjc.A.dylib 0x000000010e977f41 objc_exception_throw + 48
    2 UIKit 0x000000010fb3497e +[UIStoryboard storyboardWithName:bundle:] + 672
    3 Life Simulator 0x000000010df9d4c2 _T0So12UIStoryboardCABSS4name_So6BundleCSg6bundletcfCTO + 82
    4 Life Simulator 0x000000010df9d3b4 _T014Life_Simulator14PaymentsInitVCC0c7MonthlyE0AaDCfgAEycfU_ + 148
    5 Life Simulator 0x000000010df9d27c _T014Life_Simulator14PaymentsInitVCC0c7MonthlyE0AaDCfg + 284
    6 Life Simulator 0x000000010df9db06 _T014Life_Simulator14PaymentsInitVCC10updateView33_E43AF4EBB56EE4B5233CBF7B2DF08881LLyyF + 38
    7 Life Simulator 0x000000010df9dad0 _T014Life_Simulator14PaymentsInitVCC9setupView33_E43AF4EBB56EE4B5233CBF7B2DF08881LLyyF + 32
    8 Life Simulator 0x000000010df9da21 _T014Life_Simulator14PaymentsInitVCC11viewDidLoadyyF + 81
    9 Life Simulator 0x000000010df9da94 _T014Life_Simulator14PaymentsInitVCC11viewDidLoadyyFTo + 36
    10 UIKit 0x000000010f3bc46c -[UIViewController loadViewIfRequired] + 1235
    11 UIKit 0x000000010f3bc8b9 -[UIViewController view] + 27
    12 Life Simulator 0x000000010dfb52da _T014Life_Simulator9GuVInitVCC024addViewControllerAsChildgH033_7A8B003EFB001068CD09474FA1AC70F4LLySo06UIViewH0C05childgH0_tF + 298
    13 Life Simulator 0x000000010dfb3c7c _T014Life_Simulator9GuVInitVCC012PaymentsInitE0AaDCfgAEycfU_ + 300
    14 Life Simulator 0x000000010dfb3aac _T014Life_Simulator9GuVInitVCC012PaymentsInitE0AaDCfg + 284
    15 Life Simulator 0x000000010dfb4696 _T014Life_Simulator9GuVInitVCC10updateView33_7A8B003EFB001068CD09474FA1AC70F4LLyyF + 38
    16 Life Simulator 0x000000010dfb4660 _T014Life_Simulator9GuVInitVCC9setupView33_7A8B003EFB001068CD09474FA1AC70F4LLyyF + 32
    17 Life Simulator 0x000000010dfb45b1 _T014Life_Simulator9GuVInitVCC11viewDidLoadyyF + 81
    18 Life Simulator 0x000000010dfb4624 _T014Life_Simulator9GuVInitVCC11viewDidLoadyyFTo + 36
    19 UIKit 0x000000010f3bc46c -[UIViewController loadViewIfRequired] + 1235
    20 UIKit 0x000000010f403ffc -[UINavigationController _updateScrollViewFromViewController:toViewController:] + 68
    21 UIKit 0x000000010f404338 -[UINavigationController _startTransition:fromViewController:toViewController:] + 153
    22 UIKit 0x000000010f40544f -[UINavigationController _startDeferredTransitionIfNeeded:] + 841
    23 UIKit 0x000000010f4066d3 -[UINavigationController __viewWillLayoutSubviews] + 150
    24 UIKit 0x000000010f6614e2 -[UILayoutContainerView layoutSubviews] + 231
    25 UIKit 0x000000010f2e5a6d -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1439
    26 QuartzCore 0x000000011834861c -[CALayer layoutSublayers] + 159
    27 QuartzCore 0x000000011834c7ad _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 401
    28 QuartzCore 0x00000001182d386c _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 364
    29 QuartzCore 0x0000000118300946 _ZN2CA11Transaction6commitEv + 500
    30 QuartzCore 0x0000000118301694 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 76
    31 CoreFoundation 0x0000000113160c07 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
    32 CoreFoundation 0x0000000113160b5e __CFRunLoopDoObservers + 430
    33 CoreFoundation 0x0000000113145124 __CFRunLoopRun + 1572
    34 CoreFoundation 0x0000000113144889 CFRunLoopRunSpecific + 409
    35 GraphicsServices 0x00000001163f79c6 GSEventRunModal + 62
    36 UIKit 0x000000010f2145d6 UIApplicationMain + 159
    37 Life Simulator 0x000000010dfa5597 main + 55
    38 libdyld.dylib 0x0000000114222d81 start + 1
    39 ??? 0x0000000000000001 0x0 + 1
    )
    libc++abi.dylib: terminating with uncaught exception of type NSException

  • Na also:
    Could not find a storyboard named 'PaymentsInit' in bundle
    Dann vergleiche einmal den Namen des in der Info.plist genannten Haupt-Storyboards mit dem Dateinamen Deiner Storyboard-Datei, da passt etwas nicht. Wahrscheinlich hast Du letzteres einmal umbenannt ohne die Projekt-Properties anzupassen.

    Mattes

    P.S.: @MCDan war schneller und hat die falsche Referenz gesehen :)
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • Bingo!!!

    Das war es. Ich hab aus Versehen den Name vom Storyboard mit dem Namen des VC überschrieben.

    Danke Jungs!

    Eine kurze Frage hätte ich aber noch. Ich habe ja auf den Seiten ein SegmentedControl. Was ist, wenn ich zwei SegmentedControl auf einer Seite unterbringen möchte.
    Genügt es, wenn ich die durchnummeriere? Also:

    @IBOutlet var segmentedControl1: UISegmentedControl1!
    @IBOutlet var segmentedControl2: UISegmentedControl2!

    ...und dann im weiteren Code entsprechend?
  • Was mich wundert ist, daß ich den SegmentedControl des darunter liegenden Containers nicht sehe. Ich hab ja folgenden Aufbau:

    Init VC mit Segmented Control A

    Init 1 VC mit Segmented Control B
    Unterseite 1.1
    Unterseite 1.2
    Init 2 VC mit Segmented Control C
    Unterseite 2.1
    Unterseite 2.2
    Init 3 VC mit Segmented Control D
    Unterseite 3.1
    Unterseite 3.2

    Der Segmented Control A funktioniert richtig und zeigt mir die Seiten 1.1, 2.1 und 3.1. Doch es fehlt deren Segmented Control B-D.
    Befinde ich mich also auf einer der Unterseiten, möchte ich immer den Segmented Control A links oben und den entsprechenden Segmented Control der Unterseite rechts oben sehen.

    Kann es sein, daß er mit meinem Code nur mit einem Segmented Control umgehen kann und dann immer nur den Ersten bzw. Obersten nimmt? (S. Code erster Post)
  • Man man man, die Screenshots sind so klein, dass man da eine Lupe braucht, um etwas zu erkennen.

    Wenn ich es richtig sehe, dann befindet sich das Segmented Control vom GuVInitVC als leftBarButtonItem in der Navigation Bar des Navigation Controllers. Die Segmentes Controls der Child View Controller möchtest Du dann als rightBarButtonItem in der Navigation Bar anzeigen, sobald diese angezeigt werden, richtig? Nur wo ist der Code dazu. Dies geschieht ja nicht von selbst. :D
  • Sorry für die kleine Auflösung. Aber deine Vermutung war richtig, bzw. geht noch etwas weiter. Wenn ich mich auf einem ChildViewController befinde, sollen beide angezeigt werden, also left und right, da ich ja auch wieder ins "Hauptmenü" zurück möchte. Dieser Teil des Codes ist noch nicht programmiert, da ich erst mal das Problem lösen möchte, warum der Segmented Control der Child Views nicht angezeigt wird.

    Ich habe oben ja 2 Codevarianten eingefügt, einmal vom Master (GuVInitVC) und von einem darunter liegenden Child. Damit müsste doch der Code für die jeweiligen Ebenen gegeben sein. Aber wie erwähnt, zeigt er immer nur den Segmented Control des Masters an, auch wenn ich schon auf einem Child bin. Muß ich dann sagen, sobald du auf dem Child bist, ignoriere den SegmentedControl des Master und nimm den vom Child?
  • Du hast in dem Storyboard zwar schön das rightBarButtonItem für das NavigationItem der Child View Controller definiert, allerdings nur in Verbindung mit einem Navigation Controller. Du fügst im GuVInitVC allerdings nur den View des PaymentsInitVC hinzu. Für das NavigationItem bzw, das rightBarButtonItem sehe ich keinen Code. Da musst Du natürlich noch etwas programmieren, da dies nicht automatisch erfolgt. ;)

    In GuVInitVC sollte dies in updateView etwa so aussehen:

    Quellcode

    1. self.navigationItem.rightBarButtonItem = self.PaymentsInitVC.navigationItem.rightBarButtonItem
    um z.B. das rightBarButtonItem für den Navigation Controller zu setzen, wenn der PaymentsInitVC angezeigt wird. Für die anderen View Controller muss Du es entsprechend der Auswahl im Segmented Control machen. ;)