Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Geleitwort
Vorwort
1 Hello iPhone
2 Die Reise nach iOS
3 Sehen und anfassen
4 Alles unter Kontrolle
5 Daten, Tabellen und Controller
6 Models, Layer, Animationen
7 Programmieren, aber sicher
8 Datenserialisierung und Internetzugriff
9 Multimedia
10 Jahrmarkt der Nützlichkeiten
Stichwort

Jetzt Buch bestellen
Ihre Meinung?

Spacer
Apps programmieren für iPhone und iPad von Klaus M. Rodewig, Clemens Wagner
Das umfassende Handbuch
Buch: Apps programmieren für iPhone und iPad

Apps programmieren für iPhone und iPad
Rheinwerk Computing
1172 S., geb., mit DVD
49,90 Euro, ISBN 978-3-8362-2734-6
Pfeil 4 Alles unter Kontrolle
Pfeil 4.1 Viewcontroller, XIBs und Storyboards
Pfeil 4.1.1 Erstellung von Viewcontrollern über XIB-Dateien
Pfeil 4.1.2 Applikationsinitialisierung
Pfeil 4.1.3 Storyboards
Pfeil 4.1.4 Modale Dialoge
Pfeil 4.1.5 Pop-over
Pfeil 4.1.6 Übergänge
Pfeil 4.2 Der Navigationcontroller
Pfeil 4.2.1 Viewcontroller anzeigen und entfernen
Pfeil 4.2.2 Die Navigationsleiste
Pfeil 4.2.3 Der Zurück-Button
Pfeil 4.2.4 Gehe drei Felder zurück
Pfeil 4.2.5 Die Werkzeugleiste
Pfeil 4.3 Navigation- und Pop-over-Controller in der Praxis
Pfeil 4.3.1 Navigationcontroller anlegen
Pfeil 4.3.2 Einen Dialog für die Einstellungen gestalten
Pfeil 4.3.3 Früher war alles besser ...
Pfeil 4.3.4 Einstellungen dauerhaft speichern
Pfeil 4.3.5 Storyboard lokalisieren
Pfeil 4.3.6 Anpassung an das iPad
Pfeil 4.4 Der Splitviewcontroller
Pfeil 4.4.1 Das Splitviewcontroller-Delegate
Pfeil 4.4.2 Eine Projektvariante erstellen
Pfeil 4.4.3 Rotationsverhalten für einzelne Viewcontroller ändern
Pfeil 4.4.4 Anlegen eines Splitviewcontrollers
Pfeil 4.4.5 Autolayout-Restriktionen per Programmcode erstellen
Pfeil 4.4.6 Anzeige des Masters
Pfeil 4.5 Der Tabbarcontroller
Pfeil 4.5.1 Aufbau einer Reiternavigation
Pfeil 4.5.2 Plaketten
Pfeil 4.5.3 Für ein paar Controller mehr
Pfeil 4.6 Der Page-Viewcontroller
Pfeil 4.6.1 Einen Page-Viewcontroller erzeugen
Pfeil 4.6.2 Die Datenquelle und das Delegate
Pfeil 4.7 Eigene Container- und Subviewcontroller
Pfeil 4.7.1 Container- und Subviewcontroller
Pfeil 4.7.2 Verwaltung der Subviewcontroller
Pfeil 4.7.3 ContainerViews leicht gemacht
Pfeil 4.8 Regelbasierte Anpassung der Oberfläche
Pfeil 4.8.1 Gestaltungsregeln ...
Pfeil 4.8.2 ... und Gestaltungsmöglichkeiten

Rheinwerk Computing - Zum Seitenanfang

4.4Der SplitviewcontrollerZur nächsten Überschrift

Das iPad bietet gegenüber dem iPhone eine circa fünfmal größere Anzeigefläche, die für die alleinige Darstellung einzelner Dialoge häufig zu groß ist. Beispielsweise ist die Fläche, die der Einstellungsdialog unserer Wecker-App benötigt, für den iPad-Bildschirm viel zu klein.

Das iPad bietet mit dem Splitviewcontroller die Möglichkeit, im Querformat die Views von zwei Viewcontrollern nebeneinander anzuzeigen. Da der linke Viewcontroller in der Regel den Inhalt des rechten festlegt, nennt Apple den linken Master- und den rechten Detailviewcontroller. Im Hochformat zeigt der Splitviewcontroller den Detailviewcontroller auf der kompletten Bildschirmfläche und den Masterviewcontroller optional in einem Pop-over an (siehe Abbildung 4.37), das seit iOS 5.1 immer die volle Höhe des Splitviewcontrollers besitzt. Damit das funktioniert, müssen beide Controller sowohl das Quer- als auch das Hochformat unterstützen. Für die Anzeige und die Steuerung des Pop-overs sollte der Detailviewcontroller außerdem eine Werkzeug- oder Navigationsleiste für den Button bieten. Fällt die Wahl auf eine Werkzeugleiste, gehört diese in der Regel nicht zu einem Navigationcontroller und kann auch am oberen Bildschirmrand liegen.

Abbildung

Abbildung 4.37 Splitviewcontroller im Hoch- und Querformat

Master-Detail

