Container-Containment implementieren - Sliding Menu Controller

In diesem Tutorial implementieren wir eine minimalistische Version der Benutzeroberfläche von Facebook / Path. Ziel ist es, zu verstehen, wie View Controller Containment verwendet wird, um einen benutzerdefinierten Ablauf in Ihrer App zu implementieren.


Theoretische Übersicht

View Controller sind ein wesentlicher Bestandteil jeder iOS-Anwendung, egal wie klein, groß, einfach oder komplex. Sie bieten die "Glue-Logic" zwischen dem Datenmodell Ihrer App und der Benutzeroberfläche.

Im Großen und Ganzen gibt es zwei Arten von View-Controllern:

  • Inhaltsansicht-Controller: Diese sind für die Anzeige und Verwaltung sichtbarer Inhalte verantwortlich.
  • Containersteuerungen: Diese verwalten Content-View-Controller und sind für die gesamte Struktur und den gesamten Ablauf der App verantwortlich.

Ein Container-Controller hat möglicherweise eine eigene sichtbare Komponente, fungiert jedoch im Wesentlichen als Host für Content-View-Controller. Container-Controller dienen dazu, das Kommen und Gehen von Content-View-Controllern zu "transportieren".

UINavigationController, UITabBarController und UIPageViewController sind Beispiele für Containeransicht-Controller, die mit dem iOS-SDK geliefert werden. Berücksichtigen Sie, wie sich die drei in Bezug auf die Anwendungsflüsse unterscheiden, zu denen sie führen. Der Navigations-Controller eignet sich hervorragend für eine Drilldown-App, bei der die Auswahl, die der Benutzer auf einem Bildschirm trifft, die Auswahl beeinflusst, die ihm auf dem nächsten Bildschirm angezeigt wird. Der Tab-Leisten-Controller eignet sich hervorragend für Apps mit unabhängigen Funktionen. Sie können durch einfaches Drücken einer Tab-Taste bequem umschalten. Schließlich stellt der Seitensichtcontroller eine Buchmetapher dar, die es dem Benutzer ermöglicht, zwischen Inhaltsseiten hin und her zu springen.

Hierbei ist zu beachten, dass ein Bildschirminhalt, der durch einen dieser Container-View-Controller selbst angezeigt wird, sowohl in Bezug auf die Daten (Modell) als auch auf die Bildschirmpräsentation verwaltet werden muss (die Ansicht), was wiederum die Aufgabe eines View-Controllers sein würde. Jetzt reden wir über Controller für die Inhaltsansicht. Bei einigen Apps, insbesondere auf dem iPad, weil der größere Bildschirm mehr Inhalte auf einmal anzeigen kann, müssen möglicherweise unterschiedliche Ansichten auf dem Bildschirm sogar unabhängig voneinander verwaltet werden. Dies erfordert, dass mehrere Ansichtskontroller gleichzeitig auf dem Bildschirm angezeigt werden. All dies impliziert, dass die View-Controller in einer durchdachten App hierarchisch implementiert werden sollten, wobei sowohl Container- als auch Content-View-Controller ihre jeweilige Rolle spielen sollten.

Vor iOS 5 gab es keine Möglichkeit, eine hierarchische Beziehung (d. H. Eltern-Kind-Beziehung) zwischen zwei Ansichtssteuerungen zu deklarieren, und daher keine "richtige" Möglichkeit, einen benutzerdefinierten Anwendungsfluss zu implementieren. Man musste entweder mit den integrierten Typen auskommen oder es auf eine zufällige Art und Weise tun, was im Wesentlichen darin bestand, Ansichten, die von einem Ansichtscontroller verwaltet werden, in die Ansichtshierarchie der Ansicht einzufügen, die von einem anderen Ansichtscontroller verwaltet wird. Dies würde Inkonsistenzen erzeugen. Zum Beispiel würde eine Ansicht in der Ansichtshierarchie von zwei Controllern sein, ohne dass einer dieser Controller den anderen bestätigt, was manchmal zu einem merkwürdigen Verhalten führt. Containment wurde in iOS 5 eingeführt und in iOS 6 geringfügig verfeinert. Dadurch können die Steuerelemente für übergeordnete und untergeordnete Ansicht in einer Hierarchie formalisiert werden. Im Wesentlichen verlangt die korrekte Eindämmung des Ansichtscontrollers, dass, wenn Ansicht B eine Unteransicht (untergeordnete Ansicht) von Ansicht A ist und sie nicht von demselben Ansichtscontroller verwaltet werden, der Ansichtscontroller von B zum Ansichtscontroller von A werden muss.

