5. Mai 2018

Hierarchische Navigation mit UINavigationController

Ein UINavigationController ist ein UIViewController, der einen Stapel von UIViewControllern verwaltet. Die Navigations-Bar mit Zurück-Button und Titel wird dabei automatisch angezeigt und aktualisiert:

UINavigationController-Stapel mit Navigations-Bar

Initial zeigt ein Navigation Controller seinen Root View Controller an, der im Storyboard über ein Relationship-Segue-Übergang festgelegt wird:

Root View Controller relationship im Storyboard

Innerhalb eines UINavigationControllers bekommen UIViewController ein navigationItem zur Konfiguration der Navigation-Bar gesetzt:

Navigation-Item im StoryBoard

Programmatische Navigation

Alternativ zu Storyboard-Segues kann die Navigation über Controller-Methoden erfolgen. Dazu kennen UIViewController den Navigations-Controller in dem sie enthalten sind. Mit pushViewController wird ein View-Controller auf den Stapel gelegt und angezeigt:

let viewController = self.storyboard!.instantiateViewController(withIdentifier:"Example")
self.navigationController?.pushViewController(viewController, animated: true)

popViewController entfernt den aktuellen View-Controller vom Stapel und zeigt den darunterliegenden an:

self.navigationController?.popViewController(animated:true)

Weitere View-Controller

  1. Laden Sie den Start-Stand von dem Flashcards-Beispielprojekt: Flashcards.zip.

  2. Wählen Sie im Storyboard den Start-Controller mit dem Button aus und erstellen Sie mit Editor » Embed in » Navigation Controller einen Navigations-Controller. Damit wird ein Navigations-Controller erstellt und ein Segue-Übergang der eine Root View Controller-Beziehung zwischen den beiden Controllern herstellt:

    Navigations-Controller erstellen
  3. Fügen Sie beiden Controllern über die Object Library ein Navigation Item hinzu und vergeben Sie die Titel Flashcards und Lernkarte für die beiden View-Controller:

    Navigation Item: Titel für View-Controller
  4. Starten Sie die App und prüfen Sie den Übergang zwischen den Controllern.

  5. Übernehmen Sie die Änderungen mittels Source Control » Commit in das lokale Git-Repository. Prüfen Sie die Änderungen im Projekt und geben Sie als Commit-Nachricht "10.1. Navigations-Controller hinzugefügt" an.

Tutorial 2: Segues programmatisch beeinflussen - Übergang zur Lernkarte

Es soll nun das Lernen von mehreren Lernkarten implementiert werden. Dazu wird der CardViewController mit der zu lernenden Lernkarte konfiguriert:

  1. Passen Sie die Eigenschaft card im CardViewController so an, dass diese nicht mehr mit Beispieldaten befüllt wird, sondern dem Controller gesetzt werden muss:

    class CardViewController: UIViewController {
    
        // ...
    
        var card : Card? = nil
        
        // ...
    
    }
  2. Erstellen Sie mit New File » iOS » Cocoa Touch Class eine neue Klasse in der Controller-Gruppe. Nennen Sie diese StartViewController und lassen Sie sie von UIViewController erben.

  3. Geben Sie die soeben erstellte Klasse im Storyboard als Custom Class für den Start-ViewController an.

  4. Entfernen Sie den Beispielcode in StartViewController und überschreiben Sie die Methode prepare(for:), so dass dem CardViewController die zu lernende Karte beim Segue-Übergang gesetzt wird:

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let controller = segue.destination as? CardViewController {
            controller.card = FlashcardsModel.shared.cards.first
        }
    }
  5. Starten Sie die App testweise und committen Sie Ihre Änderungen als „10.2. Übergang zur Lernkarte“.

Tutorial 3: Mehrere Lernkarten

In diesem Tutorial wird das Beispiel so erweitert, dass mehrere Lernkarten nacheinander gelernt werden können.

  1. Ergänzen Sie im CardViewController eine Eigenschaft cards mit der mehrere zu lernende Lernkarten gesetzt werden können:

    class CardViewController: UIViewController {
        // ...
        var cards : [Card] = []
        var card : Card?
        // ...
    }
  2. Passen Sie im StartViewController die prepareForSegue-Methode so an, dass die Liste von Lernkarten statt der einzelnen Karte gesetzt wird.

    controller.cards = FlashcardsModel.shared.cards
  3. Implementieren Sie eine Methode showNextCard, die die nächste Lernkarte im Kartenstapel anzeigt und die Darstellung aktualisiert. Verwenden Sie die Methode Array#removeFirst, um die Karte aus dem Kartenstapel zu entfernen. Rufen Sie in viewDidLoad statt updateView die Methode showNextCard auf.

    class CardViewController: UIViewController {
    
        // ...
        
        override func viewDidLoad() {
            super.viewDidLoad()
            showNextCard() 
        }
    
        func showNextCard() {
        // Nächste Lernkarte:
        if let nextCard = cards.first {
            cards.removeFirst()
            self.card = nextCard
            self.side = .front
            updateView()
        }
    }
    
    }
  4. Erweitern Sie showNextCard so, dass der ViewController vom Navigations-Stapel entfernt wird, nachdem alle Karten gelernt wurden:

    func showNextCard() {
        if ...
            // Nächste Lernkarte:
            // ...
        } else {
        // Letzte Lernkarte: 
        // ViewController vom Navigations-Stapel entfernen
        self.navigationController?.popViewController(animated: true)
    }
    }
  5. Implementieren Sie die Action-Methoden wrong und correct durch den Aufruf von showNextCard().

  6. Ergänzen Sie updateView so, dass die Anzahl der zu lernenden Karten als Titel gesetzt wird:

    fileprivate func updateView() {
        // ...
        self.navigationItem.title = "Noch \(self.cards.count + 1) Lernkarten"
    }
  7. Starten Sie die App testweise und committen Sie Ihre Änderungen als „10.3. Mehrere Lernkarten“.