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

Inhaltsverzeichnis
Geleitwort
Vorwort
1 Hello iPhone
2 Grundlagen
3 Views und Viewcontroller
4 Alles unter Kontrolle
5 Daten, Tabellen und Controller
6 Models, Layer, Animationen
7 Programmieren, aber sicher
8 Datenserialisierung und Internetzugriff
9 Jahrmarkt der Nützlichkeiten
A Sicherer Entwicklungszyklus
Stichwort

Download:
- ZIP, ca. 49,9 MB
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
Galileo Computing
1000 S., geb., mit DVD
39,90 Euro, ISBN 978-3-8362-1915-0
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ückbutton
Pfeil 4.2.4 Gehe drei Felder zurück
Pfeil 4.2.5 Die Werkzeugleiste
Pfeil 4.2.6 Navigations- und Werkzeugleisten gestalten
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 Einstellungen dauerhaft speichern
Pfeil 4.3.4 Anpassung an das iPad
Pfeil 4.4 Der Splitviewcontroller
Pfeil 4.4.1 Das Splitviewcontroller-Delegate
Pfeil 4.4.2 Ein praktisches Beispiel
Pfeil 4.4.3 Anpassung des Rotationsverhaltens
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.5.4 Der Tabbarcontroller im Storyboard
Pfeil 4.5.5 Die Gestaltungsmöglichkeiten für die Tabbar unter iOS 5
Pfeil 4.6 Der Pageviewcontroller
Pfeil 4.6.1 Einen Pageviewcontroller 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 Eigene Subviewcontroller
Pfeil 4.7.3 Eigene Delegate-Protokolle erstellen
Pfeil 4.7.4 Delegate-Protokolle erweitern
Pfeil 4.8 Regelbasierte Anpassung der Oberfläche
Pfeil 4.8.1 Gestaltungsregeln ...
Pfeil 4.8.2 ... und Gestaltungsmöglichkeiten

Galileo Computing - Zum Seitenanfang

4.3 Navigation- und Pop-over-Controller in der PraxisZur nächsten Überschrift

So viel Controller-Theorie verlangt nach praktischen Beispielen. Dazu werden Sie den analogen Wecker um einen Einstellungsdialog erweitern. Dabei soll der Wecker drei Einstellungsmöglichkeiten erhalten:

  1. Der Nutzer soll den Alarmton an- und ausschalten können. Ein abgeschalteter Alarmton ist besonders für lärmempfindliche Personen und notorische Zuspätkommer praktisch. Letztere können sich dann zu Recht entschuldigen, sie hätten ihren Wecker nicht gehört.
  2. Über eine weitere Einstellung soll der Nutzer Ziffern auf dem Ziffernblatt einschalten können.
  3. Minimalisten schätzen die dritte Einstellungsmöglichkeit, mit der Sie die Unterteilung des Ziffernblattes verändern können.

Projektinformation

Den Quellcode des nachfolgenden Beispielprojekts finden Sie auf der DVD unter Code/Apps/Storyboard/AlarmClock oder im Github-Repository zum Buch im Unterverzeichnis https://github.com/Cocoaneheads/iPhone/tree/Auflage_2/Apps/Storyboard/AlarmClock.

Für die zweite und dritte Einstellungsmöglichkeit müssen Sie die Klasse ClockView erweitern, damit sie diese Anzeigeoptionen auch darstellen kann. Dazu erhält die Klasse zunächst einmal einen Aufzählungstyp und zwei Propertys:

typedef enum {
PartitionOfDialNone = 0,
PartitionOfDialHours,
PartitionOfDialMinutes
} PartitionOfDial;

@interface ClockView : UIView

@property (nonatomic, strong) NSDate *time;
@property (nonatomic, strong, readonly) NSCalendar *calendar;
@property (nonatomic) BOOL showDigits;
@property (nonatomic) PartitionOfDial partitionOfDial;

- (void)startAnimation;
- (void)stopAnimation;

@end

Listing 4.19 Änderungen im Header von »ClockView.h«

Die Änderungen sehen Sie hervorgehoben in Listing 4.19. Der Aufzählungstyp PartitionOfDial benennt die drei möglichen Unterteilungen (keine, nur Stunden sowie Minuten und Stunden) für eine Uhr fest. Sie können einen Clockview über die Property partitionOfDial entsprechend konfigurieren. Mit der booleschen Property showDigits können Sie die Anzeige der Ziffern steuern. Das Zeichnen der Ziffern übernimmt die neue Methode drawDigits, die Sie vor der Methode drawRect: in die Implementierung der Klasse einfügen. Im Gegensatz zu den anderen Methoden verwendet sie ausschließlich Methoden aus dem UIKit und keine Core-Graphics-Funktionen zum Zeichnen. Das ist für die Darstellung von Text wesentlich einfacher, und Sie dürfen diese beiden Frameworks beim Zeichnen auch ruhig mischen.