Sie werden möglicherweise gefragt, ob es einen konkreten Nutzen gibt, den View Controller Containment bietet, neben dem Vorteil des hierarchischen Designs, das wir besprochen haben. Die Antwort ist ja. Denken Sie daran, dass, wenn ein View-Controller auf dem Bildschirm angezeigt wird oder ausfällt, möglicherweise Ressourcen eingerichtet oder abgebaut werden müssen, dass Informationen vom Dateisystem aufgeräumt, abgerufen oder gespeichert werden müssen. Wir alle kennen Erscheinungsrückrufe. Durch die explizite Deklaration der Parent-Child-Beziehung stellen wir sicher, dass der übergeordnete Controller Callbacks an seine untergeordneten Elemente weiterleitet, wenn er ein- oder ausgeblendet wird. Rotation Callbacks müssen ebenfalls weitergeleitet werden. Wenn sich die Ausrichtung ändert, müssen alle Ansichtssteuerungen auf dem Bildschirm wissen, dass sie ihren Inhalt entsprechend anpassen können.

Was bedeutet das alles in Bezug auf Code? View Controller haben eine NSArray Eigenschaft aufgerufen childViewControllers Zu unseren Aufgaben gehört auch das Hinzufügen und Entfernen von untergeordneten Ansichtscontrollern zu und aus diesem Array im übergeordneten Element, indem entsprechende Methoden aufgerufen werden. Diese Methoden umfassen addChildViewController (beim Elternteil angerufen) und removeFromParentViewController (das Kind angerufen), wenn wir versuchen, die Eltern-Kind-Beziehung herzustellen oder zu brechen. Zu Beginn und am Ende des Hinzufügungs- / Entfernungsvorgangs werden außerdem einige Benachrichtigungsnachrichten an den untergeordneten Ansichtscontroller gesendet. Diese sind willMoveToParentViewController: und didMoveToParentViewController:, mit dem entsprechenden übergeordneten Controller als Argument gesendet. Das Argument ist Null, wenn das Kind entfernt wird. Wie wir sehen werden, wird eine dieser Nachrichten automatisch für uns gesendet, während die andere in unserer Verantwortung liegt. Dies hängt davon ab, ob wir das Kind hinzufügen oder entfernen. Wir werden die genaue Reihenfolge in Kürze untersuchen, wenn wir Dinge im Code implementieren. Der untergeordnete Controller kann auf diese Benachrichtigungen reagieren, indem er die entsprechenden Methoden implementiert, wenn er zur Vorbereitung dieser Ereignisse etwas unternehmen muss.

Außerdem müssen wir die mit dem untergeordneten Ansichtscontroller verknüpften Ansichten zur Hierarchie der übergeordneten Hierarchie hinzufügen oder daraus entfernen. Dazu werden Methoden wie z addSubview: oder removeFromSuperview), einschließlich der Durchführung etwaiger begleitender Animationen. Es gibt eine bequeme Methode (-) transitionFromViewController: toViewController: Dauer: Optionen: Animationen: Abschluss: Auf diese Weise können wir den Prozess des Austauschs untergeordneter Ansichtscontroller mit Animationen auf dem Bildschirm optimieren. Wir werden die genauen Details prüfen, wenn wir den Code schreiben - der nächste!


1. Neues Projekt anlegen

Erstellen Sie eine neue iOS-App in Xcode basierend auf dem "Leere Anwendung"template. Machen Sie eine iOS-App mit aktiviertem ARC. Nennen Sie es VCContainmentTut.

Ein neues Projekt erstellen

2. Implementieren des Container View Controllers

Erstellen Sie eine neue Klasse namens RootController. Mache es ein UIViewController Unterklasse. Stellen Sie sicher, dass alle Kontrollkästchen deaktiviert sind. Dies ist unsere Container-View-Controller-Unterklasse.