Die Aufteilung in Master-Detail finden Sie bei Apple häufiger. Dabei zeigt der Master eine Übersicht oder die Struktur der Daten an, während die Detailansicht ein einzelnes Datenobjekt darstellt. Ein typisches Beispiel ist die Mail-App auf dem iPad, wo der Master die Postfächer beziehungsweise die Mails auflistet und die Detailansicht den Inhalt einer Mail anzeigt.

Die Klasse UISplitViewController besitzt eine minimalistische Schnittstelle, die nur drei Propertys enthält. Über viewControllers können Sie die beiden enthaltenen Viewcontroller in einem Array abfragen beziehungsweise festlegen. Dabei ist der erste Controller im Array der Master- und der zweite der Detailviewcontroller. Über die zweite Property bestimmen Sie das Delegate des Splitviewcontrollers. Über die boolesche Property presentsWithGesture können Sie festlegen, ob sich das Pop-over im Portraitmodus über eine Wischgeste öffnen und schließen lässt.


Rheinwerk Computing - Zum Seitenanfang

4.4.1Das Splitviewcontroller-DelegateZur nächsten ÜberschriftZur vorigen Überschrift

Das Delegate bietet mehrere optionale Methoden an, die Sie über Anzeigeereignisse des Masterviewcontrollers informieren und mit denen Sie das Anzeigeverhalten des Masters steuern können. Ohne Delegate zeigt der Splitviewcontroller nur im Querformat den Master an und versteckt ihn im Hochformat. Wenn Sie ihn auch dauerhaft im Hochformat anzeigen möchten, können Sie die Delegate-Methode splitViewController:shouldHideViewController:inOrientation: implementieren. Wenn der Splitviewcontroller den Master in einer Ausrichtung anzeigen soll, sollte die Delegate-Methode für diese Ausrichtung NO zurückliefern. Beispielsweise erlaubt die Implementierung aus Listing 4.29 nur die Anzeige des Masters im Hochformat.

- (BOOL)splitViewController: 
(UISplitViewController *)inSplitViewController
shouldHideViewController:(UIViewController *)inController
inOrientation:(UIInterfaceOrientation)inOrientation {
return UIInterfaceOrientationIsLandscape(inOrientation);
}

Listing 4.29 Master nur im Hochformat anzeigen

Alternativ erlaubt der Splitviewcontroller auch dem Nutzer, die Anzeige des Masters zu steuern, wenn die Methode splitViewController:shouldHideViewController:inOrientation: für die entsprechende Geräteausrichtung YES liefert. In diesem Fall blendet der Splitviewcontroller den Master nicht neben, sondern über dem Detailcontroller ein, so dass der Master einen Teil der linken Seite des Detailcontrollers verdeckt.

Für die Steuerung stellt der Splitviewcontroller einen Pop-over-Controller und einen Button bereit. Diese beiden Objekte übergibt er an die Delegate-Methode splitViewController:willHideViewController:withBarButtonItem:forPopoverController:, wenn die App den Splitviewcontroller mit verstecktem Master anzeigt oder bevor der Splitviewcontroller den Master versteckt. Das Delegate sollte den Button in einer Navigations- oder Werkzeugleiste des Detailcontrollers anzeigen. Eine typische Implementierung dieser Methode zeigt Listing 4.30, die außerdem den Titel des Buttons setzt; das ist insbesondere unter iOS 7 wichtig, da ansonsten der Button nicht sichtbar ist.

- (void)splitViewController: 
(UISplitViewController *)inSplitController
willHideViewController:(UIViewController *)inController
withBarButtonItem:(UIBarButtonItem *)inBarButtonItem
forPopoverController:(UIPopoverController *)inPopover {
inBarButtonItem.title =
NSLocalizedString(@"Master", @"Master");
[self.navigationItem setLeftBarButtonItem:inBarButtonItem
animated:YES];
}

Listing 4.30 Verwaltung des Buttons und des Pop-over-Controllers

Durch diese Implementierung stellt die Navigationsleiste des Detail-Views auf der linken Seite einen Button dar, über den Sie den Masterviewcontroller anzeigen lassen können. Bevor der Splitviewcontroller den Master im Pop-over anzeigt, ruft er noch die Delegate-Methode splitViewController:popoverController:willPresentViewController: auf.

Wenn der Splitviewcontroller den Master durch eine Rotation links neben dem Detail-View anzeigt, müssen Sie den Button aus der Navigationsleiste wieder entfernen. Das sollte am besten in der Delegate-Methode splitViewController:willShowViewController:invalidatingBarButtonItem: geschehen.

- (void)splitViewController: 
(UISplitViewController*)inSplitViewController
willShowViewController:(UIViewController *)inController
invalidatingBarButtonItem:(UIBarButtonItem *)inButton {
[self.navigationItem setLeftBarButtonItem:nil
animated:YES];
}

Listing 4.31 Button aus Navigationsleiste wieder entfernen


Rheinwerk Computing - Zum Seitenanfang

4.4.2Eine Projektvariante erstellenZur nächsten ÜberschriftZur vorigen Überschrift

