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 3 Sehen und anfassen
Pfeil 3.1 Eigene Viewklassen in Cocoa Touch
Pfeil 3.1.1 Zeichnen in Cocoa Touch
Pfeil 3.1.2 Zeitberechnung
Pfeil 3.1.3 View-Erzeugung über Storyboards
Pfeil 3.1.4 Aktualisierung der Zeitanzeige
Pfeil 3.1.5 Wiederverwendbarkeit von Views
Pfeil 3.1.6 Zeichenfarbe festlegen
Pfeil 3.2 Views und Viewcontroller
Pfeil 3.2.1 Outlets
Pfeil 3.2.2 Outlet-Collections
Pfeil 3.2.3 Containerviews
Pfeil 3.2.4 View-Hierarchien
Pfeil 3.2.5 Actions
Pfeil 3.2.6 Ereignisse
Pfeil 3.2.7 Controlzustände und Buttons
Pfeil 3.2.8 Touch-A, Touch-A, Touch Me
Pfeil 3.2.9 Übergänge
Pfeil 3.2.10 Ein kleines Intermezzo über Labels
Pfeil 3.2.11 Beliebige Objekte im Storyboard
Pfeil 3.2.12 Der Lebenszyklus eines Viewcontrollers
Pfeil 3.2.13 Speicher- und Ressourcenverwaltung des Viewcontrollers
Pfeil 3.3 Lokale Benachrichtigungen
Pfeil 3.3.1 Benachrichtigungen versenden
Pfeil 3.3.2 Benachrichtigungen verarbeiten
Pfeil 3.4 Eine App für alle
Pfeil 3.4.1 Das Retina-Display
Pfeil 3.4.2 Launch-Images
Pfeil 3.4.3 Sprachkursus für die App
Pfeil 3.4.4 Es funktioniert nicht
Pfeil 3.4.5 Unterstützung älterer iOS-Versionen
Pfeil 3.4.6 Base-Internationalisierung ausschalten
Pfeil 3.4.7 Universelle Apps
Pfeil 3.5 Geräteausrichtungen, Autosizing und Autolayout
Pfeil 3.5.1 Flexible Views dank Autosizing
Pfeil 3.5.2 Autolayout
Pfeil 3.5.3 Restriktionen im Interface-Builder festlegen
Pfeil 3.5.4 Autolayout und Lokalisierung
Pfeil 3.6 Fehlersuche
Pfeil 3.6.1 Logging
Pfeil 3.6.2 Der Debugger
Pfeil 3.6.3 Breakpoints verwalten
Pfeil 3.6.4 Die Debugger-Konsole

Rheinwerk Computing - Zum Seitenanfang

3.3Lokale BenachrichtigungenZur nächsten Überschrift

Dem Wecker fehlt jetzt nur noch ein Alarmton, der zur gewählten Zeit erklingt. Sie können natürlich die Zeitdifferenz zum aktuellen Zeitpunkt berechnen und einen Timer starten. Dieser ruft nach Ablauf der Zeit eine Methode auf, die einen Ton abspielt. Das funktioniert allerdings nur, wenn Ihr Programm die ganze Zeit im Vordergrund weiterläuft. Sie können also Ihr iPhone in der Zwischenzeit nur eingeschränkt nutzen.

Das Beispielprogramm verwendet stattdessen lokale Benachrichtigungen. Das sind Ereignisse, die das Betriebssystem zu einem bestimmten Zeitpunkt auslöst. Eine lokale Benachrichtigung kann beim Auslösen eine Nachricht auf dem Bildschirm anzeigen, eine Audiodatei abspielen oder den Wert im Badge der App auf einen festen Wert setzen. Die Badges sind die roten Plaketten am App-Icon, die viele Apps (z. B. Mail) verwenden (siehe Abbildung 3.46). Lokale Benachrichtigungen funktionieren auch, wenn Sie Ihre App in den Hintergrund schicken oder sogar stoppen.

Die Darstellung der Textnachricht erfolgt über die Mitteilungszentrale. Sie stellt lokale Benachrichtigungen standardmäßig als Banner am oberen Bildschirmrand dar. Sie können in der App Einstellungen unter MitteilungenAlarmClock [Anm.: Nach der Lokalisierung der App im Abschnitt Sprachkursus für die App heißt dieser Punkt Wecker.] Hinweisstil aber Hinweise auswählen, um die Nachrichten in Alertboxen anzeigen zu lassen. Eine (Vor-)Auswahl dieses Anzeigestils über die App ist dagegen leider nicht möglich.