Ersetzen Sie den Code in RootViewController.h mit den folgenden.

 #einführen  @Interface RootController: UIViewController // (1) - (id) initWithViewControllers: (NSArray *) viewControllers undMenuTitles: (NSArray *) Titel; // (2) @end

Unser Container-Controller verfügt über eine Tabellenansicht, die als unser Menü fungiert. Wenn Sie auf eine beliebige Zelle tippen, wird der derzeit sichtbare View-Controller durch den durch das Tippen des Benutzers ausgewählten ersetzt.

Verweise auf die Punkte im Code,

  1. Unser Root-Controller dient gleichzeitig als Delegat und Datenquelle des Menüs (d. H. Der Tabellenansicht).
  2. Wir bieten eine extrem einfache API (für unseren Container-Klassenbenutzer), die aus einem Initialisierer besteht, der eine Liste von Ansichtscontrollern enthält, die unser Stammcontroller enthalten soll, und eine Liste von Zeichenfolgen, die die Titel für jeden Ansichtscontroller im darstellen Speisekarte. So können wir uns auf die Grundlagen konzentrieren. Sobald Sie diese verstanden haben, können Sie die API beliebig anpassbar machen.

Lassen Sie uns einen Blick darauf werfen, wie unser Endprodukt aussehen wird, so dass Sie ein mentales Bild haben, mit dem Sie die Implementierung verknüpfen können.


Es wird hilfreich sein zu erkennen, dass unser Container-View-Controller einem Tab-View-Controller sehr ähnlich ist. Jeder Menüpunkt entspricht einem unabhängigen View-Controller. Der Unterschied zwischen unserenSchiebemenü"Controller und der Tab-Controller sind zum größten Teil visuell. Die Phrase"Schiebemenü"ist ein bisschen irreführend, da es sich tatsächlich um den Content View Controller handelt, der sich dahingehend verschiebt, dass das darunterliegende Menü ausgeblendet wird.