Die gute alte Wecker-App soll natürlich auch nicht vom Splitviewcontroller verschont bleiben. Diese Variante realisieren wir über ein eigenes Target im Projekt. Auch hier sollten Sie zunächst die App für eine Sprache anpassen und danach die anderen Sprachen nachziehen.

Zunächst legen Sie das neue Target für die App an, indem Sie das bestehende Target kopieren. Dazu wählen Sie im Kontextmenü des Targets AlarmClock den Punkt Duplicate aus (siehe Abbildung 4.38).

Abbildung

Abbildung 4.38 Duplizieren des Targets

Xcode legt dadurch ein neues Target mit dem Namen AlarmClock copy an. Klicken Sie diesen Namen mit der Maus einmal an, um ihn in »SplitViewAlarmClock« zu ändern. Allerdings verwendet Xcode den alten Namen noch an einigen anderen Stellen. Wenn Sie den Reiter Build Settings in den Target-Einstellungen öffnen und in das Suchfeld den Text »AlarmClock copy« eingeben, findet Xcode noch mehrere passende Zeilen dazu, von denen einige Zeilen eine fette Schrift verwenden (siehe Abbildung 4.39).

Abbildung

Abbildung 4.39 Target-Einstellungen mit dem Namen des Targets

Diese Einstellungen überschreiben die Standardwerte, und es reicht aus, wenn Sie diese Werte ändern. Dazu führen Sie einen einfachen Klick auf den entsprechenden Wert aus, und geben in das Feld den neuen Wert ein. Für die Zeile Product Name verwenden Sie den Wert »$(TARGET_NAME)«, und für Info.plist File verwenden Sie »$(PRODUCT_NAME)-Info.plist«. Durch diese Änderung erhält die App den Namen des Targets, und der Name der Info.plist-Datei basiert auf dem Namen der App. Wenn Sie jetzt also das Target umbenennen, ändert sich auch automatisch der Name der App. Durch die Umbenennungen verschwinden nicht nur die Zeilen in fetter Schrift, sondern auch die anderen Zeilen, so dass letztendlich der Bereich unter dem Suchfeld leer bleibt. Xcode verwendet ja für diese Einstellungen jetzt den Namen des Targets, also »SplitViewAlarmClock«, und Sie können das überprüfen, indem Sie diesen Text in das Suchfeld eintragen.

Die Info.plist-Datei speichert einige wichtige Parameter der App, wie beispielsweise die unterstützten Geräteausrichtungen, notwendige Gerätevoraussetzungen, den Namen der Haup-Storyboard-Datei. Jedes Target besitzt eine solche Datei, und Xcode hat beim Duplizieren des Targets auch eine Kopie mit dem Namen AlarmClock copy-Info.plist im Projekt angelegt. Da Sie soeben jedoch den Namen in den Target-Einstellungen geändert haben, sollten Sie diesen Dateinamen durch Anklicken und Eingabe des Textes »SplitViewAlarmClock-Info.plist« anpassen. Außerdem sollten Sie die Datei in die Gruppe Supporting Files verschieben.

Als Nächstes legen Sie ein neues, leeres iPad-Storyboard unter dem Namen SplitView.storyboard an. Da dieses Storyboard nur das neue Target benutzt, wählen Sie in der Dateiauswahl auch nur das Target SplitViewAlarmClock aus. Speichern Sie es außerdem im Ordner Base.lproj ab, damit Xcode es als Base-Lokalisierung verwendet (siehe Abbildung 4.40).

Abbildung

Abbildung 4.40 Anlegen des neuen Storyboards

Damit das neue Target auch dieses Storyboard verwendet, öffnen Sie den Reiter Info in den Einstellungen des Targets und wählen zuerst unter Devices den Wert iPad aus, da das neue Target nur diesen Gerätetyp unterstützt. Unter Main Interface wählen Sie den Eintrag SplitView.storyboard aus, damit die App auch das richtige Storyboard verwendet. Da diese Variante alle Geräteausrichtungen unterstützen soll, selektieren Sie alle Checkboxen unter Device Orientation. Die beschriebenen Änderungen sehen Sie in Abbildung 4.41.

Abbildung

Abbildung 4.41 Neue Target-Einstellungen


Rheinwerk Computing - Zum Seitenanfang

4.4.3Rotationsverhalten für einzelne Viewcontroller ändernZur nächsten ÜberschriftZur vorigen Überschrift

Mit den Target-Einstellungen legen Sie das Rotationsverhalten für die gesamte Applikation fest. Bis iOS 5 können Sie über die Methode shouldAutorotateToInterfaceOrientation: festlegen, welche Viewausrichtungen ein Viewcontroller unterstützt. Wenn Sie beispielsweise nur das Hochformat mit untenliegendem Home-Button unterstützen möchten, können Sie die Methode folgendermaßen implementieren:

- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)inOrientation {
return inOrientation == UIInterfaceOrientationPortrait;
}

Listing 4.32 Unterstützte Viewausrichtungen dynamisch festlegen

Die Methode liefert YES, wenn der View des Controllers die angegebene Ausrichtung unterstützt, und ansonsten NO. Um das Ergebnis zu ermitteln, gibt es zum einen für jede Ausrichtung jeweils eine Konstante UIInterfaceOrientationPortrait, -PortraitUpsideDown, -LandscapeLeft und -LandscapeRight. Außerdem gibt es die beiden Makros UIInterfaceOrientationIsPortrait() und UIInterfaceOrientationIsLandscape(), mit denen Sie einen Wert auf Hoch- beziehungsweise Querformat prüfen.