- (void)drawDigits {
CGFloat theRadius = CGRectGetWidth(self.bounds) / 2.0;
UIFont *theFont = [UIFont systemFontOfSize:24];
CGRect theFrame;

[[UIColor darkGrayColor] set];
theRadius *= self.partitionOfDial == PartitionOfDialNone ?
0.9 : 0.6;
for(int i = 1; i <= 12; ++i) {
NSString *theText =
[NSString stringWithFormat:@"%d", i];

theFrame.origin = [self pointWithRadius:theRadius
angle:i * M_PI / 6];
theFrame.size = [theText sizeWithFont:theFont];
theFrame = CGRectOffset(theFrame,
-CGRectGetWidth(theFrame) / 2.0,
-CGRectGetHeight(theFrame) / 2.0);
[theText drawInRect:theFrame withFont:theFont];
}
}

Listing 4.20 Zeichnen des Ziffernblatts

Methoden zur Darstellung von Text stellt die Kategorie NSString(UIStringDrawing) zur Verfügung. Listing 4.20 verwendet zur Textdarstellung die Methode drawInRect:withFont:. Das Rechteck für die Darstellung berechnen Sie über die Größe des Textes, die Sie über die Methode sizeWithFont: ermitteln. Die bereits bekannte Methode pointWithRadius:angle: liefert den Mittelpunkt des darzustellenden Textes. Da Sie jedoch den Ursprungspunkt des Rechtecks benötigen, müssen Sie diesen durch eine Verschiebung um jeweils die halbe Breite und Höhe des Rechtecks berechnen. Die Verschiebung wird dabei von der Funktion CGRectOffset durchgeführt.

Der Wert der Schleifenvariablen i entspricht übrigens der darzustellenden Ziffer, und aus ihr lässt sich auch der Winkel i·2p/12 = i·p/6 berechnen. Zur Erinnerung noch einmal die Herleitung dieser Formel. Eine Stunde ist ein Zwölftel des Ziffernblattes. Ein Kreis hat 360°, also hat eine Stunde 360°/12 = 30°. Da das iPhone bekanntlich nur Winkel im Bogenmaß versteht, müssen Sie in dieser Rechnung 360° durch 2p ersetzen und erhalten 2p/12 = p/6 als Winkel für eine Stunde.

Falls das Ziffernblatt keine Unterteilungsstriche anzeigt, verwendet die Methode einen größeren Radius, um die Ziffern weiter außen darzustellen. Außerdem stellt es die Ziffern in Dunkelgrau dar. Dazu setzt das Listing die Textzeichenfarbe über die Anweisung [[UIColor darkGrayColor] set].

Als Nächstes passen Sie die Methode drawRect: der Klasse ClockView entsprechend Listing 4.21 an. Hier sind nur die wesentlichen Zeilen für die Änderungen und jeweils drei Punkte für die restlichen Anweisungen angegeben, um die Übersichtlichkeit zu erleichtern. Für die Konfiguration der Unterteilung müssen Sie zwei Abfragen einfügen. Wenn das Ziffernblatt keine Unterteilung haben soll, braucht das Programm die Schleife nicht zu durchlaufen, und nur für die Darstellung von Minutenunterteilungen darf es den else-Block innerhalb der Schleife ausführen.

- (void)drawRect:(CGRect)inRectangle {
...
if(self.partitionOfDial != PartitionOfDialNone) {
// Zifferneinteilung zeichnen
for(NSInteger i = 0; i < 60; ++i) {
...
if(i % 5 == 0) {
...
}
else if(self.partitionOfDial == Zeilenumbruch
PartitionOfDialMinutes) {
// Minuteneinteilungsstrich darstellen
...
}
}
}
if(self.showDigits) {
[self drawDigits];
}
[self drawClockHands];
CGContextRestoreGState(theContext);
}

Listing 4.21 Anpassungen an der Methode »drawRect:«

Schließlich fügen Sie am Ende einen Aufruf der neuen Methode drawDigits in einem if-Block ein. Diese Methode darf das Programm schließlich nur ausführen, wenn es Ziffern darstellen soll.

Wenn Sie das Programm jetzt ausführen, sehen Sie einen weißen Kreis mit den Zeigern darauf. Da die Objective-C-Laufzeitumgebung alle Attribute einer Klasse mit 0 initialisiert, sind die Unterteilungsstriche und die Ziffern zunächst einmal ausgeschaltet. Sie sollten also nun Ihre App um einen Dialog erweitern, mit dem der Nutzer diese Einstellungen anpassen kann.