Ersetzen Sie bei der Implementierung den gesamten Code in RootController.m durch den folgenden Code.

 #define kExposedWidth 200.0 #define kMenuCellID @ "MenuCell" #import "RootController.h" @interface RootController () @property (nonatomic, strong) UITableView * -Menü; @ property (nonatomic, strong) NSArray * viewController; @ property (nonatomic, strong) NSArray * menuTitles; @ property (nichtatomisch, zuweisen) NSInteger indexOfVisibleController; @ property (nichtatomisch, zuweisen) BOOL isMenuVisible; @end @implementation RootController - (id) initWithViewControllers: (NSArray *) viewControllers undMenuTitles: (NSArray *) menuTitles if (self = [super init]) NSAssert (self.viewControllers.count == self.menuTitles.count, @ "Es muss nur einen Menütitel geben, der jedem Ansichtscontroller entspricht!"); // (1) NSMutableArray * tempVCs = [NSMutableArray arrayWithCapacity: viewControllers.count]; self.menuTitles = [menuTitles copy]; für (UIViewController * vc in viewControllers) // (2) if (! [vc isMemberOfClass: [UINavigationController-Klasse]])) [tempVCs addObject: [[UINavigationController-Zuordnung] initWithRootViewController: vc]];  else [tempVCs addObject: vc]; UIBarButtonItem * declMenuBarButtonItem = [[Zuweisung von UIBarButtonItem] initWithTitle: @ "Menu" Stil: UIBarButtonItemStylePlain Ziel: Eigenaktion: @selector (toggleMenuVisibility :)]; // (3) UIViewController * topVC = ((UINavigationController *) tempVCs.lastObject) .topViewController; topVC.navigationItem.leftBarButtonItems = [@ [offenbartMenuBarButtonItem] arrayByAddingObjectsFromArray: topVC.navigationItem.leftBarButtonItems];  self.viewControllers = [Kopie von TempVCs]; self.menu = [[UITableView Alloc] init]; // (4) self.menu.delegate = self; self.menu.dataSource = self;  return self;  - (void) viewDidLoad [super viewDidLoad]; [self.menu registerClass: [UITableViewCell-Klasse] forCellReuseIdentifier: kMenuCellID]; self.menu.frame = self.view.bounds; [self.view addSubview: self.menu]; self.indexOfVisibleController = 0; UIViewController * visibleViewController = self.viewControllers [0]; visibleViewController.view.frame = [self offScreenFrame]; [self addChildViewController: visibleViewController]; // (5) [self.view addSubview: visibleViewController.view]; // (6) self.isMenuVisible = YES; [self adjustContentFrameAccordingToMenuVisibility]; // (7) [self.viewControllers [0] didMoveToParentViewController: self]; // (8) - (void) toggleMenuVisibility: (id) Sender // (9) self.isMenuVisible =! Self.isMenuVisible; [self adjustContentFrameAccordingToMenuVisibility];  - (void) adjustContentFrameAccordingToMenuVisibility // (10) UIViewController * visibleViewController = self.viewControllers [self.indexOfVisibleController]; CGSize size = visibleViewController.view.frame.size; if (self.isMenuVisible) [UIView animateWithDuration: 0.5 Animationen: ^ visibleViewController.view.frame = CGRectMake (kExposedWidth, 0, size.width, size.height); ];  else [UIView animateWithDuration: 0.5 Animationen: ^ visibleViewController.view.frame = CGRectMake (0, 0, size.width, size.height); ];  - (void) replaceVisibleViewControllerWithViewControllerAtIndex: (NSInteger) index // (11) if (index == self.indexOfVisibleController) return; UIViewController * incomingViewController = self.viewControllers [Index]; incomingViewController.view.frame = [self offScreenFrame]; UIViewController * outgoingViewController = self.viewControllers [self.indexOfVisibleController]; CGRect visibleFrame = self.view.bounds; [outgoingViewController willMoveToParentViewController: nil]; // (12) [self addChildViewController: incomingViewController]; // (13) [[UIApplication sharedApplication] beginIgnoringInteractionEvents]; // (14) [self transitionFromViewController: outgoingViewController // (15) toViewController: incomingViewController Dauer: 0.5 Optionen: 0 Animationen: ^ outgoingViewController.view.frame = [self offScreenFrame];  completion: ^ (BOOL beendet) [UIView animateWithDuration: 0.5 Animationen: ^ [outgoingViewController.view removeFromSuperview]; [self.view addSubview: incomingViewController.view]; incomingViewController.view.frame = visibleFrame; [[UIApplication sharedApplication] endIgnoringInteractionEvents]; // (16)]; [incomingViewController didMoveToParentViewController: self]; // (17) [outgoingViewController removeFromParentViewController]; // (18) self.isMenuVisible = NO; self.indexOfVisibleController = Index; ];  // (19): - (NSInteger) numberOfSectionsInTableView: (UITableView *) tableView return 1;  - (NSInteger) tableView: (UITableView *) tableView numberOfRowsInSection: (NSInteger) Abschnitt return self.menuTitles.count;  - (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier: kMenuCellID]; cell.textLabel.text = self.menuTitles [indexPath.row]; zurück Zelle;  - (void) tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath [self replaceVisibleViewControllerWithViewControllerAtIndex: indexPath.row];  - (CGRect) offScreenFrame return CGRectMake (self.view.bounds.size.width, 0, self.view.bounds.size.width, self.view.bounds.size.height);  @Ende