Abbildung

Abbildung 3.46 App-Icon mit Badge

Nachrichten an aktive Apps

Die Nachricht erscheint dagegen nicht, wenn die App im Vordergrund läuft. Stattdessen ruft Cocoa Touch eine Methode des Application-Delegates auf. In diesem Fall können Sie also selbst entscheiden, was Sie mit der Nachricht anfangen wollen.

Abbildung

Abbildung 3.47 Anzeige einer lokalen Benachrichtigung in einem Banner

Sie definieren eine neue Benachrichtigung, indem Sie ein Objekt der Klasse UILocalNotification erzeugen und den Auslösezeitpunkt setzen. Außerdem können Sie einen Nachrichtentitel und -text angeben, wenn Sie eine Alertbox anzeigen lassen wollen. Für den Benachrichtigungston und das Badge können Sie den Namen des Tons beziehungsweise den Wert übergeben. Die neue Benachrichtigung fügen Sie danach in die Warteschlange für lokale Benachrichtigungen ein, auf die Sie über Methoden des Application-Singletons zugreifen können. Cocoa Touch liest die Benachrichtigungen von dort, löst sie zum gewünschten Zeitpunkt aus und löscht sie danach, falls es sich um eine einmalige Benachrichtigung handelt.


Rheinwerk Computing - Zum Seitenanfang

3.3.1Benachrichtigungen versendenZur nächsten ÜberschriftZur vorigen Überschrift

Die Methode scheduleLocalNotification: der Klasse UIApplication fügt eine Benachrichtigung in die Warteschlange ein, und cancelLocalNotification: entfernt sie wieder daraus; die Methode cancelAllLocalNotifications entfernt alle Benachrichtigungen der App aus der Warteschlange. Über die Property scheduledLocalNotifications können Sie alle Benachrichtigungen aus der Warteschlange auslesen und auch setzen. Sie können in der Warteschlange allerdings nur auf die Benachrichtigungen Ihrer App zugreifen. Der Zugriff auf die Benachrichtigungen anderer Apps ist nicht möglich. Ebenso wenig können Sie anderen Apps über diesen Mechanismus eine Benachrichtigung schicken.

Für die Verwaltung der lokalen Benachrichtigung benötigen Sie zunächst eine weitere Action-Methode, die das Clock-Control auslöst. Sie brauchen diese Methode nur auszuführen, wenn der Nutzer den Alarmzeiger loslässt. Ziehen Sie dafür also eine Action-Verbindung vom Clock-Control in die anonyme Kategorie des Viewcontrollers. Als Methodennamen verwenden Sie updateAlarm, und im Feld Event wählen Sie Touch Up Inside aus. Alle Einstellungen können Sie Abbildung 3.48 entnehmen.

Abbildung

Abbildung 3.48 Die Parameter der neuen Action-Verbindung

Die Methode updateAlarm löscht entweder alle Benachrichtigungen der App, wenn Sie den Alarm ausgeschaltet haben, oder legt über einen Aufruf der Methode createAlarm eine neue Benachrichtigung an.

- (IBAction)updateAlarm {
if(self.alarmHidden) {
UIApplication *theApplication =
[UIApplication sharedApplication];

[theApplication cancelAllLocalNotifications];
}
else {
[self createAlarm];
}
}

Listing 3.54 Aktualisierung des Alarms

Da der Nutzer auch über den Gesture-Recognizer die Alarmzeit ändern kann, sollten Sie auch die Methode switchAlarm: die Methode updateAlarm aufrufen. Fügen Sie dazu in Listing 3.48 den Methodenaufruf als letzte Anweisung der äußeren if-Abfrage ein

- (IBAction)switchAlarm: 
(UILongPressGestureRecognizer *)inRecognizer {
if(inRecognizer.state == UIGestureRecognizerStateEnded) {
if(self.alarmHidden) {
...
}
else {
self.alarmHidden = YES;
}
[self updateAlarm];
}
}