Erweiterung von universellen und lokalisierten Apps

Genauer gesagt müssen Sie vier Einstellungsdialoge erstellen, da Ihre App zwei Gerätefamilien und zwei Sprachen unterstützt. Das klingt nach viel Arbeit, die sich jedoch bei einer systematischen Vorgehensweise relativ gut begrenzen lässt. Erweitern Sie die App zunächst nur für eine Gerätefamilie (am besten für das iPhone) und eine Sprache.

Achten Sie dabei jedoch darauf, dass Ihre Testumgebung (Simulator, iOS-Geräte) auch genau zu dieser Konfiguration passt. Das gilt insbesondere für die Spracheinstellungen. Erst wenn Sie die Anpassungen für diese Konfiguration beendet haben, nehmen Sie die anderen Sprachen und die andere Gerätefamilie (also das iPad) in Angriff – allerdings auch hier immer nur jeweils eine Konfiguration gleichzeitig. Dabei können Sie neue Viewcontroller einfach in die anderen Storyboards kopieren. Das geht sogar mit mehren Viewcontrollern auf einmal, wobei Xcode dann sogar die Übergänge zwischen diesen Controllern mitkopiert.


Galileo Computing - Zum Seitenanfang

4.3.1 Navigationcontroller anlegenZur nächsten ÜberschriftZur vorigen Überschrift

Die Wecker-App braucht also einen Einstellungsdialog, den sie über einen Navigationcontroller anzeigt. Dazu erweitern Sie zunächst das englische Storyboard für iPhones. Da die App den Einstellungsdialog über einen Navigationcontroller anzeigt, ziehen Sie zunächst aus der Bibliothek einen solchen Controller auf die Zeichenfläche des Storyboards. Der Interface Builder legt dabei automatisch einen neuen Rootviewcontroller an, den Sie jedoch nicht brauchen. Selektieren Sie dieses Objekt, und löschen Sie es über die Taste æ_ oder Entf. Als Nächstes ziehen Sie eine Verbindung vom Navigation- zum vorhandenen Viewcontroller des Weckers und wählen im darauf erscheinenden Pop-over-Dialog den Punkt root view controller aus (siehe Abbildung 4.19). Sie haben dem Navigationcontroller nun einen neuen Rootviewcontroller zugewiesen.

Der Alarmclock-Viewcontroller sollte jetzt auch eine Navigationsleiste anzeigen. Dort löschen Sie das Label Alarm Clock und tragen stattdessen diesen Text in den Titel des Navigation-Items ein. Durch einen Doppelklick in die Mitte der Leiste können Sie diese Änderungen direkt in der simulierten Navigationsleiste vornehmen. Außerdem legen Sie dort auf der rechten Seite einen Button (Typ UIBarButtonSystemItemCompose) für den Aufruf der Einstellungen an. Durch die Anzeige der Navigationsleiste verschieben sich die Views der Zeitanzeige. Kontrollieren und korrigieren Sie die Abmessungen des Ziffernblatts, sodass es wieder quadratisch ist.

Die App sollte nun den Navigationcontroller als initialen Viewcontroller des Fensters verwenden. Dazu öffnen Sie den Attributinspektor des Navigationcontrollers und setzen dort das Häkchen bei Is Initial View Controller. Beim Viewcontroller für die Zeitanzeige entfernen Sie dieses Häkchen. Nach diesen Änderungen sollte Ihr Storyboard so wie in Abbildung 4.28 aussehen.

Abbildung

Abbildung 4.28 Der Navigationcontroller in der Wecker-App

Um Ihre Änderungen zu überprüfen, sollten Sie die App starten. Achten Sie dabei darauf, dass die Spracheinstellungen des Simulators beziehungsweise des Geräts der Sprache des Storyboards entsprechen. Die App sollte nun eine Navigationsleiste mit dem Titel und einem Button anzeigen.


Galileo Computing - Zum Seitenanfang

4.3.2 Einen Dialog für die Einstellungen gestaltenZur nächsten ÜberschriftZur vorigen Überschrift

Als Nächstes erstellen Sie den Dialog für die Einstellungen der App. Dazu eignen sich hervorragend statische Tableviews, die Apple mit iOS 5 eingeführt hat. Ein Tableview verwaltet seinen Inhalt in untereinanderliegenden Zellen, die er in Abschnitte gruppieren kann. Während die Anzahl der Zellen in einem dynamischen Tableview von der darzustellenden Datenmenge der Applikation abhängt, ist sie bei statischen Ta-bleviews fest. Dynamische Tableviews lernen Sie in Kapitel 5, »Daten, Tabellen und Controller«, kennen.