Nun zur Erklärung des Codes. Die Teile, die ich hervorgehoben habe, sind besonders für die Implementierung von Containment relevant.

  1. Zunächst prüft der Initialisierer einfach, ob jedem View-Controller ein Menütitel zugewiesen wurde. Wir haben keine Typprüfung durchgeführt, um sicherzustellen, dass jedes der zwei an den Initialisierer übergebenen Arrays die richtigen Objekte enthält, UIViewController und NSString Typen. Sie könnten es in Betracht ziehen. Beachten Sie, dass wir für jeden dieser Arrays Arrays verwalten viewController, und menuTitles.
  2. Wir möchten, dass es eine Schaltfläche gibt, die beim Antippen das Menü anzeigt oder verbirgt. Meine einfache Lösung, wo die Schaltfläche sein sollte, bestand darin, jeden Ansichts-Controller, den wir vom Initialisierer erhalten hatten, in einen Navigations-Controller zu integrieren. Dies gibt uns eine kostenlose Navigationsleiste, der eine Schaltfläche hinzugefügt werden kann, es sei denn, der in View Controller übergebene Controller war bereits ein Navigationscontroller. In diesem Fall nehmen wir keine zusätzlichen Aktionen vor.
  3. Wir erstellen ein Bar-Button-Element, das das Erscheinungsbild des Menüs oder das Ausblenden des Menüs auslöst, indem der derzeit sichtbare View-Controller verschoben wird. Wir fügen es der Navigationsleiste hinzu, indem wir vorhandene Schaltflächen in der Navigationsleiste nach rechts verschieben. Wir machen dies, weil der hinzugefügte View-Controller bereits ein Navigations-Controller mit bereits vorhandenen Schaltflächen ist.
  4. Wir instantiieren unser Menü als Tabellensicht und weisen den Root-Controller selbst als Delegat und Datenquelle zu.
  5. Im viewDidLoad, Nach der Konfiguration und dem Hinzufügen der Menütabellenansicht zur Ansicht unseres Root-Controllers führen wir in unserer App den ersten View-Controller in der App ein viewController Array. Durch das Senden der addChildViewController: Nachricht an unseren Root-Controller, führen wir unsere erste Verantwortung für die Eindämmung aus. Sie sollten wissen, dass dies die Nachricht verursacht willMoveToParentViewController: auf dem untergeordneten Controller aufgerufen werden.
  6. Beachten Sie, dass wir die Ansicht unseres untergeordneten Controllers explizit zur Ansichtshierarchie des übergeordneten Elements hinzufügen müssen!
  7. Wir legen fest, dass das Menü anfänglich sichtbar ist, und rufen eine Methode auf, die den Frame des sichtbaren Inhalts-View-Controllers unter Berücksichtigung der Sichtbarkeit des Menüs anpasst. Wir werden uns in Kürze mit den Details dieser Methode befassen.
  8. Sobald die Ansicht des untergeordneten Ansichtscontrollers bequem in der Ansichtshierarchie der übergeordneten Ansicht liegt, Wir senden die didMoveToParentViewController-Nachricht an den hinzugefügten untergeordneten Controller mit selbst, die RootController-Instanz als Argument. In unserem untergeordneten Controller können wir diese Methode bei Bedarf implementieren.
  9. Eine einfache Methode in Verbindung mit der Aktion der Menüleistenschaltfläche, mit der die Sichtbarkeit des Menüs geändert wird, indem die Ansicht des überlagerten View-Controllers entsprechend angepasst wird.
  10. Wie der Name schon sagt, adjustContentFrameAccordingToMenuVisibility lässt uns den Rahmen des Inhaltsansicht-Controllers anpassen, um anzugeben, ob das Menü ausgeblendet ist oder nicht. Wenn ja, wird der Überblick überlagert. Ansonsten wird es nach rechts verschoben kExposedWidth. Ich habe das auf 200 Punkte gesetzt.
  11. Wieder wie durch den Namen deutlich angegeben, replaceVisibleViewControllerWithViewControllerAtIndex erlaubt uns, View-Controller und die entsprechenden Views aus der Hierarchie auszutauschen. Um unsere Animation abzurufen, die darin besteht, den ersetzten View-Controller nach rechts aus dem Bildschirm zu schieben und dann den Ersatz-Controller von derselben Stelle aus zu bringen, definieren wir einige rechteckige Rahmen.
  12. willMoveToParentViewController mit Null. Sobald dieser Schritt abgeschlossen ist, erhält dieser Ansichts-Controller keine Callbacks für Erscheinungsbild und Rotation mehr vom übergeordneten Element. Dies ist sinnvoll, da es nicht mehr aktiver Bestandteil der App ist.
  13. Wir fügen den eingehenden View-Controller als untergeordnetes Element zum Root-Controller hinzu, ähnlich wie wir es am Anfang gemacht haben.
  14. Wir ignorieren Ereignisse der Benutzerinteraktion, damit unser View-Controller reibungslos wechseln kann.
  15. Diese bequeme Methode ermöglicht es uns, das Entfernen des abgehenden Controllers und die Ankunft des eingehenden Controllers zu animieren, während die erforderliche Sequenz von Ereignissen ausgeführt wird, die am Hinzufügungs- und Entfernungsprozess des untergeordneten View-Controllers beteiligt sind. Wir animieren die Ansicht des ausgehenden VC, um den Bildschirm nach rechts zu verschieben, und nachdem die Animation abgeschlossen ist, entfernen wir sie aus der Ansichtshierarchie. Anschließend animieren wir den Incoming View Controller, um ihn von derselben Stelle außerhalb des Bildschirms einzuschieben und den Platz einzunehmen, der zuvor von der Sicht des ausgehenden Controllers eingenommen wurde.
  16. Wir ermöglichen unserer App, eingehende Interaktionsereignisse zu akzeptieren, da der View-Controller-Swap abgeschlossen ist.
  17. Wir benachrichtigen den Incoming View Controller, dass er in den Container Controller verschoben wurde, indem er ihn sendet didMoveToParentViewController Nachricht mit selbst als das Argument.
  18. Wir entfernen den ausgehenden Controller aus dem Containercontroller, indem wir ihn senden removeFromParentViewController Botschaft. Das solltest du wissen didMoveToParentViewController: mit Null als Argument wird für Sie geschickt.
  19. Wir implementieren die Delegate- und Datenquellenprotokollmethoden der Menütabellenansicht, die ziemlich unkompliziert sind. Dies beinhaltet das Auslösen des View-Controller-Swapping-Schrittes (11), wenn im Menü über das Symbol eines neuen Elements eine Zelle angetippt wird -tableView: didSelectRowAtIndexPath: Methode.