»shouldAutorotateToInterfaceOrientation:« und iOS 6

Apple hat die Methode shouldAutorotateToInterfaceOrientation: ab iOS 6 als veraltet markiert. Sie dürfen sie aber trotzdem weiterhin verwenden. Außerdem hat Apple ihr Verhalten geändert: Vor iOS 6 ruft Cocoa Touch diese Methode gegebenenfalls mehrmals bei jeder Gerätedrehung auf, um die unterstützten Ausrichtungen zu erfahren. Ab iOS 6 fragt es hingegen jede Ausrichtung nur einmal ab und merkt sich die unterstützten Ausrichtungen. Über diese Methode können Sie also ab iOS 6 die unterstützten Viewausrichtungen zur Laufzeit nur einmal festlegen.

Ab iOS 6 gibt es neue Methoden, um die unterstützten Viewausrichtungen festzulegen. Entweder stellen Sie die Ausrichtung für jeden Viewcontroller über die Methoden shouldAutorotate und supportedInterfaceOrientations getrennt ein, oder Sie wählen den zentralen Weg über application:supportedInterfaceOrientationsForWindow: des Application-Delegates. Die Methode shouldAutorotate legt dabei fest, ob sich der View des Controllers an die Geräteausrichtung anpassen soll. Wenn diese Methode NO liefert, dreht sich der View also nicht mit.

Die Methoden supportedInterfaceOrientations und application:supportedInterfaceOrientationsForWindow: liefern dabei jeweils eine Bitmaske mit den unterstützten Viewausrichtungen, für die es jeweils eine Konstante gibt. Sie können mehrere Ausrichtungen in den Rückgabewert über den Operator für das bitweise Oder | kodieren; beispielsweise lassen sich beide Hochformate durch

UIInterfaceOrientationMaskPortrait | 
UIInterfaceOrientationMaskPortraitUpsideDown

ausdrücken. Außerdem gibt es die Konstante UIInterfaceOrientationMaskAll, wenn alle Ausrichtungen unterstützt werden.

Die Navigationsleiste über dem Ziffernblatt zeigt allerdings immer noch den Button zum Öffnen der Einstellungen an, der nach wie vor funktioniert. Bei einem Splitviewcontroller ist er jedoch unnötig. Sie können den Button zwar im Storyboard löschen, aber dann funktioniert die Variante mit dem Navigationcontroller nicht mehr einwandfrei. Alternativ lässt sich der Button auch über den Programmcode ausschalten, indem Sie ihn aus dem Navigation-Item des Controllers löschen. In Listing 4.33 sehen Sie den entsprechenden Code, den Sie in die Methode viewDidLoad des Alarmclock-Viewcontrollers einfügen.

if(self.splitViewController != nil) {
self.NavigationItem.rightBarButtonItem = nil;
}

Listing 4.33 Entfernen Sie den Button bei Verwendung eines Splitviews.

Damit der Nutzer den Einstellungsdialog auch im Hochformat sehen kann, sollten Sie nun noch die Delegate-Methoden des Splitviewcontrollers implementieren. Als Delegate verwenden Sie dabei den Alarmclock-Viewcontroller. Damit er auch alle Delegateaufrufe des Splitviewcontrollers mitbekommt, erfolgt die Zuordnung in seiner awakeFromNib-Methode:

- (void)awakeFromNib {
[super awakeFromNib];
self.splitViewController.delegate = self;
}

Listing 4.34 Alarmviewcontroller als Splitview-Controller-Delegate festlegen


Rheinwerk Computing - Zum Seitenanfang

4.4.4Anlegen eines SplitviewcontrollersZur nächsten ÜberschriftZur vorigen Überschrift

In der Storyboard-Datei SplitView.storyboard legen Sie einen Splitviewcontroller an, indem Sie das entsprechende Symbol aus der Objektbibliothek auf die Arbeitsfläche ziehen. Der Interface Builder bestimmt diesen Controller automatisch zum initialen Viewcontroller des Storyboards. Außerdem legt er drei weitere Viewcontroller an, von denen Sie allerdings nur den Navigationcontroller des Masters benötigen; den Tableviewcontroller des Masters und den Detailviewcontroller können Sie getrost löschen. Danach kopieren Sie alle Viewcontroller aus dem Storyboard AlarmClock.storyboard in das Storyboard des Splitviewcontrollers. Das geht wie gewohnt über Copy & Paste.

Um diese Viewcontroller einzubinden, müssen Sie zwei neue Verbindungen anlegen:

  1. Den kopierten Navigationcontroller, der den Alarmclock-Viewcontroller als Rootviewcontroller enthält, legen Sie als neuen Detailviewcontroller des Splitviewcontrollers fest.
  2. Den Viewcontroller für die Einstellungen verwenden Sie als Rootviewcontroller des Navigationcontrollers des Masters.