Um statische Tableviews nutzen zu können, müssen Sie einen Tableviewcontroller verwenden. Ziehen Sie ein solches Objekt aus der Bibliothek auf die Zeichenfläche Ihres Storyboards. Der Tableviewcontroller enthält bereits einen Tableview, den Sie allerdings noch konfigurieren müssen, wozu Sie dessen Attributinspektor öffnen (siehe Abbildung 4.29). Wählen Sie unter Content den Punkt Static Cells aus, um einen statischen Tableview zu erhalten. Der Tableview soll sich in zwei Abschnitte aufteilen, weswegen Sie unter Sections den Wert 2 einstellen. Wenn Sie unter Style den Eintrag Grouped auswählen, zeichnet der Tableview einen grauen Rahmen um die Abschnitte und rundet deren Ecken ab. Außerdem sollten Sie die Auswahl von Zellen unterbinden, indem Sie unter Selection den Punkt No Selection auswählen.

Als Nächstes können Sie die Zellen des Tableviews gestalten. Im ersten Abschnitt benötigen Sie zwei Zellen und im zweiten Abschnitt eine Zelle. Der Interface Builder hat in jedem Abschnitt jeweils drei Zellen angelegt. Sie brauchen also nur die überflüssigen Zellen zu löschen. In alle Zellen ziehen Sie jeweils ein Label, deren Texte Sie in »Digits« (Ziffern), »Partition of Dial« (Ziffernblatteinteilung) und »Sound« (Ton) umändern. In die Zellen für das Ein- und Ausschalten der Ziffern und des Tons fügen Sie jeweils auf der rechten Seite noch einen Schalter (UISwitch) ein.

Abbildung

Abbildung 4.29 Einstellungen für den statischen Tableview

Für die Einstellung der Ziffernblatteinteilung brauchen Sie ein Segmented Control. Das ist ein Schalter, der mehrere Zustände haben kann. Für die App benötigen Sie drei Zustände (keine Einteilung, Stundeneinteilung sowie Stunden- und Minuteneinteilung). Allerdings reicht dafür der Platz in der Zelle nicht aus. Sie können jedoch die Höhe der Zelle in deren Größeninspektor verändern. Tragen Sie unter Row Height den Wert 92 ein, dann lässt sich das Control unter dem Label platzieren.

Abbildung

Abbildung 4.30 Der Einstellungsdialog des Weckers

Die Absatzüberschriften können Sie anpassen, indem Sie die Objekte mit der Bezeichnung Table View Section in der Baumdarstellung auswählen. Öffnen Sie deren Attributinspektor, und geben Sie unter Header jeweils »Appearance« und »Alarm« ein. Der fertige View sollte so wie in Abbildung 4.30 aussehen.

Als Nächstes legen Sie den Übergang zum Einstellungsdialog an. Dazu ziehen Sie eine Verbindung vom Button in der Navigationsleiste des Viewcontrollers für die Zeitanzeige zu dem neuen Controller für die Einstellungen. In der Auswahl (siehe Abbildung 4.19) selektieren Sie den Punkt push. Sie können nun den Einstellungsdialog in Ihrer App aufrufen, indem Sie auf diesen Button tippen. Da Sie bei seinem Vorgänger einen Titel in das Navigations-Item eingetragen haben, zeigt die Navigationsleiste beim Einstellungsdialog auch einen Zurückbutton an, über den Sie zur Zeitanzeige zurückkehren können.


Galileo Computing - Zum Seitenanfang

4.3.3 Einstellungen dauerhaft speichernZur nächsten ÜberschriftZur vorigen Überschrift

Die App zeigt den Einstellungsdialog zwar an und auch die Schalter funktionieren bereits, das hat allerdings noch keinen Einfluss auf die Zeitanzeige. Der Einstellungsdialog benötigt also eine eigene Logik in Form einer eigenen Klasse. Dafür legen Sie im Projekt eine Unterklasse von UITableViewController mit dem Namen PreferencesViewController an, die Sie dem Viewcontroller im Storyboard über dessen Identitätsinspektor zuweisen. Anschließend löschen Sie aus der Implementierungsdatei den kompletten Inhalt des Implementierungsblocks.

In die neue Klasse fügen Sie für jedes der drei Controls eine passende Outlet-Property mit dem Speicherverwaltungstyp weak hinzu, sodass die Klassendeklaration wie in Listing 4.22 aussieht. Am einfachsten legen Sie diese drei Propertys an, indem Sie jeweils eine Verbindung von den Controls in die Headerdatei des Einstellungscontrollers ziehen und die entsprechenden Namen eingeben.

@interface PreferencesViewController : UITableViewController

@property (weak, nonatomic) IBOutlet UISwitch *digitsSwitch;
@property (weak, nonatomic) IBOutlet Zeilenumbruch
UISegmentedControl *partitionControl;
@property (weak, nonatomic) IBOutlet UISwitch *soundSwitch;