Die Reihenfolge der Anrufe, die sich auf die Controller-Eindämmung beziehen, ist möglicherweise etwas verwirrend. Es hilft zusammenzufassen.

    Beim Hinzufügen eines untergeordneten Ansichtscontrollers zu einem übergeordneten Element:
  • Anruf addChildViewController: auf dem Elternteil mit dem Kind als Argument. Dies verursacht die Nachricht willMoveToParentViewController: an das Kind mit dem Elternteil als Argument geschickt werden.
  • Fügen Sie die Ansicht des Kindes als Unteransicht der Ansicht des Elternteils hinzu.
  • Rufen Sie explizit an didMoveToParentViewController: auf dem Kind mit dem Elternteil als Argument.
    Wenn Sie einen untergeordneten Ansichtscontroller von seinem übergeordneten Element entfernen:
  • Anruf willMoveToParentViewController: auf das Kind mit Null als das Argument.
  • Entfernen Sie die Ansicht des Kindes aus seiner Übersicht.
  • Senden removeFromParentViewController dem Kind Die Ursache der Nachricht didMoveToParentViewController mit Null als das Argument, das in Ihrem Namen an das Kind gesendet werden soll.

3. Testen

Lassen Sie uns die verschiedenen Arten von View-Controllern testen, die unserem Root-Controller hinzugefügt wurden! Erstellen Sie eine neue Unterklasse von UIViewController namens ViewController, alle Optionen deaktiviert lassen.

Ersetzen Sie den Code in ViewController.m durch den folgenden Code.

 #import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void) willMoveToParentViewController: (UIViewController *) übergeordnetes NSLog (@ "% @ (% p) -% @", NSStringFromClass ([eigene Klasse]) ), self, NSStringFromSelector (_cmd));  - (void) didMoveToParentViewController: (UIViewController *) übergeordnetes NSLog (@ "% @ (% p) -% @"), NSStringFromClass ([self-Klasse]), self, NSStringFromSelector (_cmd));  - (void) viewWillAppear: (BOOL) animiert [super viewWillAppear: animated]; NSLog (@ "% @ (% p) -% @"), NSStringFromClass ([Selbstklasse]), self, NSStringFromSelector (_cmd));  - (void) viewDidAppear: (BOOL) animiert [super viewDidAppear: animiert]; NSLog (@ "% @ (% p) -% @"), NSStringFromClass ([Selbstklasse]), self, NSStringFromSelector (_cmd));  - (void) willRotateToInterfaceOrientation: (UIInterfaceOrientation) toInterfaceOrientation dauer: (NSTimeInterval) duration [super willRotateToInterfaceOrientation: toInterfaceOrientation duration: duration]; NSLog (@ "% @ (% p) -% @"), NSStringFromClass ([Selbstklasse]), self, NSStringFromSelector (_cmd));  - (void) didRotateFromInterfaceOrientation: (UIInterfaceOrientation) fromInterfaceOrientation [super didRotateFromInterfaceOrientation: fromInterfaceOrientation]; NSLog (@ "% @ (% p) -% @"), NSStringFromClass ([Selbstklasse]), self, NSStringFromSelector (_cmd));  @Ende