Listing 3.55 Aktualisierung der Alarmzeit

Als Nächstes müssen Sie die Alarmzeit, die das Clock-Control als Zeit in Sekunden über seine Property time liefert, in ein Datum – die absolute Alarmzeit – umrechnen. Dabei liegen deren Werte zwischen 0 (einschließlich) und 43200 (exklusive), was der Anzahl der Sekunden in 12 Stunden entspricht, da der Wecker ja ein 12-Stunden-Zifferblatt hat. Also ist die absolute Alarmzeit die Summe aus Zeitpunkt des Tagesbeginns und der relativen Alarmzeit. Allerdings ist das nur die frühestmögliche Alarmzeit; die Berechnung der tatsächlichen Alarmzeit ist noch etwas komplizierter. Dazu ein Beispiel: Angenommen, Sie stellen Ihren Wecker um 22 Uhr auf 8 Uhr. Dann erwarten Sie, dass der Wecker um 8 Uhr am nächsten Morgen klingelt. Die absolute Alarmzeit sollte also immer später als die aktuelle Zeit sein.

Um diese Anforderung zu erfüllen, addieren Sie zu der frühestmöglichen Alarmzeit so lange 12 Stunden, bis das Ergebnis nach der aktuellen Zeit liegt. Bei dem Beispiel ist die frühestmögliche Alarmzeit 8 Uhr des aktuellen Tages. Wenn Sie 12 Stunden addieren, erhalten Sie 20 Uhr, was immer noch vor 22 Uhr liegt. Erneutes Hinzuzählen von 12 Stunden liefert 8 Uhr des Folgetages, und das ist genau die erwartete Alarmzeit. Diese Berechnung führt die Methode alarmDate in Listing 3.56 durch.

- (NSDate *)alarmDate {
NSTimeInterval theTime = self.startTimeOfCurrentDay +
self.clockControl.time;

while(theTime < [NSDate timeIntervalSinceReferenceDate]) {
theTime += kSecondsOfDay / 2.0;
}
return [NSDate dateWithTimeIntervalSinceReferenceDate:
theTime];
}

Listing 3.56 Berechnung der absoluten Alarmzeit

Die Methode startTimeOfCurrentDay berechnet den Startzeitpunkt des Tages zu der aktuellen Uhrzeit. Dazu verwenden Sie einen Kalender, mit dem Sie das aktuelle Datum ohne Uhrzeit in einem NSDateComponents-Objekt berechnen lassen. Da alle Zeitkomponenten auf 0 stehen, hat dieses Objekt die Uhrzeit 0:00 Uhr. Sie brauchen es also nur über den Kalender wieder in ein NSDate-Objekt zurückrechnen zu lassen:

- (NSTimeInterval)startTimeOfCurrentDay {
NSCalendar *theCalendar = [NSCalendar currentCalendar];
NSDateComponents *theComponents =
[theCalendar components:NSYearCalendarUnit |
NSMonthCalendarUnit |
NSDayCalendarUnit
fromDate:[NSDate date]];
NSDate *theDate =
[theCalendar dateFromComponents:theComponents];

return [theDate timeIntervalSinceReferenceDate];
}

Listing 3.57 Berechnung des Startzeitpunktes des aktuellen Tages

Die Methode createAlarm in der Klasse AlarmClockViewController erzeugt nun mit Hilfe der Methode alarmDate eine neue Benachrichtigung. Bevor sie jedoch die neue Alarmzeit setzt, löscht sie eventuell noch vorhandene Benachrichtigungen aus der Warteschlange über cancelAllLocalNotifications.

const NSTimeInterval kSecondsOfDay = 60.0 * 60.0 * 24.0;

- (void)createAlarm {
UIApplication *theApplication =
[UIApplication sharedApplication];
UILocalNotification *theNotification =
[UILocalNotification new];

[theApplication cancelAllLocalNotifications];
theNotification.fireDate = [self alarmDate];
theNotification.timeZone = [NSTimeZone defaultTimeZone];
theNotification.alertBody = @"Aufwachen";
theNotification.soundName =
UILocalNotificationDefaultSoundName;
[theApplication scheduleLocalNotification:theNotification];
}

Listing 3.58 Erzeugung einer lokalen Benachrichtigung