@end

Listing 4.22 Klassendeklaration für den Einstellungsdialog

Xcode fügt bis zur Version 4.4 noch Synthesize-Anweisungen in die Implementierung ein, die die Version 4.0 des LLVM-Compilers von Xcode 4.5 nicht mehr benötigt. Außerdem legt Xcode noch die Methode viewDidUnload an, die die Outlets freigibt. Da Sie dafür jedoch den Speicherverwaltungstyp weak verwendet haben, ist das nicht notwendig, und Sie können die komplette Methode löschen.

Natürlich haben Sie durch die Einführung dieser neuen Klasse das Verhalten des Programms noch nicht verändert. Es stellt sich nun die Frage, wie der Einstellungsdialog die Änderungen an den Wecker übergeben soll. Die naive Lösung zu dieser Frage lautet: Der Controller für die Einstellungen besitzt eine Referenz auf den Clockview oder den Viewcontroller für die Zeitanzeige und schreibt die geänderten Werte direkt oder indirekt in den Clockview. Bei dieser Lösung entsteht meist eine starke Abhängigkeit zwischen den Viewcontroller- und/oder Viewklassen.

Um unsere Frage zu beantworten, sollten Sie noch eine weitere, implizite Anforderung berücksichtigen: Die Einstellungswerte sollen über einen Neustart der App erhalten bleiben. Sie müssen also die Einstellungswerte nicht nur an das Ziffernblatt, sondern auch an eine Komponente übertragen, die diese Werte dauerhaft speichern kann. Mit der Lösung dieser Anforderung erhalten Sie auch eine recht elegante Lösung für das oben beschriebene Abhängigkeitsproblem.

Die Klasse NSUserDefaults aus dem Foundation-Framework erlaubt eine permanente Speicherung nahezu beliebiger Schlüssel-Wert-Paare. Dazu verwenden Sie in der Regel das Objekt, das Ihnen die Klassenmethode standardUserDefaults liefert. Listing 4.23 enthält zwei Methoden, mit denen der Einstellungsdialog seine Werte speichern und wiederherstellen kann.

Wofür »NSUserDefaults« verwenden?

Sie können zwar in den Userdefaults beliebig große Daten und Datenmengen abspeichern, diese Klasse ist jedoch vornehmlich für die Einstellungsdaten der App und nicht für die Ablage größerer Dokumente oder Daten gedacht. Wenn Sie größere Datenmengen dauerhaft speichern möchten, sollten Sie andere Möglichkeiten, wie das Dateisystem oder das Framework Core Data, nutzen. Das Fototagebuch in Kapitel 5 zeigt ein Beispiel für die Nutzung von Core Data.

Fügen Sie diese beiden Methoden zu der Implementierung der Klasse PreferencesViewController hinzu, und deklarieren Sie sie auch im Header. Das Userdefaults-Objekt benutzen Sie dabei wie ein Verzeichnis. Sie können jeweils unter einem Schlüssel einen Wert dauerhaft speichern, und diesen Wert können Sie anhand des Schlüssels wiederherstellen. Beispielsweise speichert die Wecker-App die Ziffernblatteinteilung als einen Ganzzahlwert unter dem Schlüssel partitionOfDial ab.

- (void)savePreferences {
NSUserDefaults *theDefaults = Zeilenumbruch
[NSUserDefaults standardUserDefaults];

[theDefaults setBool:self.digitsSwitch.on Zeilenumbruch
forKey:@"showDigits"];
[theDefaults Zeilenumbruch
setInteger:self.partitionControl.selectedSegmentIndex Zeilenumbruch
forKey:@"partitionOfDial"];
[theDefaults setBool:self.soundSwitch.on Zeilenumbruch
forKey:@"playSound"];
[theDefaults synchronize];
}
- (void)restorePreferences {
NSUserDefaults *theDefaults = Zeilenumbruch
[NSUserDefaults standardUserDefaults];

[theDefaults synchronize];
self.digitsSwitch.on = Zeilenumbruch
[theDefaults boolForKey:@"showDigits"];
self.partitionControl.selectedSegmentIndex = Zeilenumbruch
[theDefaults integerForKey:@"partitionOfDial"];
self.soundSwitch.on = Zeilenumbruch
[theDefaults boolForKey:@"playSound"];
}

Listing 4.23 Wiederherstellung und Speicherung der Einstellungen

Einstellungen synchronisieren

NSUserDefaults legt die Einstellungswerte nicht sofort im Dateisystem des iOS-Gerätes ab, sondern nur in periodischen Zeitabständen. Durch einen Aufruf der Methode synchronize können Sie allerdings die sofortige Speicherung erzwingen. Diese Methode dient auch dazu, die aktuellen Einstellungen aus dem Dateisystem zu laden.