Unser View-Controller selbst hat nichts Besonderes, außer dass wir die verschiedenen Rückrufe außer Kraft gesetzt haben, sodass wir sie jederzeit protokollieren können ViewController Die Instanz wird zum Kind unseres Stammcontrollers, und es tritt ein Erscheinungsbild oder Rotationsereignis auf.

In allen vorherigen Code, _cmd bezieht sich auf den Selektor, der der Methode entspricht, in der sich unsere Ausführung befindet. NSStringFromSelector () wandelt es in einen String um. Dies ist eine schnelle und einfache Möglichkeit, den Namen der aktuellen Methode zu ermitteln, ohne sie manuell eingeben zu müssen.

Lassen Sie uns einen Navigationscontroller und einen Tabulator in den Mix einfließen lassen. Dieses Mal verwenden wir Storyboards.

Erstellen Sie eine neue Datei und unter iOS> Benutzeroberfläche, wählen Storyboard. Stellen Sie die Gerätefamilie auf iPhone, und nennen Sie es NavStoryBoard.


Von dem Objektbibliothek, Drag & Drop a Navigationssteuerung Objekt in die Leinwand. Ziehen Sie eine Bar-Button-Element in die linke Seite der Navigationsleiste in der Table View Controller bezeichnet als "Root View Controller". Diese enthält die Tabellenansicht in der Leinwand. Geben Sie einen beliebigen Namen. Ich habe es benannt."Links". Es dient dazu, den Code zu überprüfen, den wir geschrieben haben, damit die Schaltfläche zum Anzeigen / Verbergen der Menüleiste als ganz linke Schaltfläche in der Navigationsleiste angezeigt wird, indem alle bereits vorhandenen Schaltflächen nach rechts gedrückt werden Controller anzeigen Instanz und platzieren Sie es rechts neben dem Controller mit dem Titel "Root View Controller"in der Leinwand.

Klicken Sie, wo es heißt "Tabellenansicht"in der Mitte des zweiten Controllers und in der Attribute-Inspektor den Inhalt ändern von "Dynamischer Prototyp"bis"Statische Zellen".

Zelleninhaltstyp von dynamisch in statisch ändern

Dadurch werden im Interface-Builder drei statische Tabellensichtzellen angezeigt. Löschen Sie alle bis auf eine dieser Tabellensichtzellen und halten Sie sie gedrückt Steuerung, Klicken und ziehen Sie aus der verbleibenden Zelle in den Ansichtsregler ganz rechts und lassen Sie sie los. Wählen "drücken"unter Auswahl Segue. Wenn Sie in der Tabellenansicht auf die einzelne Zelle tippen, führt dies zu einem Übergang zum rechten Ansichtscontroller. Wenn Sie möchten, können Sie ein löschen UILabel auf die Tabellenzelle, um etwas Text zu geben. Ihr Storyboard sollte dem Foto unten ähnlich aussehen.

NavStoryBoard

Schließlich fügen wir einen Tab-Leisten-Controller hinzu. Erstellen Sie wie bereits zuvor eine Storyboard Datei und nennen Sie es TabStoryBoard. Ziehen Sie eine Tab-Leiste Controller Artikel aus der Objektbibliothek in die Leinwand. Es ist mit zwei Registerkarten vorkonfiguriert. Wenn Sie möchten, können Sie die Hintergrundfarbe der beiden Registerkarten-Ansichts-Controller ändern, indem Sie auf die Ansicht klicken, die den jeweiligen Ansichts-Controllern entspricht, und die Option "Hintergrund"Option in der Attribute-Inspektor. Auf diese Weise können Sie überprüfen, ob die Ansichts-Controller-Auswahl über die Registerkarte ordnungsgemäß funktioniert.