Löschen Sie den Button für die Einstellungen aus der Navigationsleiste des Alarmclock-Viewcontrollers. Dadurch löschen Sie auch den entsprechenden Übergang zum Viewcontroller für die Einstellungen. Im Attributinspektor des Preferencesviewcontrollers wählen Sie unter Size den Eintrag Inferred aus und löschen den Haken aus der Checkbox Use Explicit Size.

Das fertige Storyboard sehen Sie in Abbildung 4.42.

Sie können diese App nun testen, indem Sie zunächst in der Werkzeugleiste von Xcode das Schema AlarmClock copy auswählen (siehe Abbildung 4.43) und die App dann wie gewohnt starten.

Abbildung

Abbildung 4.42 Das Storyboard für den Splitviewcontroller

Abbildung

Abbildung 4.43 Auswahl eines Schemas

Sie können übrigens den Namen des Schemas an den Namen des Targets anpassen, indem Sie in der Schemenauswahl den Menüpunkt Manage Schemes... auswählen. In der Schemenübersicht können Sie den Namen durch einfaches Anklicken ändern, wie das Abbildung 4.44 zeigt.

Abbildung

Abbildung 4.44 Ändern des Schemanamens

Die geänderte Applikation zeigt den Einstellungsdialog und die Zeitanzeige nun in einem Splitview an, wenn Sie sie auf dem iPad oder im Simulator ausführen, und Sie bekommen den Masterviewcontroller nur im Querformat zu sehen. Allerdings verkleinert Cocoa Touch den Detail-View und schneidet dadurch den rechten und unteren Rand des Zifferblatts ab. Sie können natürlich das Zifferblatt so weit verkleinern, dass es in den Detail-View komplett hineinpasst, allerdings füllt es dann im Hochformat nicht mehr die komplette Breite des Views aus.


Rheinwerk Computing - Zum Seitenanfang

4.4.5Autolayout-Restriktionen per Programmcode erstellenZur nächsten ÜberschriftZur vorigen Überschrift

Die Größe des Zifferblatts sollte also am besten von der Größe des Detail-Views abhängen, wobei das Layout folgende Regeln beachten muss:

  1. Die Breite und die Höhe des Zifferblatts sollten jeweils nicht größer als die Breite beziehungsweise Höhe des Detail-Views sein.
  2. Das Zifferblatt ist quadratisch; seine Breite und Höhe sind also gleich.

Diese Bedingungen lassen sich sehr gut über Restriktionen umsetzen, wobei sich die Bedingung des quadratischen Zifferblattes nicht über den Interface Builder, sondern nur über Programmcode umsetzen lässt. Dazu legen Sie Objekte der Klasse NSLayoutConstraint an und fügen sie über die Methoden addConstraint: oder addConstraints: zu einem View hinzu.

Cocoa Touch stellt Restriktionen durch eine Gleichung der Form

Attribut1 = Faktor × Attribut2 + Konstante

oder eine Ungleichung der Form

Attribut1 ≤ Faktor × Attribut2 + Konstante

beziehungsweise

Attribut1 ≥ Faktor × Attribut2 + Konstante

dar. Dabei sind Attribut1 und Attribut2 jeweils Eigenschaften der beteiligten Views und Faktor und Konstante frei wählbare, feste Werte. Beispielsweise können Sie die Restriktion, dass zwei Buttons die gleiche Breite haben sollen, durch

Breite des OK-Buttons = 1 × Breite des Abbrechen-Buttons + 0

ausdrücken. Diese Formel lässt sich über den Convenience-Konstruktor constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant: in die entsprechende Restriktion umsetzen:

NSLayoutConstraint *theConstraint = 
[NSLayoutConstraint constraintWithItem:theOkButton
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:theCancelButton
attribute:NSLayoutAttributeWidth
multiplier:1.0 constant:0.0];

theOkButton.translatesAutoresizingMaskIntoConstraints = NO;
theCancelButton.translatesAutoresizingMaskIntoConstraints = NO;
[theView addConstraint:theConstraint];

Listing 4.35 Erzeugen und Einfügen einer Restriktion

Die beiden ersten Parameter des Konstruktoraufrufs in Listing 4.35 beschreiben das Breitenattribut des OK-Buttons, und der vierte und fünfte Parameter das Breitenattribut des Abbrechen-Buttons. Die Konstante NSLayoutRelationEqual im releatedBy-Parameter legt fest, dass es sich bei der Restriktion um eine Gleichung handelt, und die beiden letzten Parameter enthalten den Faktor und die Konstante für die Gleichung. Wie Sie in Listing 4.35 sehen, geben Sie für die Attribute jeweils eine Konstante und nicht etwa den Namen des Attributs an. Die möglichen Werte für diese Attribute finden Sie in Tabelle 4.5.

Tabelle 4.5 Verfügbare Attribute für Restriktionen

Konstanten Beschreibung

NSLayoutAttributeLeft, NSLayoutAttributeRight, NSLayoutAttributeTop und NSLayoutAttributeBottom

Verweist auf den jeweiligen Rand des Views.

NSLayoutAttributeLeading

Der Rand, der in der Leserichtung der Interface-Sprache dem Text vorangeht. Bei Deutsch ist das der linke Rand.

NSLayoutAttributeTrailing