Als Alarmton verwendet die Methode createAlarm den Systemton für Benachrichtigungen, indem sie der Property soundName die Konstante UILocalNotificationDefaultSoundName zuweist.

Damit haben Sie den Versand einer lokalen Benachrichtigung abgeschlossen. Denken Sie beim Ausprobieren der App aber daran, dass sie die Benachrichtigungen nicht anzeigt, wenn sie im Vordergrund läuft. Sie sollten die App also vor der Versendung über den Home-Button des Simulators in den Hintergrund schicken oder sie über Xcode stoppen.

Die App soll nach einem Neustart natürlich auch wieder die aktuelle Alarmzeit anzeigen. Dazu muss sie vor der Anzeige des Views die letzte Benachrichtigung auslesen und deren Zeit auf das Clock-Control und das Label für die Anzeige der Alarmzeit übertragen, was die Methode updateViews übernimmt. Die letzte Benachrichtigung ermitteln Sie über die NSArray-Methode lastObject und die Property scheduledLocalNotifications. Dabei liefert diese Methode nil, wenn das Array leer ist, was häufig sehr praktisch ist, da Sie dadurch Fallunterscheidungen vermeiden können.

- (void)updateViews {
UIApplication *theApplication =
[UIApplication sharedApplication];
UILocalNotification *theNotification = [[theApplication
scheduledLocalNotifications] lastObject];

if(theNotification == nil) {
self.alarmHidden = YES;
}
else {
self.alarmHidden = NO;
NSTimeInterval theTime = [theNotification.fireDate
timeIntervalSinceReferenceDate] –
self.startTimeOfCurrentDay;

theTime = remainder(theTime, kSecondsOfDay / 2.0);
self.clockControl.time = theTime < 0 ?
theTime + kSecondsOfDay / 2.0 : theTime;
}
[self updateTimeLabel];
}

Listing 3.59 Aktualisierung der Views aus den Benachrichtigungen