Ihr Storyboard sollte so aussehen.

4. Konfigurieren des App-Delegaten

Jetzt ist es Zeit, alles in der AppDelegate.

Ersetzen Sie den Code in AppDelegate.m durch den folgenden Code.

 #import "AppDelegate.h" #import "RootController.h" #import "ViewController.h" @implementation AppDelegate - (BOOL) Anwendung: (UIApplication *) Anwendung didFinishLaunchingWithOptions: (NSDictionary *) launchOptions self.window Alloc] initWithFrame: [[UIScreen-MainScreen] -Begrenzungen]]; UIStoryboard * tabStoryBoard = [UIStoryboard storyboardWithName: @ "TabStoryboard" -Bündel: nil]; UIStoryboard * navStoryBoard = [UIStoryboard storyboardWithName: @ "NavStoryboard" -Bündel: nil]; UINavigationController * navController = [navStoryBoard instantiateViewControllerWithIdentifier: @ "Nav Controller"]; UITabBarController * tabController = [tabStoryBoard instantiateViewControllerWithIdentifier: @ "Tab Controller"]; ViewController * redVC, * greenVC; redVC = [[ViewController-Zuordnung] init]; greenVC = [[ViewController Alloc] init]; redVC.view.backgroundColor = [UIColor redColor]; greenVC.view.backgroundColor = [UIColor greenColor]; RootController * menuController = [[RootController-Zuordnung] initWithViewControllers: @ [tabController, redVC, greenVC, navController] undMenuTitles: @ [@ "Tab", @ "Rot", @ "Grün", @ "Nav"]]; self.window.rootViewController = menuController; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; JA zurückgeben; 

Wir haben nur Instanzen erstellt ViewController und instantiieren Sie die Navigation und den Tab-Controller von den beiden Storyboards aus. Wir haben sie in einer Reihe an unsere weitergegeben RootControllerInstanz. Das ist der Containercontroller, den wir zu Beginn implementiert haben. Wir haben dies zusammen mit einem Array von Strings getan, um die Ansichts-Controller im Menü zu benennen. Jetzt legen wir einfach unsere initialisierte Root Controller-Instanz als Fenster fest rootViewController Eigentum.

App erstellen und ausführen Sie haben gerade Containercontainer implementiert! Tippen Sie auf die verschiedenen Tabellenzellen im Menü, um die sichtbare Folie durch die neue zu ersetzen, die von rechts eingeschoben wird. Beachten Sie, wie für die Navigations-Controller-Instanz (genannt "NavC"im Menü), die"Links"Die Schaltfläche hat sich um eine Stelle nach rechts verschoben, und die Menüleistenschaltfläche hat die Position ganz links eingenommen. Sie können die Ausrichtung in Querformat ändern und überprüfen, ob alles korrekt aussieht.

Simulator-Screenshots

Fazit

In diesem Einführungs-Tutorial haben wir untersucht, wie View Controller Containment in iOS 6 implementiert wird. Wir haben eine einfache Version einer benutzerdefinierten App-Oberfläche entwickelt, die sehr populär geworden ist und häufig in häufig verwendeten Apps wie Facebook und Path zu sehen ist. Unsere Implementierung war so einfach wie möglich, sodass wir sie leicht analysieren und die Grundlagen richtig stellen konnten. Es gibt viele ausgereifte Open-Source-Implementierungen dieses Controllertyps, die Sie herunterladen und studieren können. Es erscheint eine schnelle Google-Suche JasidePAnels und SWRevealViewController, unter anderen.

Hier sind einige Ideen, an denen Sie arbeiten können.

  • Machen Sie die Implementierung flexibler und die API anpassbarer.
  • Machen Sie die Schnittstelle schöner. Sie können die Darstellung der Tabellensymbolzelle anpassen oder der Ansicht des View-Controllers einen Schatten in das Menü werfen, um der Benutzeroberfläche etwas mehr Tiefe zu verleihen.
  • Passen Sie