Der Rand, der in der Leserichtung der Interface-Sprache dem Text folgt. Bei Deutsch ist das der rechte Rand.

NSLayoutAttributeWidth und NSLayoutAttributeHeight

Die Breite und die Höhe des Views.

NSLayoutAttributeCenterX und NSLayoutAttributeCenterY

Bezeichnet jeweils die Mittelpunkte des Views.

NSLayoutAttributeBaseline

Die Grundlinie des Textes im View

NSLayoutAttributeNotAnAttribute

Wird für den zweiten View verwendet, wenn die Restriktion auf einem View basiert.

Analog können Sie damit die Restriktion für das quadratische Zifferblatt umsetzen, indem Sie auf der rechten und linken Seite der Restriktionsgleichung die Breite und die Höhe des Zifferblatts verwenden. Die Methode viewDidLoad fügt die entsprechende Restriktion zum Clockview hinzu und gibt ihr die höchste Priorität.

- (void)viewDidLoad {
[super viewDidLoad];
ClockView *theView = self.clockView;
NSLayoutConstraint *theConstraint =
[NSLayoutConstraint constraintWithItem:theView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual toItem:theView
attribute:NSLayoutAttributeWidth
multiplier:1.0 constant:0.0];

theConstraint.priority = UILayoutPriorityRequired;
[theView addConstraint:theConstraint];
}

Listing 4.36 Restriktion per Programmcode erzeugen

Automatisch erzeugte Restriktionen

Wenn Sie einen View in Ihrem Programmcode erzeugen, erstellt er automatisch Restriktionen für die Autoresizingmask, was zu Konflikten mit den von Ihnen erstellten Restriktionen führen kann. Sie können dieses Verhalten über den Setter setTranslatesAutoresizingMaskIntoConstraints: abschalten und über den Getter translatesAutoresizingMaskIntoConstraints abfragen.

Für alle Views aus einem Storyboard oder NIB erzeugt Xcode automatisch zur Übersetzungszeit Restriktionen, wenn Sie dafür keine Restriktionen im Interface Builder angelegt haben. Diese Standardrestriktionen legen jeweils einen festen linken und oberen Abstand zum Rand des Superviews und eine feste Größe des Views fest.

Wenn Sie mehrere Restriktionen für mehrere Views in einem gemeinsamen Superview anlegen möchten, ist das über Aufrufe des Convenience-Konstruktors wie in Listing 4.35 und Listing 4.36 schnell sehr mühselig. Über die Klassenmethode constraintsWithVisualFormat:options:metrics:views: lassen sich relativ bequem mehrere Restriktionen für eine Dimension auf einmal erzeugen. Die Beschreibung der Restriktionen erfolgt dabei über ein Format, dessen Aufbau die Visual Format Language beschreibt. In Tabelle 4.6 sehen Sie einige Beispiele für die Bestandteile eines Formats.

Tabelle 4.6 Bestandteile für Formate

Bestandteil Beschreibung

H:; V:

Legt die Ausrichtung fest (H: = horizontal, V: = vertikal); steht immer am Anfang des Formats. Die Ausrichtung ist horizontal, wenn diese Angabe fehlt.

|

Rand zum Superview; kann am Anfang hinter der Ausrichtung oder am Ende des Formats stehen.

-; –20-; -leftMargin-; -(>=20,<=50)-

Abstände: Standardabstand; einfacher, fester Abstand; einfacher, fester Abstand mit einer Metrik; Abstand mit Mindest- und Höchstwert

[okButton]

Verweis auf einen View

[okButton(>=40,<=50)]; [okButton(==cancelButton)]

Verweis auf einen View mit Beschreibung der Abmessung: Mindest- und Maximalabmessung; Abmessung von okButton ist gleich der von cancelButton.

-(20@750)-; [okButton(80@250)]

Abmessungsangaben mit Priorität 250

Das Autolayout richtet die Views in diesem Beispiel also alle in einer Zeile aus; allerdings macht das Format keine Angabe zu der vertikalen Ausrichtung der Views. Die können Sie jedoch über den Parameter options der Methode constraintsWithVisualFormat:options:metrics:views: für alle Views einheitlich festlegen. Dazu wählen Sie eine Konstante des Typs NSLayoutFormatOptions aus. Bei einer horizontalen Ausrichtung dürfen Sie allerdings nur eine Konstante mit einer vertikalen Angabe (also NSLayoutFormatAlignAllTop, NSLayoutFormatAlignAllBottom, NSLayoutFormatAlignAllCenterY oder NSLayoutFormatAlignAllBaseline) auswählen; die Klassenmethode erzeugt damit entsprechende Restriktionen, die die Views entlang der gewählten Linie ausrichten. Zusätzlich können Sie bei horizontalen Formaten die Richtung über eine der Konstanten NSLayoutFormatDirectionLeftToRight oder NSLayoutFormatDirectionRightToLeft festlegen. Wenn Sie diese Angabe weglassen, entspricht die Richtung immer der Leserichtung der aktuellen Spracheinstellung.