Die Methode updateViews muss die absolute Alarmzeit wieder in die relative Alarmzeit zurückrechnen. Dazu zieht sie zunächst die Startzeit des Tages von der absoluten Alarmzeit ab. Die C-Funktion remainder berechnet dabei den Divisionsrest von zwei Fließkommazahlen [Anm.: http://de.wikipedia.org/wiki/Division_mit_Rest#Verallgemeinerung:_Reelle_Zahlen] ; diese Funktion führt die Umkehrung zu der while-Schleife in der Methode alarmDate aus Listing 3.56 durch. Den berechneten Wert übergibt die Methode updateViews dann an das Clock-Control. Außerdem sorgt sie durch einen Aufruf von updateTimeLabel dafür, dass der Controller die Textanzeige aktualisiert. Um diese Aktualisierung zu aktivieren, müssen Sie die Methode noch aus der Methode viewWillAppear: aus aufrufen:

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

Listing 3.60 Aufruf der Aktualisierungsmethode


Rheinwerk Computing - Zum Seitenanfang

3.3.2Benachrichtigungen verarbeitenZur vorigen Überschrift

Durch eine lokale Benachrichtigung können Sie die oben besprochenen Ereignisse auslösen. Sie können damit indes nicht Ihre Applikation automatisch in den Vordergrund bringen. Entweder ist sie bereits im Vordergrund, oder es erscheinen abhängig von der Konfiguration eine Nachricht und die Badge-Nummer, und es ertönt ein Klang. Um die App zu aktivieren, muss der Nutzer den Button Anzeigen in der Alertbox betätigen oder das Banner antippen.

Wenn die App im Vordergrund läuft, informiert Cocoa Touch Sie direkt über das Auftreten einer lokalen Benachrichtigung. Das geschieht über die Methode application:didReceiveLocalNotification: im Application-Delegate. Das Betriebssystem ruft diese Methode auch auf, wenn die App nicht oder im Hintergrund lief und der Nutzer die App über die Nachricht aktiviert hat. Sie können diese Methode nutzen, um auch dann eine Nachricht anzuzeigen, wenn die App während des Nachrichtenempfangs aktiv ist. Mit dieser Methode können Sie also die Benachrichtigung manuell verarbeiten. Das Beispielprogramm soll eine Alertbox anzeigen und einen Ton abspielen.

Allerdings sollten Sie die Alertbox auch nur dann anzeigen, wenn die App bereits aktiv war. Andernfalls sähe der Nutzer ja zwei Hinweise auf den Alarm direkt hintereinander, was er zumindest als störend oder sogar fehlerhaft ansehen könnte. Um diesen Effekt zu vermeiden, können Sie den Zustand der App beim Auslösen der Benachrichtigung über die Property applicationState des Singletons UIApplication unterscheiden. Nur wenn die App den Zustand UIApplicationStateActive hat, soll sie den Alarm auslösen.

Die Nachricht zeigen Sie über eine Alertbox an. Dazu verwenden Sie die Klasse UIAlertView. Die Alertbox in Listing 3.61 zeigt nur die Nachricht und einen OK-Button an und lässt den Titel leer. Fügen Sie diese Implementierung der Methode application:didReceiveLocalNotification: in die Datei AlarmClockAppDelegate.m ein.

- (void)application:(UIApplication *)inApplication 
didReceiveLocalNotification:
(UILocalNotification *)inNotification {
if(inApplication.applicationState ==
UIApplicationStateActive) {
UIAlertView *theAlert =
[[UIAlertView alloc] initWithTitle:nil
message:inNotification.alertBody
delegate:nil cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[theAlert show];
[self playSound];
}
}

Listing 3.61 Anzeige einer Alertbox beim Empfang einer Benachrichtigung

Sie können den Systemton für lokale Benachrichtigungen leider nicht selbst in Ihrem Programm abspielen. Es gibt keine Funktion oder Methode, die genau diesen Klang ertönen lässt. Die Applikation soll stattdessen einen anderen Ton abspielen, was in der Methode playSound geschieht. Das Klingeln eines mechanischen Weckers passt hier sehr gut, und Sie finden die Datei ringtone.caf im Beispielprojekt auf der DVD. Sie können aber auch andere Töne verwenden, wenn sie im passenden Format vorliegen.

Die Töne müssen laut Dokumentation als 16-Bit-Little-Endian-PCM-Audiodaten im Core-Audio-Format mit der Endung .caf vorliegen, und der Ton darf nicht länger als 30 Sekunden sein. Unter OS X gibt es das Kommandozeilenprogramm afconvert, mit dem Sie eine Audiodatei in dieses Format konvertieren können. Der Aufruf dafür ist:

/usr/bin/afconvert -f caff -d LEI16 {Eingabedatei} {Ausgabedatei}

Dabei ist der Parameter {Eingabedatei} die Datei mit dem Ton im Ursprungsformat, und das Programm schreibt den konvertierten Klang in die Datei {Ausgabedatei}. Liegt der Klingelton für den analogen Wecker beispielsweise als WAV-Datei vor, konvertieren Sie ihn mit

/usr/bin/afconvert -f caff -d LEI16 ringtone.wav ringtone.caf

in eine CAF-Datei.

Bevor Sie den Klang verwenden können, müssen Sie ihn als Ressource zu Ihrem Projekt hinzufügen. Dazu ziehen Sie einfach die gewünschte Datei auf die Gruppe Supporting Files in der Navigatorspalte des Projekts. Sie sollten im folgenden Dialog darauf achten, dass Xcode die Datei auch in das Projekt kopiert und dem Target AlarmClock zuordnet. Setzen Sie dazu die Häkchen in den Checkboxen Destination | Copy items into destination groups folder (if needed) und Add to targets | AlarmClock. Sie können anschließend den Systemton bei der Erzeugung der lokalen Benachrichtigung auch durch die Sounddatei ersetzen, indem Sie den Dateinamen ohne Pfadangabe verwenden. Tauschen Sie dazu einfach die Zuweisung des Klangnamens in Listing 3.58 durch die folgende Anweisung aus:

theNotification.soundName = @"ringtone.caf";

Im Gegensatz zu dem Systemton können Sie diesen Klang auch über Funktionen aus der Audio-Toolbox abspielen, wozu Sie allerdings das entsprechende Framework einbinden müssen. Wählen Sie dazu zunächst das Target der App aus. Ein Xcode-Projekt kann mehrere Produkte (zum Beispiel Apps oder Frameworks) verwalten. Jedes Produkt hat dabei sein eigenes Target. Es legt die Dateien und Regeln zur Erstellung des jeweiligen Produkts fest. Sie erreichen das Target, indem Sie in der linken Navigatorspalte das Projekt auswählen. Daneben erscheint eine Spalte mit dem Abschnitt Targets. Wählen Sie dort das Target AlarmClock aus (siehe Abbildung 3.49, links).

Abbildung

Abbildung 3.49 So fügen Sie ein Framework zu einem Target hinzu.

Im Hauptfenster befindet sich dann ein Tabulator General mit dem Abschnitt Linked Frameworks and Libraries (Abbildung 3.49, unten Mitte). Dort können Sie dem Target über das Pluszeichen neue Frameworks hinzufügen. Selektieren Sie im Dialog den Eintrag AudioToolbox.framework, und klicken Sie auf den Button Add.

Wenn Sie ein Framework einbinden, fügt Xcode sowohl die Binärdateien mit den entsprechenden Klassen, Methoden und Funktionen als auch die entsprechenden Suchpfade für Headerdateien mit den Deklarationen zum Projekt hinzu. Dabei liegen die Headerdateien immer unterhalb eines virtuellen Ordners mit dem Namen des Frameworks, wie beispielsweise Foundation/Foundation.h oder UIKit/UIKit.h.

In der Regel hat jedes Framework von Apple eine Headerdatei mit dem Namen des Frameworks. Über diese Datei können Sie alle Header des Frameworks auf einmal einbinden. Da Sie die Audio-Toolbox im Application-Delegate verwenden möchten, müssen Sie dort den Header dieses Frameworks über die Direktive

#import <AudioToolbox/AudioToolbox.h>

einbinden.

Das Abspielen des Tons erfolgt in zwei Schritten. Zuerst muss ihn die App aus der Datei laden. Sie erhält dabei einen Verweis auf den Ton, mit dem sie ihn beliebig oft abspielen kann. Wenn sie ihn nicht mehr benötigt, kann sie den Ton über diesen Verweis auch wieder freigeben.

Der Verweis auf den Ton ist eine vorzeichenlose Zahl, die das Application-Delegate über die Property soundId verwaltet. Das Delegate soll den Ton jedoch nur dann laden, wenn es ihn braucht und sie ihn nicht bereits geladen hat. Dies erfolgt über den Lazy-Getter soundId. Diese Methode prüft zuerst, ob sie den Ton bereits geladen hat, und macht dies gegebenenfalls.

Lazy-Getter

Lazy-Getter sind eine einfache und effektive Möglichkeit für den sparsamen Umgang mit Ressourcen. Sie haben bereits ein weiteres Beispiel dafür kennengelernt. Die Property view der Klasse UIViewController lädt den View erst, wenn das Programm das erste Mal darauf zugreift. Das ist in der Regel kurz vor seiner Anzeige.

Lazy-Getter haben meist den gleichen, einfachen Aufbau:

- (MyClass *)myObject {
if(myObject == nil) {
myObject = ...;
}
return myObject;
}

Legen Sie zunächst eine anonyme Kategorie für die Klasse AlarmClockAppDelegate an, und deklarieren Sie darin die Property soundId, wie es Listing 3.62 zeigt. Für die Property soundId verwenden Sie anstatt des Typs NSUInteger die Klasse NSNumber. Das hat den Vorteil, dass Sie über den Property-Wert nil den Zustand des nicht geladenen Tons abbilden können.

@interface AlarmClockAppDelegate()

@property (nonatomic, strong) NSNumber *soundId;

@end

Listing 3.62 Property-Deklaration für die Sound-ID

Als Nächstes implementieren Sie den Lazy-Getter für die Property soundId. In dieser Methode müssen Sie auch den Wert der Property lesen, wofür Sie jedoch nicht den Getter über self.soundId aufrufen dürfen, da das zu einer Endlosrekursion führt. Bei einer Endlosrekursion ruft sich eine Methode so oft selbst auf, bis das Programm mit einem Crash abstürzt. Stattdessen müssen Sie über die Instanzvariable auf den Property-Wert zugreifen. Sofern Sie keine @synthesize-Anweisung für die Property verwenden, hat die Instanzvariable den Namen der Property mit einem Unterstrich als Präfix. Die Instanzvariable der Property soundId hat also den Namen _soundId.

- (NSNumber *)soundId {
if(_soundId == nil) {
NSURL *theURL = [[NSBundle mainBundle] URLForResource:
@"ringtone" withExtension:@"caf"];
SystemSoundID theId;

if(AudioServicesCreateSystemSoundID(
(__bridge CFURLRef) theURL, &theId) ==
kAudioServicesNoError) {
self.soundId = @(theId);
}
}
return _soundId;
}

Listing 3.63 Ton über einen Lazy-Getter laden

Wie Sie in Listing 3.63 sehen, dürfen Sie im Getter durchaus auch den Setter verwenden, da dieser eine eigenständige Methode ist und somit der Aufruf hier zu keiner Endlosrekursion führen kann.

Bundles

Unter iOS ist ein Programm ein Paket, das nicht nur den Binärcode des Programms, sondern auch dessen Ressourcen wie Bilder, Töne und andere Dateien enthält, die das Programm zur Ausführung braucht. Die Klasse NSBundle stellt in Cocoa Touch dieses Programmpaket dar und erlaubt den Zugriff auf die Ressourcen. Dabei liefert die Klassenmethode mainBundle das Bundle, das die Applikation repräsentiert. Über die Methode URLForResource:extension: erhalten Sie die URL auf eine Datei im Bundle.

Nun fehlt nur noch die Methode playSound, die den Ton abspielt.

- (void)playSound {
NSNumber *theId = self.soundId;

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

Listing 3.64 Laden und Abspielen eines Tons

Der Setter der Property soundId sollte den Wert nicht einfach überschreiben, ansonsten entsteht ein Speicherleck. Zwar gibt die App das alte NSNumber-Objekt frei, jedoch die Daten des Tons nicht. In Listing 3.65 erfolgt der lesende Zugriff auf den Wert auch über das Attribut und nicht über den Getter. Der ist hier ungeeignet, da er ja den Ton lädt. Diese Implementierung stellt außerdem sicher, dass das Delegate den alten Alarmton immer freigibt, wenn Sie einen neuen über den Setter setzen.

- (void)setSoundId:(NSNumber *)inSoundId {
if(soundId != inSoundId) {
if(soundId != nil) {
AudioServicesDisposeSystemSoundID(
[soundId unsignedIntValue]);
}
soundId = inSoundId;
}
}

Listing 3.65 Freigabe des Alarmtons im Setter

Da Sie nun beide Accessoren für die Property implementiert haben, legt der Compiler nicht mehr automatisch eine Instanzvariable für die Property an, und stattdessen zeigt Xcode bei jeder Verwendung von _soundId einen Fehler. Diese Fehler können Sie beheben, indem Sie manuell eine Instanzvariable über die Anweisung @synthesize anlegen lassen, die Sie am Anfang des Implementierungsblocks einfügen. Die entsprechende Anweisung ist in Listing 3.66 hervorgehoben.

@implementation AlarmClockAppDelegate

@synthesize soundId = _soundId;

Listing 3.66 Anlegen der Instanzvariablen

In der Methode dealloc sollten Sie auch den Ton wieder freigeben. Hierfür reicht ein Aufruf des Setters mit dem Wert nil völlig aus:

- (void)dealloc {
self.soundId = nil;
}

Listing 3.67 Freigabe des Alarmtons

Das Application-Delegate kann über die Methode applicationDidReceiveMemoryWarning: auch auf Speicherwarnungen reagieren. Da es ja den Alarmton jederzeit wieder laden kann, sollten Sie in dieser Methode den Property-Wert für soundId auf nil setzen, um den Ton freizugeben und dadurch Speicherplatz einzusparen.

- (void)applicationDidReceiveMemoryWarning: 
(UIApplication *)inApplication {
self.soundId = nil;
}

Listing 3.68 Freigabe des Alarmtons bei einer Speicherwarnung

Ressourcen sind vielfältig

Auch der Alarmton stellt eine Ressource des Programms dar, die nach der Terminologie aus Abschnitt 3.2.11, »Beliebige Objekte im Storyboard«, wiederherstellbar ist. Dabei besteht zwar die Referenz auf den Ton nur aus einigen Bytes, die Tondaten dürften aber wesentlich größer sein.



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