Sie können nun diese beiden Methoden aufrufen, bevor beziehungsweise nachdem die App den Einstellungsdialog anzeigt. Dazu überschreiben Sie die Methoden viewWillAppear: und viewWillDisappear: so wie in Listing 4.24:

- (void)viewWillAppear:(BOOL)inAnimated {
[super viewWillAppear:inAnimated];
[self restorePreferences];
}

- (void)viewWillDisappear:(BOOL)inAnimated {
[self savePreferences];
[super viewWillDisappear:inAnimated];
}

Listing 4.24 Wiederherstellung und Speicherung des Anzeigezustands

Der Einstellungsdialog behält nun seine Werte auch über einen Programmneustart hinweg bei. Das sollten Sie ruhig einmal mit ein paar Testläufen ausprobieren.

Jetzt fehlt nur noch die Übergabe der Werte an die Zeitanzeige. Dafür können Sie das gleiche Vorgehen wie beim Einstellungsdialog verwenden. Das heißt, der Viewcontroller für die Zeitanzeige liest die Werte aus den Userdefaults und übergibt sie an den View. Diese Aufgabe übernimmt die Methode updateClockView, die Sie in die Implementierung der Klasse AlarmClockViewController einfügen und von der Methode viewWillAppear: aus aufrufen (siehe Listing 4.25). Da Cocoa Touch beim Entfernen des Einstellungsdialogs aus dem Navigationcontroller diese Methode aufruft, übernimmt die Uhr dadurch auch die neuen Einstellungswerte in die Anzeige, und es entsteht auch keine Abhängigkeit zwischen den beiden Viewcontrollern.

- (void)updateClockView {
NSUserDefaults *theDefaults = Zeilenumbruch
[NSUserDefaults standardUserDefaults];

self.clockView.showDigits = Zeilenumbruch
[theDefaults boolForKey:@"showDigits"];
self.clockView.partitionOfDial = Zeilenumbruch
[theDefaults integerForKey:@"partitionOfDial"];
[self.clockView setNeedsDisplay];
}

- (void)viewWillAppear:(BOOL)inAnimated {
[super viewWillAppear:inAnimated];
[self updateClockView];
[self updateControl];
}

Listing 4.25 Aktualisierung der Einstellungen in der Zeitanzeige

Nun können Sie über den Einstellungsdialog das Aussehen der Zeitanzeige verändern, und die App speichert Ihre Einstellungen auch dauerhaft ab. Dabei nutzt die App zur Lösung der Frage nach der Wertübergabe die Userdefaults, was den angenehmen Nebeneffekt hat, dass die App die Einstellungen auch dauerhaft speichert.

Etwas begriffsstutzig ...

... scheint der Clockview zu sein, wenn Sie den Aufruf der Methode setNeedsDisplay in updateClockView weglassen. Dann sieht er nämlich nach dem Verlassen des Einstellungsdialogs zunächst genau so aus wie vorher. Nach einer kurzen Zeit springt dann die Anzeige auf die neuen Werte um. Das Ziffernblatt zeigt die neuen Einstellungen nämlich erst an, wenn der Timer die Zeitanzeige aktualisiert. Durch den Aufruf von setNeedsDisplay erzwingen Sie die Aktualisierung jedoch schon vorher.

Die App wertet jetzt die Einstellungen für die Ziffernblatteinteilung und die Anzeige der Ziffern aus. Über die dritte Einstellung können Sie den Ton für die lokale Benachrichtigung an- oder abschalten. Um diese Einstellungsmöglichkeit zu aktivieren, müssen Sie die Methode createAlarm in AlarmClockViewController ändern und dort die Zuweisung der Property soundName in eine if-Abfrage packen:

if([[NSUserDefaults standardUserDefaults]
boolForKey:@"playSound"]) {
theNotification.soundName = @"ringtone.caf";
}

Listing 4.26 Anpassung, um den Sound abzuschalten

Wenn die App zur Laufzeit eine lokale Benachrichtigung empfängt, spielt sie den Ton ab. Auch hier sollte sie die Einstellungen berücksichtigen. Dazu müssen Sie die Methode playSound im App-Delegate anpassen:

- (void)playSound {
if([[NSUserDefaults standardUserDefaults]
boolForKey:@"playSound"]) {
NSNumber *theId = self.soundId;

if(theId) {
AudioServicesPlaySystemSound(
[theId unsignedIntValue]);
}
}
}

Listing 4.27 Den Ton nur bei der entsprechenden Einstellung abspielen

Wenn Sie die Applikation jetzt starten, zeigt sie ein leeres Ziffernblatt an. Das liegt daran, dass auch die Einstellungen noch leer sind. Sie können sie zwar jetzt leicht über den Dialog ändern, es ist allerdings schöner, wenn die App beim ersten Start das Ziffernblatt mit Unterteilungen anzeigt. Über die Methode registerDefaults: können Sie in den Userdefaults Standardwerte anlegen, ohne Sie jedoch dort zu speichern. Das Userdefaults-Objekt verwendet diese Werte, wenn es zu einem Schlüssel keinen Wert enthält. Um Standardwerte zu setzen, fügen Sie die Anweisungen aus Listing 4.28 in die Methode application:didFinishLaunchingWithOptions: ein; danach sollte die App auch beim ersten Start Ziffern und Unterteilungen anzeigen, die sich über den Einstellungsdialog aber dauerhaft ändern lassen.

NSUserDefaults *theDefaults = Zeilenumbruch
[NSUserDefaults standardUserDefaults];

[theDefaults registerDefaults:@{
@"showDigits" : @YES,
@"partitionOfDial" : @(PartitionOfDialMinutes),
@"playSound" : @YES
}];

Listing 4.28 Anlegen von Standardeinstellungen

Das erste Mal

Sie können den ersten Start einer App im Simulator und auf dem Gerät beliebig oft simulieren, indem Sie einfach die App über das Springboard löschen. Halten Sie dazu das App-Icon so lange gedrückt, bis die Icons zu wackeln anfangen. Sie können die App nun über das Kreuz an der linken oberen Ecke des Icons löschen.

Mit diesen Änderungen haben Sie die Umstellung der englischen Version des Programms für das iPhone abgeschlossen. Als Nächstes sollten Sie sich die deutsche Version für das iPhone vornehmen. Da Sie nur geringfügige Anpassungen am View der Zeitanzeige im Storyboard vorgenommen haben, sollten Sie einfach den Viewcontroller aus dem deutschen Storyboard löschen und alle Viewcontroller aus dem englischen Storyboard ins deutsche kopieren. Sie können auch einfach das deutsche Storyboard über dessen Dateiinspektor löschen und danach eine neue deutsche Lokalisierung aus der englischen anlegen. Danach brauchen Sie nur noch die Texte zu übersetzen. Änderungen am Programmcode oder anderen Dateien sind nicht notwendig.


Galileo Computing - Zum Seitenanfang

4.3.4 Anpassung an das iPadZur vorigen Überschrift

Die Anpassungen für das iPad können Sie nun analog zu der Erstellung des deutschen Storyboards durchführen, indem Sie die Elemente aus dem englischen Storyboard für das iPhone in ein neues Storyboard für das iPad kopieren. Achten Sie dabei darauf, den initialen Viewcontroller richtig zu setzen und in den Targeteinstellungen das Main Storyboard für das iPad auszuwählen. Wenn Sie den Einstellungsdialog der iPad-App danach aufrufen, stellen Sie allerdings fest, dass er optisch nicht sehr ansprechend ist. Das iPad zieht den Tableview mit den Einstellungen auf die volle Bildschirmgröße auf, und der Wechsel zwischen den beiden Viewcontrollern sieht sehr unruhig aus, weil Cocoa Touch den kompletten Bildschirmbereich auswechselt. Führen Sie diese Umstellung für die englische Version jedoch trotzdem aus, weil sie die Basis für eine iPad-spezifische Anpassung ist.

Auf dem iPad ist ein Pop-over für die Anzeige des Einstellungsdialogs geeigneter, da Sie hier die Anzeigegröße des Views frei wählen können. Dazu müssen Sie im Interface Builder erst mal diesen View kleinkriegen. Dazu wählen Sie im Attributinspektor des Einstellungsdialogs unter Size den Punkt Freeform. Durch diese Einstellung können Sie jetzt die Größe des Tableviews in dessen Größeninspektor verändern. Stellen Sie hier eine Breite und Höhe von jeweils 320 Pixeln ein.

Als Nächstes ändern Sie den Übergang von der Zeitanzeige zu den Einstellungen von Push auf Pop-over um. Selektieren Sie dazu den Übergang im Storyboard, öffnen Sie dessen Attributinspektor, und wählen Sie dort Popover unter Style aus. Nach dieser Änderung öffnet die App die Einstellungen in einem Pop-over.

Abbildung

Abbildung 4.31 Der Einstellungsdialog als Pop-over

Bei den Anpassungen für das iPad brauchten Sie bis jetzt keine Zeile Code zu schreiben. Die App wählt beim Programmstart das passende Storyboard zur Gerätefamilie aus, und das Storyboard legt dann über den Übergang die Anzeigevariante des Einstellungsdialogs fest.

Allerdings funktioniert die App auf dem iPad noch nicht ganz zufriedenstellend. Wenn Sie den Einstellungsdialog schließen, übernimmt die Zeitanzeige die neuen Werte nicht. Sie müssen dafür die App erst neu starten. Das liegt daran, dass Cocoa Touch die Methode viewWillAppear: beim Schließen des Pop-ups nicht aufruft. Die App zeigt ja das Ziffernblatt auch während der Darstellung des Einstellungsdialogs an, und so ist dieser Methodenaufruf hier nicht sinnvoll. Die Zeitanzeige muss also auf einem anderen Weg die Änderung der Einstellungen mitbekommen.

Dazu eignet sich das Key-Value-Observing, das Sie bereits in Kapitel 2, »Grundlagen«, kennengelernt haben. Der Viewcontroller für die Zeitanzeige registriert sich dazu in der Methode viewDidAppear: als Beobachter für die Schlüssel showDigits und partitionOfDial der Userdefaults. Entsprechend dazu muss der Viewcontroller die Beobachtung in viewWillDisappear: auch wieder aufheben. Dies ist ein typischer Fall für die in Kapitel 3, »Views und Viewcontroller«, genannte Faustregel für die Ressourcenverwaltung.

Die Beobachtermethode muss nun lediglich die Methode updateClockView aufrufen. Den Programmcode dafür finden Sie in Listing 4.29. Nach dieser Änderung übernimmt das Ziffernblatt die Einstellungen, sobald Sie den Einstellungsdialog schließen.

- (void)viewDidAppear:(BOOL)inAnimated {
[super viewDidAppear:inAnimated];
NSUserDefaults *theDefaults = Zeilenumbruch
[NSUserDefaults standardUserDefaults];

[theDefaults addObserver:self forKeyPath:@"showDigits" Zeilenumbruch
options:0 context:nil];
[theDefaults addObserver:self Zeilenumbruch
forKeyPath:@"partitionOfDial" options:0 context:nil];
[self.clockView startAnimation];
}

- (void)viewWillDisappear:(BOOL)inAnimated {
NSUserDefaults *theDefaults = Zeilenumbruch
[NSUserDefaults standardUserDefaults];

[theDefaults removeObserver:self Zeilenumbruch
forKeyPath:@"showDigits"];
[theDefaults removeObserver:self Zeilenumbruch
forKeyPath:@"partitionOfDial"];
[self.clockView stopAnimation];
[super viewWillDisappear:inAnimated];
}

- (void)observeValueForKeyPath:(NSString *)inKeyPath
ofObject:(id)inObject change:(NSDictionary *)inChange
context:(void *)inContext {
[self updateClockView];
}

Listing 4.29 An- und Abmeldung für das Key-Value-Observing

Für den Nutzer wäre es natürlich hilfreich, wenn er die Auswirkungen der Einstellungsänderungen unmittelbar sehen könnte – wenn also das Ziffernblatt die Einstellungen schon aus dem geöffneten Dialog übernehmen würde. Auch diese Anforderung lässt sich leicht erfüllen. Dazu muss die App lediglich die Einstellungen immer dann in den Userdefaults speichern, wenn der Nutzer eine Einstellung ändert. Durch das Key-Value-Observing leitet der Viewcontroller für die Zeitanzeige die neuen Werte unmittelbar an den Clockview weiter.

Sie können dieses Verhalten erreichen, indem Sie in der Klasse PreferencesViewController eine Actionmethode dafür bereitstellen, die Sie mit den Value-Changed-Events der Controls verbinden. Am einfachsten verwenden Sie dafür die Methode savePreferences, indem Sie deren Ergebnistyp in IBAction ändern. Wenn Sie nun die App auf einem iPad starten und den Einstellungsdialog aufrufen, übernimmt das Ziffernblatt die neuen Einstellungen, sobald Sie einen Wert im geöffneten Pop-up-Dialog ändern.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.







<< zurück
  Zum Katalog
Zum Katalog: Apps programmieren für iPhone und iPad

Apps programmieren für iPhone und iPad
Jetzt 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: Apps entwickeln für iPhone und iPad - Videotraining






 Apps entwickeln für
 iPhone und iPad -
 Videotraining


Zum Katalog: Apps mit HTML5 und CSS3






 Apps mit HTML5
 und CSS3


Zum Katalog: iPhone- und iPad-Apps entwickeln






 iPhone- und
 iPad-Apps entwickeln


Zum Katalog: Android 4






 Android 4


Zum Katalog: Android-Apps entwickeln - Videotraining






 Android-Apps
 entwickeln -
 Videotraining


Zum Katalog: Windows Store Apps mit XAML und C#






 Windows Store Apps
 mit XAML und C#


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo





Copyright © Galileo Press 2013
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.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de