In den beiden letzten Parametern der Klassenmethode übergeben Sie jeweils ein Dictionary mit den Views und den Werten für die Metriken. Das Beispielprojekt ExtendedAlarmClock legt in der Methode viewDidLoad über die Klassenmethode außerdem die anderen Restriktionen für den Clockview fest, was sich zwar auch über den Interface Builder realisieren ließe, hier jedoch zur Demonstration der Klassenmethode dient.

Für Views in Storyboards oder NIBs ohne Restriktionen legt Xcode beim Erzeugen der App Standardrestriktionen an. Diese Restriktionen kollidieren in der Regel jedoch mit den Restriktionen, die Sie in Ihrem Programmcode anlegen. Sie sollten deshalb bei Views ohne Restriktionen zuerst die Standardrestriktionen über die Methode removeConstraints: löschen. Diese Standardrestriktionen liegen im Superview des Views, für den Sie Restriktionen anlegen wollen. Die Schritte zum Löschen sehen also so aus:

UIView *theSuperView = theView.superview;
[theSuperView removeConstraints:theSuperView.constraints];

Listing 4.37 Standardrestriktionen entfernen

Da das Beispielprojekt jedoch auch Restriktionen für das Label setzt, darf die App diese Restriktionen nicht löschen. Stattdessen löscht sie nur die Restriktionen des Clockviews. Dazu verwendet sie die Kategorie UIView(LayoutConstraints), deren Methoden Sie in Listing 4.38 sehen:

- (NSArray *)constraintsForView:(UIView *)inView {
NSPredicate *thePredicate =
[NSPredicate predicateWithFormat:
@"firstItem == %@ OR secondItem == %@",
inView, inView];

return [self.constraints
filteredArrayUsingPredicate:thePredicate];
}

- (void)removeConstraintsForView:(UIView *)inView {
NSArray *theConstraints =
[self constraintsForView:inView];

[self removeConstraints:theConstraints];
}

Listing 4.38 Restriktionen zu einem View finden

Die geänderten Zeilen gegenüber Listing 4.36 stellt Listing 4.38 hervorgehoben dar:

- (void)viewDidLoad {
[super viewDidLoad];
ClockView *theView = self.clockView;
NSLayoutConstraint *theConstraint =
[NSLayoutConstraint constraintWithItem:theView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual toItem:theView
attribute:NSLayoutAttributeWidth
multiplier:1.0 constant:0.0];

theConstraint.priority = UILayoutPriorityRequired;
theView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view removeConstraintsForView:theView];
[self.view addConstraint:theConstraint];
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:
@"H:|[clock(>=100)]-(>=0@750)-|"
options:0 metrics:nil views:@{ @"clock" : theView }]];
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:
@"V:|[clock(>=100)]-(>=0@750)-|"
options:0 metrics:nil views:@{ @"clock" : theView }]];
}

Listing 4.39 Restriktion per Programmcode erzeugen

Die neuen Restriktionen verwenden für die Breite und die Höhe den Mindestwert 100. Dadurch lässt sich vermeiden, dass das Zifferblatt bei der ersten Anzeige die Größe 0 × 0 hat. Die Bedingungen >=0 für den Abstand zum rechten beziehungsweise unteren Rand verhindern, dass der Clockview größer als sein Superview, der Detail-View, wird. In diesem Fall wäre der Abstand zu diesen Rändern nämlich negativ.

Restriktionen und Storyboards

Das Anlegen von Restriktionen im Programmcode ist aufwendiger als über den Interface Builder. Es spricht auch nichts dagegen, die Restriktionen aus dem Storyboard mit Restriktionen zu mischen, die Sie per Programmcode erzeugt haben. In der Praxis ist es in der Regel wesentlich einfacher, alle Restriktionen, die Sie über das Storyboard definieren können, auch dort anzusiedeln.

Insofern dient Listing 4.39 nur als Beispiel für die Klassenmethode constraintsWithVisualFormat:options:metrics:views:; eine Definition dieser Restriktionen über das Storyboard sollten Sie hier zu bevorzugen. Das gilt natürlich nicht für die Restriktion, die eine quadratische Fläche des Zifferblatts festlegt, da sich diese nur per Code anlegen lässt.

Anstatt die Restriktionen des Clockviews nach dem Laden des Views zu entfernen, bietet Xcode noch eine Alternative an. Sie können auch Restriktionen anlegen, die Xcode nicht mit in das Storyboard der App übernimmt. Damit lassen sich außerdem Warnungen für das Storyboard vermeiden. Wenn Xcode fehlende oder fehlerhafte Restriktionen in der View-Hierarchie einer Viewcontroller-Szene erkennt, zeigt es ein rotes Symbol neben dem Namen der Szene an, wie in Abbildung 4.45 rechts neben Alarm Clock Scene.

Abbildung

Abbildung 4.45 Viewcontroller-Szene mit ungültigen Restriktionen

Anklicken des Symbols öffnet eine detaillierte Beschreibung der Fehler. Dabei besitzt jeder Fehler ein Symbol bestehend aus einem weißen Punkt in einem roten Kreis. Wenn Sie dieses Symbol anklicken, schlägt Xcode vor, den Fehler zu beseitigen (siehe Abbildung 4.46).

Abbildung

Abbildung 4.46 Fehlerbeschreibungen für fehlende Restriktionen

Sie können auf diesem Weg also relativ einfach Fehler in den Restriktionen beheben; ob diese Behebung allerdings sinnvoll ist und Ihrer Intention entspricht, sei dahingestellt. Im Beispielprojekt können diese Restriktionen zumindest keinen Schaden anrichten, da ja die Methode viewDidLoad aus Listing 4.39 alle Restriktionen des Clockviews entfernt.

Falls Sie jedoch Restriktionen aus dem Storyboard mit Restriktionen aus dem Programmcode kombinieren möchten, empfiehlt sich die Aktivierung der Checkbox Remove at build time im Attributinspektor der nicht benötigten Restriktionen (siehe Abbildung 4.47). Dadurch veranlassen Sie Xcode, die entsprechende Restriktion nicht in das Storyboard der App zu übernehmen; der Interface Builder beachtet sie jedoch, wie alle anderen Restriktionen auch.

Abbildung

Abbildung 4.47 Restriktionen zur Übersetzungszeit entfernen


Rheinwerk Computing - Zum Seitenanfang

4.4.6Anzeige des MastersZur vorigen Überschrift

Um die Anzeige des Splitview-Masters steuern zu können, müssen Sie das Delegate des Splitviewcontrollers implementieren. Dabei stellt sich zunächst die Frage, welches Objekt diese ehrenvolle Aufgabe übernehmen darf. Zunächst sollte es zwei Bedingungen erfüllen:

  1. Es sollte in der Controller-Schicht liegen, da die Verwendung von Modellobjekten oder Views gegen das MVC-Muster verstieße.
  2. Seine Lebenszeit muss die Lebenszeit des Splitviews überdecken; d. h., es muss existieren, wenn der Splitview existiert.

Mit diesen Bedingungen lässt sich die Auswahl recht stark einschränken, da das Beispielprojekt nur über drei eigene Klassen in der Controller-Schicht verfügt. Das Application-Delegate und der Alarmclock-Viewcontroller erfüllen auf jeden Fall auch die zweite Bedingung und eignen sich deshalb als Delegate für den Splitviewcontroller. Da das Delegate nur den Button in die Navigationsleiste des Detail-Views einfügen beziehungsweise daraus entfernen soll, ist der Alarmclock-Viewcontroller hier der bessere Kandidat für diese Aufgabe.

Mit diesen Änderungen sollte die Applikation auf einem iPad immer im Hochformat eine Werkzeugleiste anzeigen und im Querformat nicht. Die Implementierungen der beiden Delegate-Methoden fallen dagegen sehr kurz aus; sie übergeben den Button an die Toolbar-Items beziehungsweise entfernen ihn daraus. Außerdem legen sie einen Titel für den Button fest.

- (void)splitViewController: 
(UISplitViewController *)inSplitViewController
willHideViewController:(UIViewController *)inController
withBarButtonItem:(UIBarButtonItem *)inItem
forPopoverController:(UIPopoverController *)inPopover {
inItem.title = NSLocalizedString(@"Preferences", @"");
self.navigationItem.leftBarButtonItem = inItem;
}

- (void)splitViewController:
(UISplitViewController *)inSplitViewController
willShowViewController: (UIViewController *)inController
invalidatingBarButtonItem:(UIBarButtonItem *)inItem {
self.navigationItem.leftBarButtonItem = nil;
}

Listing 4.40 Aktualisierung des Buttons in der Navigationsleiste

Die App muss jetzt nur noch das Delegate des Splitviewcontrollers setzen. Diese Zuweisung erfolgt in der Methode application:didFinishLaunchingWithOptions:. Da diese Methode natürlich weiterhin in den anderen Varianten funktionieren soll, prüft sie zunächst, ob der Rootviewcontroller der App ein Splitviewcontroller ist. In diesem Fall ermittelt die Methode zuerst den Alarmclock-Viewcontroller aus dem Splitviewcontroller. Der Detailviewcontroller ist der zweite Viewcontroller im Array viewControllers des Splitviewcontrollers. Allerdings liegt der Alarmclock-Viewcontroller in einem Navigationcontroller, und das Application-Delegate muss die Methode topViewController verwenden, um ihn zu bestimmen (siehe Listing 4.41).

id theRootViewController = self.window.rootViewController;

if([theRootViewController isKindOfClass:
[UISplitViewController class]]) {
id theViewController = [[theRootViewController
viewControllers][1] topViewController];

[theRootViewController setDelegate:theViewController];
}

Listing 4.41 Zuweisung des Delegates an den Splitviewcontroller



Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück




Copyright © Rheinwerk Verlag GmbH, Bonn 2014
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern


  Zum Rheinwerk-Shop
Zum Katalog: Apps programmieren für iPhone und iPad






Neuauflage: Apps programmieren für iPhone und iPad
Jetzt Buch bestellen


 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Einstieg in Objective-C 2.0 und Cocoa






Einstieg in Objective-C 2.0 und Cocoa


Zum Katalog: Spieleprogrammierung mit Android Studio






Spieleprogrammierung mit Android Studio


Zum Katalog: Android 5






Android 5


Zum Katalog: iPhone und iPad-Apps entwickeln






iPhone und iPad-Apps entwickeln


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo