Holger Hinzberg
Bibliografische Information der Deutschen Nationalbibliothek
Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über <http://dnb.d-nb.de> abrufbar.
ISBN 978-3-8266-9289-5
1. Auflage 2014
www.mitp.de
E-Mail: kundenbetreuung@hjr-verlag.de
Telefon: +49 6221 / 489 -555
Telefax: +49 6221 / 489 -410
© 2014 mitp, eine Marke der Verlagsgruppe Hüthig Jehle Rehm GmbH Heidelberg, München, Landsberg, Frechen, Hamburg
Dieses Werk, einschließlich aller seiner Teile, ist urheberrechtlich geschützt. Jede Verwertung außerhalb der engen Grenzen des Urheberrechtsgesetzes ist ohne Zustimmung des Verlages unzulässig und strafbar. Dies gilt insbesondere für Vervielfältigungen, Übersetzungen, Mikroverfilmungen und die Einspeicherung und Verarbeitung in elektronischen Systemen.
Die Wiedergabe von Gebrauchsnamen, Handelsnamen, Warenbezeichnungen usw. in diesem Werk berechtigt auch ohne besondere Kennzeichnung nicht zu der Annahme, dass solche Namen im Sinne der Warenzeichen- und Markenschutz-Gesetzgebung als frei zu betrachten wären und daher von jedermann benutzt werden dürften.
Lektorat: Sabine Schulz
Korrektorat: Manfred Buchholz
electronic publication: III-satz, Husby, www.drei-satz.de
Dieses Ebook verwendet das ePub-Format und ist optimiert für die Nutzung mit dem iBooks-reader auf dem iPad von Apple. Bei der Verwendung anderer Reader kann es zu Darstellungsproblemen kommen.
Der Verlag räumt Ihnen mit dem Kauf des ebooks das Recht ein, die Inhalte im Rahmen des geltenden Urheberrechts zu nutzen. Dieses Werk, einschließlich aller seiner Teile, ist urheberrechtlich geschützt. Jede Verwertung außerhalb der engen Grenzen des Urheherrechtsgesetzes ist ohne Zustimmung des Verlages unzulässig und strafbar. Dies gilt insbesondere für Vervielfältigungen, Übersetzungen, Mikroverfilmungen und Einspeicherung und Verarbeitung in elektronischen Systemen.
Der Verlag schützt seine ebooks vor Missbrauch des Urheberrechts durch ein digitales Rechtemanagement. Bei Kauf im Webshop des Verlages werden die ebooks mit einem nicht sichtbaren digitalen Wasserzeichen individuell pro Nutzer signiert.
Bei Kauf in anderen ebook-Webshops erfolgt die Signatur durch die Shopbetreiber. Angaben zu diesem DRM finden Sie auf den Seiten der jeweiligen Anbieter.
Seit der Veröffentlichung der Version 4.3 ist die Installation von Xcode sehr einfach geworden, denn die Entwicklungsumgebung kann jetzt kostenlos direkt aus dem Mac App Store heruntergeladen werden. Ein zusätzliches Installationspaket, das in der Vergangenheit noch erforderlich war, wird jetzt nicht mehr benötigt. Sie finden Xcode am schnellsten über die Suchfunktion im Mac App Store.
Der Download und die Installation können ein wenig Zeit in Anspruch nehmen, denn mit einer Größe von fast 2 GB ist Xcode keine kleine Anwendung. Haben Sie ein wenig Geduld. Sobald die Entwicklungsumgebung auf Ihrem Rechner installiert wurde, kann es mit dem Programmieren losgehen. Sämtliche Beispiele in diesem Buch benötigen Xcode und es ist daher eine gute Idee, die Anwendung für einen schnellen Start aus dem Ordner Programme
in das Dock zu ziehen. Über das Kontextmenü dieser Verknüpfung können dann auch bereits verwendete Projekte schnell neu geladen werden.
Welchem Zweck dient das Zeichen @ bei der Verwendung von Zeichenketten?
Da Objective-C zur Sprache C kompatibel ist, können Anweisungen beider Programmiersprachen ohne großen Aufwand innerhalb desselben Programmcodes verwendet werden. Da es in beiden Sprachen jedoch teilweise ähnliche Typen gibt, sind spezielle Zeichen notwendig, um Verwechselungen zu vermeiden. Durch das Zeichen @ wird dem Compiler mitgeteilt, dass es sich bei dem folgenden Text um ein Objekt der Klasse NSString
und nicht um eine Zeichenkette der Sprache C handelt.
Eine Methode soll aufgerufen werden, sobald die zugehörige Schaltfläche vom Anwender berührt wird. Welches Ereignis einer IBAction
ist dazu am besten geeignet?
Soll eine Methode durch einfaches Berühren einer Schaltfläche aufgerufen werden, so ist das Ereignis Touch Down dafür am besten geeignet. Die Methode wird ausgeführt, noch bevor der Anwender den Button wieder loslässt.
Welche Aufgabe hat der Befehl #define
?
#define
in eine Präprozessoranweisung, ein Befehl direkt an den Compiler, im Programmcode eine Textersetzung vorzunehmen. Schlüsselwörter oder Bezeichner können mit #define
ersetzt oder entfernt werden, bevor der Compiler den Programmcode in Maschinensprache übersetzt.
Welche Methode wird nach dem Laden eines View Controllers automatisch vom System aufgerufen?
Nachdem ein View Controller geladen wurde, erhält dieser vom System die Nachricht viewDidLoad
, die mit der gleichnamigen Methode entgegengenommen werden kann. Beim Aufruf von viewDidLoad
sind alle IBOutlet
bereits mit den Steuerelementen verbunden, sodass man die Methode gut verwenden kann, um zusätzliche Anpassungen an der grafischen Oberfläche durchzuführen.
Wird ein iOS-Gerät oder der Simulator auf die Seite gedreht, dann versucht das System üblicherweise, die grafische Oberfläche den geänderten Seitenverhältnissen anzupassen. Wie kann dies verhindert werden?
Wird das Gerät gedreht, dann erhält der View Controller eine supportedInterfaceOrientations
-Nachricht, mit der das System abfragt, ob die grafische Oberfläche den veränderten Seitenverhältnissen angepasst werden soll. Über den Rückgabewert der Methode supportedInterfaceOrientations
lassen sich bestimmte Ausrichtungen, zum Beispiel das Querformat (englisch: Landscape), verhindern. Die grafische Oberfläche würde sich dann nicht ändern, auch wenn das Gerät gedreht wird.
Mit welcher Anweisung kann die virtuelle Tastatur eines UITextField
-Steuerelements ausgeblendet werden?
Erhält ein aktives Eingabefeld die Nachricht resignFirstResponder
, dann wird es nicht weiter auf Eingaben des Anwenders reagieren und die Bildschirmtastatur wird automatisch ausgeblendet.
Welche Nachricht muss an welchen Empfänger gesendet werden, um in einem View Controller die Eingaben für sämtliche Eingabefelder zu beenden?
Wird die Nachricht endEditing:YES
an den View der grafischen Oberfläche gesendet, dann werden alle Eingabefelder inaktiv und die virtuelle Tastatur verschwindet.
Der Hintergrund der grafischen Oberfläche soll auf Berührungen des Anwenders reagieren und, ähnlich wie eine Schaltfläche, eine Methode aufrufen. Was ist dafür zu tun?
Der Hintergrund der grafischen Oberfläche ist für gewöhnlich ein Objekt der Klasse UIView
und kann somit nicht mit IBAction
im Controller verbunden werden. Dies funktioniert erst, wenn man die Klassenidentität zu UIControl
ändert. UIControl
ist eine von UIView
abgeleitete Klasse und kann alle ihre Aufgaben übernehmen. Zusätzlich kann ein UIControl
mit IBAction
verbunden werden.
Mit welcher Methode lassen sich Zeichen innerhalb einer Zeichenkette vom Typ NSString
ersetzen?
Die Methode replaceOccurrencesOfString:withString:options:range:
ermöglicht es, Zeichen in einer Zeichenkette zu ersetzen. Über den Parameter range
wird bestimmt, in welchem Bereich des Textes dieser Austausch geschehen soll. Die options
ermöglichen es, die Groß- und Kleinschreibung der Buchstaben bei der Ersetzung zu ignorieren.
Ein NSNumberFormatter
ermöglicht es, Zahlen in formatierte Zeichenketten umzuwandeln. Über welche Eigenschaften wird die Anzahl der Nachkommastellen konfiguriert?
Die Klasse NSNumberFormatter
verfügt über zwei Eigenschaften, mit denen die Nachkommastellen formatiert werden können. minimumFractionDigits
und maximumFractionDigits
bestimmen die minimale und maximale Anzahl der Nachkommastellen. Sind beide Werte identisch, dann wird die Zahl, unabhängig von ihrem Wert, immer mit der gleichen Anzahl von Nachkommastellen formatiert.
Wie kann eine Szene aus einem Storyboard in einen Navigation Controller eingebettet werden?
Über die Menüpunkte Editor, Embed In und Navigation Controller lässt sich die markierte Szene in einen Navigation Controller einbetten. Gibt es weitere Szenen, die durch einen Segue mit dem Style Push verbunden sind, dann werden diese ebenfalls mit in die Navigation einschlossen.
Welche Methode wird aufgerufen, wenn der Speicherplatz eines View Controllers freigegeben werden soll?
Bevor sein Speicherplatz endgültig freigegeben wird, erhält ein Objekt eine dealloc
-Nachricht, die mit der gleichnamigen Methode entgegengenommen werden kann. In dealloc
bekommt das Objekt Gelegenheit, verbundene Ressourcen freizugeben oder laufende Vorgänge abzubrechen.
Wie können in der Methode prepareForSegue:
die View Controller des auszuführenden Übergangs ermittelt werden?
Der als Parameter übergebene UIStoryboardSegue
ermöglicht über seine Eigenschaften destinationViewController
und sourceViewController
den Zugriff auf die Controller der beiden verbundenen Szenen.
Wie lässt sich eine Eigenschaft definieren, mit der auf ein beliebiges Objekt verwiesen werden kann, das aber ein bestimmtes Protokoll umsetzt?
Ist eine Eigenschaft vom Typ id
, dann kann dieser Speicherzeiger auf ein beliebiges Objekt verweisen. Die zusätzliche Angabe eines Protokolls in spitzen Klammern schränkt die Auswahl allerdings wieder ein. Jetzt werden nur noch Objekte, die das angegebene Protokoll implementieren, vom Compiler ohne Warnungen akzeptiert.
Wie unterscheidet sich ein Bar Button Item von einer gewöhnlichen Schaltfläche?
Ein Bar Button Item kann nur innerhalb einer Navigationsleiste platziert werden und ermöglicht auch keine unterschiedlichen Events wie Touch Down oder Touch Drag Enter. Eine verbundene Methode wird erst ausgeführt, nachdem der berührte Button wieder losgelassen wurde.
Über welche Eigenschaft kann der aktuelle Wert eines Schieberegler-Steuerelements ermittelt werden?
Die Eigenschaft value
vom Typ float
ermöglicht es, den Wert eines UISlider
auszulesen und zu setzen.
Eine Szene in einem Storyboard soll einem Tableisten-Controller untergeordnet werden. Welcher Typ von Segue ist dafür erforderlich?
Ein Segue vom Typ Relationship muss verwendet werden, um einen View Controller nachträglich einem Tableisten-Controller unterzuordnen.
Was ist die Besonderheit bei Grafiken, die innerhalb der Schaltflächen einer Tableiste verwendet werden?
Bei Grafiken, die in einer Tableiste zum Einsatz kommen, wird in der Regel auf unterschiedliche Farben verzichtet und ausschließlich mit schwarzer Farbe auf einem transparenten Hintergrund gearbeitet. In der App wird dieses Bild dann zu einem Negativ der ursprünglichen Grafik.
Wie ist es möglich, dass ohne Vererbung unterschiedliche Typen mit der delegate
-Eigenschaft eines Objekts verbunden werden können?
Die delegate
-Eigenschaft ist vom Typ id
und ermöglicht somit Zeiger auf jedes beliebige Objekt. Um eine fehlerfreie Kommunikation zu gewährleisten, kommen Protokolle zum Einsatz, in denen die Methodensignaturen passend zu den gesendeten Nachrichten definiert werden.
Wie unterscheidet sich die Instanziierung eines Steuerelements durch Anweisungen im Code von der Instanziierung der meisten anderen Klassen?
Wird ein Steuerelement im Programmcode angelegt, dann erwartet die Methode initWithFrame:
ein CGRect
als Parameter. Über diese Struktur wird die Größe und Position des Objekts auf der grafischen Oberfläche bestimmt.
Wie kann eine im Programmcode angelegte Schaltfläche mit einer Methode verbunden werden, die ausgeführt wird, wenn der Button betätig wurde?
Die Anweisung addTarget: action: forControlEvents:
ermöglicht es, Steuerelemente mit Methoden zu verbinden. Der Parameter ControlEvent
bestimmt dabei, bei welchem Ereignis die Methode aufgerufen werden soll.
Worin besteht der Unterschied zwischen den Klassen NSDictionary
und NSMutableDictionary
?
Im Gegensatz zu einem NSMutableDictionary
können zu einem NSDictionary
nach der Initialisierung keine zusätzlichen Wertepaare mehr hinzugefügt werden. Benötigt man ein Wörterbuch, dem zur Laufzeit der App weitere Objekte und Schlüssel angehängt werden können, dann ist ein Objekt der Klasse NSMutableDictionary
erforderlich.
Wie lässt sich bei einem Vergleich von zwei Zeichenketten die Groß- und Kleinschreibung ignorieren?
Soll bei dem Vergleich von zwei NSString
-Objekten die Groß- und Kleinschreibung vernachlässigt werden, dann müssen die Objekte mit caseInsensitiveCompare:
anstelle von compare:
überprüft werden.
Worin besteht der Unterschied zwischen den Debugger-Funktionen Step Over und Step Into?
Wird ein Projekt in Einzelschritten durchlaufen, dann behandelt die Funktion Step Over Methodenaufrufe wie einzelne Anweisungen und führt diese in nur einem Schritt aus. Step Into springt stattdessen in die Methoden und der Programmablauf bleibt dort bei der ersten Anweisung erneut stehen.
Was ist ein Exception Breakpoint?
Ein Exception Breakpoint veranlasst die Entwicklungsumgebung, bei einem Fehler an genau der Anweisung zu unterbrechen, die den Fehler ausgelöst hat. Ohne einen solchen Breakpoint passiert dies nicht und Xcode wird Fehlermeldungen stattdessen in der main
-Methode anzeigen.
Worin besteht der Unterschied zwischen frame
und bounds
?
Neben der Größe eines Views beschreibt frame
über die Eigenschaft origin
die Position des Objekts in seinem übergeordneten View. Der origin
kann diese Information nicht liefern. Dort sind die bounds
immer 0,0
.
Mit welchen Funktionen kann der grafische Context gesichert und wiederhergestellt werden?
Der Aufruf der Funktion CGContextSaveGState
speichert die aktuellen Einstellungen des grafischen Contexts. Nach Änderungen kann diese Konfiguration mit CGContextRestoreGState
wiederhergestellt werden.
Ein UITapGestureRecognizer
soll seine zugehörige Methode nicht bei einer einfachen Berührung aufrufen, sondern erst nach einem dreifachen Antippen des Views. Wie ist dies zu konfigurieren?
Der UITapGestureRecognizer
verfügt über eine Eigenschaft mit dem Namen numberOfTapsRequired
. Mit ihr kann bestimmt werden, wie viele Berührungen erforderlich sind, um die zugewiesene Methode aufzurufen.
Was versteht man in der iOS-Entwicklung unter dem Begriff »Superview«?
Der einem View übergeordnete View wird als Superview des Ersteren bezeichnet. Ist ein Button oder ein anderes Steuerelement in der grafischen Oberfläche einer Anwendung eingebettet, so ist diese Ansicht für den Button der Superview. Untergeordnete Objekte werden dagegen gelegentlich als Subview bezeichnet.
Wie lassen sich die einzelnen Farbanteile eines UIColor
-Objekts auslesen?
Über die Methode getRed:green:blue:alpha:
der Klasse UIColor
können die Farbanteile ermittelt werden. Allerdings liefert die Methode keinen Rückgabewert, sondern erwartet float
-Variablen, die bei einem Aufruf als Referenz übergeben werden. Die Methode schreibt die Werte direkt in die Speicheradressen der float
-Variablen.
Welche beiden Verbindungen müssen erstellt werden, nachdem ein UIPickerView
zur grafischen Oberfläche hinzugefügt wurde?
Um den vollen Funktionsumfang eines Pickers nutzen zu können, müssen dem Steuerelement ein Delegate (delegate
) und eine Datenquelle (dataSource
) zugewiesen werden. Sehr oft übernimmt diese Aufgabe der View Controller, der auch für die Steuerung der Szene verantwortlich ist.
Wie lässt es sich vermeiden, dass ein Daten-Model ständig von einem View Controller in einen anderen übergeben werden muss?
Wird das Daten-Model einer Anwendung in unterschiedlichen View Controllern benötigt, dann kann es in AppDelegate
hinterlegt werden. Jede Klasse hat über den Aufruf von sharedApplication
auch Zugriff auf den Delegate von UIApplication
.
Wie können innerhalb einer iOS-App Grafiken verwendet werden, die für die Darstellung in einem Retina-Display optimiert sind?
Möchte man für das Retina-Display optimierte Bilder verwenden, so benötigt man zusätzliche Grafiken mit der doppelten Anzahl von Pixeln für Höhe und Breite. Durch den Zusatz @2x
beim Dateinamen der hochauflösenden Grafiken kann das System diese Bilder erkennen und wird diese, sofern ein Retina-Display vorhanden ist, automatisch verwenden.
Welches Steuerelement kann für die Anzeige von Bildern und Grafiken eingesetzt werden?
Das UIImageView
-Steuerelement übernimmt die Darstellung von Bildern.
Was versteht man unter dem Begriff »Layer«?
Sämtliche Steuerelemente zeichnen nicht direkt auf die grafische Oberfläche, sondern auf einen unsichtbaren Speicherbereich, der als Layer bezeichnet wird. Erst wenn das System es für nötig erachtet, die Oberfläche zu aktualisieren, werden die auf dem Layer gezeichneten Informationen zur Anzeige gebracht.
Wie kann die Höhe einer Tabellenzelle geändert werden?
Über den Rückgabewert der Delegate-Methode tableView:heightForRowAtIndexPath:
kann die Höhe einer Zelle beeinflusst werden. Ein alternativer Weg führt über direkte Anpassungen im Interface Builder.
Was muss beachtet werden, wenn der Radius der Ecken eines Steuerelements verändert werden soll?
Die Eigenschaft cornerRadius
ist nur wirksam, wenn zusätzlich die Eigenschaft masksToBounds
auf true
gesetzt wird. Beide Eigenschaften stehen auch nur zur Verfügung, wenn zuvor das Quartz Core Framework importiert wurde.
Welche Aufgabe übernimmt der Typ UIEdgeInsets
bei der Konfiguration eines Collection Views?
Mit einem UIEdgeInsets
werden in einem UICollectionView
die Abstände der Zellen konfiguriert.
Wie kann im Interface Builder die Farbe der Navigationsleiste geändert werden?
Der Stile und die Farben können im Attributes Inspector geändert werden, nachdem die Navigationsleiste ausgewählt wurde.
Bei einem UIActivityViewController
soll die Möglichkeit des Druckens und Weiterleitens auf bestimmte Dienste beschränkt werden. Wie lässt sich dies konfigurieren?
An die Eigenschaft excludedActivityTypes
des UIActivityViewController
kann eine Liste mit auszuschließenden Diensten und Funktionen übergeben werden.
Wie kann ein Block parallel zum Hauptthread der Anwendung ausgeführt werden?
Die Programmierschnittstelle Grand Central Dispatch (GCD) ermöglicht es, Codeblöcke einer vom System verwalteten Warteschlange hinzuzufügen. Diese Blöcke können durch den Aufruf von dispatch
parallel zum Hauptthread ausgeführt werden.
Was ist zu beachten, wenn man aus einem Block heraus die grafische Oberfläche eines View Controllers bearbeiten möchte?
Steuerelemente der grafischen Oberfläche können nur vom Hauptthread der Anwendung verändert werden. Wird ein Block mit GCD parallel zu diesem Thread ausgeführt, dann sind keine Änderungen möglich. Es ist aber ohne Weiteres möglich, innerhalb eines Blocks einen weiteren Block zu erzeugen, der Anweisungen im Hauptthread ausführt. So können auch parallel laufende Prozesse ihre Ergebnisse auf der grafischen Oberfläche anzeigen.
Mit welcher Funktion lässt sich der kleinere von zwei float
-Werten ermitteln?
Die Funktion fmin
liefert den kleineren von zwei float
oder double
-Werten. Mit fmax
kann der größere Wert ermittelt werden.
Durch welchen Aufruf kann ein NSTimer
ausgelöst werden, noch bevor das festgelegte Intervall abgelaufen ist?
Durch den Aufruf von fire
wird die dem Timer zugewiesene Methode sofort aufgerufen.
Über welche Nachricht kann die grafische Ausgabe eines UIView
-Objekts aktualisiert werden?
Die Nachricht setNeedsDisplay
veranlasst ein UIView
-Objekt, sich neu zu zeichnen.
Was versteht man unter String Dependencies und warum sind diese problematisch?
Wird ein Methodenname oder eine Eigenschaft im Programmcode als Zeichenkette verwendet, dann spricht man von sogenannten String Dependencies, den Abhängigkeiten von Zeichenketten. Da der Compiler Zeichenketten nicht untersucht, können sich durch String Dependencies Fehler in eine Anwendung einschleichen, die erst zur Laufzeit der App bemerkt werden. Ein typisches Beispiel für eine solche Abhängigkeit ist der Identifier einer Tabellenzelle, dessen Bezeichnung im Interface Builder mit dem Namen im Code übereinstimmen muss.
Mit welcher KVC-Aggregatfunktion kann der durchschnittliche Wert einer Auflistung ermittelt werden?
Die Aggregatfunktion @avg
liefert den durchschnittlichen Wert. Mit @min
und @max
können dementsprechend der minimale und der maximale Wert ermittelt werden.
Welchen Vorgang bezeichnet man als Reverse Geocoding?
Beim Reverse Geocoding werden zu einer Position, angegeben als Längen- und Breitengrad, Standortinformationen ermittelt. Diese können, neben einem Ort mit Postleitzahl, sogar eine Straße mit Hausnummer enthalten.
Was bezeichnet man im Map Kit Framework als Callout?
Markierte Orte auf einem MKMapView
-Steuerelement können durch die Berührung eines Nutzers kleine »Fähnchen« anzeigen, die als Callout bezeichnet werden. Oft werden Callouts eingesetzt, um zur gewählten Position zusätzliche Informationen anzuzeigen oder weitere Funktionen auszuführen.
Was ist bei der Auswahl der Genauigkeit eines CLLocationManager
zu beachten?
Die Genauigkeit eines CLLocationManager
-Objekts hat Auswirkungen auf die Akkulaufzeit des iOS-Gerätes, denn über sie wird auch bestimmt, wie das Gerät seine Position ermittelt. Sehr genaue Positionsangaben aktivieren den GPS-Sensor, der bei einer gröberen Standortbestimmung nicht benötigt wird.
Was ist bei Änderungen an Core-Data-Entitäten zu beachten?
Wird eine Entität verändert, dann muss nicht nur ihre Klasse, sondern auch die Klassen aller mit ihr verbundenen Entitäten neu generiert werden.
Welche Aufgabe haben die Löschregeln?
Sind Entitäten über Beziehungen miteinander verbunden, so bestimmen die Delete Rules, was bei der Löschung eines Objekts mit den verbundenen Objekten geschehen soll. Die Objekte erhalten oder ebenfalls löschen sind zwei mögliche Konfigurationen. Es ist jedoch auch möglich, dass ein Objekt gar nicht entfernt werden kann, solange es noch mit anderen Objekten verbunden ist.
Wie behandelt das Betriebssystem eine lokale Benachrichtigung, wenn die zugehörige App bereits aktiv ist?
Ist eine Anwendung beim Eintreffen einer ihr zugeordneten Benachrichtigung aktiv, wird vom Betriebssystem keine Mitteilung angezeigt. Stattdessen wird in der Anwendung in der Klasse AppDelegate
die Methode application: didReceiveLocalNotification:
aufgerufen und ein UILocalNotification
-Objekt übergeben. Die weitere Auswertung ist Aufgabe des App-Entwicklers.
Wie können lokale Benachrichtigungen, die von einer Anwendung an das Betriebssystem übergeben wurden, wieder gelöscht werden?
Über die Nachricht scheduledLocalNotifications
, gesendet an das sharedApplication
-Objekt, können sämtliche Nachrichten einer App vom System abgefragt werden. Löschen lassen sich Nachrichten durch den Aufruf von cancelLocalNotification:
. Zur Identifizierung einer Nachricht kann die Eigenschaft userInfo
der Klasse UILocalNotification
verwendet werden.
Für tragbare Computer und Mobiltelefone war die Akkulaufzeit schon immer eine kritische Größe, die nicht selten auch über den finanziellen Erfolg eines Gerätes entschieden hat. Ein Anwender möchte seine neu gewonnene Unabhängigkeit vermutlich ungern wieder aufgeben, weil sein Telefon oder sein Rechner zu oft nach einer Steckdose und einem Ladegerät verlangt. Für die Hersteller war es daher eine große Herausforderung, zusätzlich zu umfangreichen Funktionen der Hardware und des Betriebssystems, möglichst lange Akkulaufzeiten zu gewährleisten. Die Problemlösung erwies sich dabei als recht einfach, und so ist es nicht verwunderlich, dass viele Hersteller ein ähnliches System anwenden: Man darf Software nicht mehr so einsetzen, wie man es von Desktop-Computern her gewohnt ist.
Im Gegensatz zu den Anwendungen auf Desktop-Rechnern gibt es auf einem Mobiltelefon in der Regel nur wenige Programme, die ständig aktiv sind. Wird eine App in den Hintergrund gedrängt, zum Beispiel weil der Nutzer sich einem anderen Programm zuwendet, dann gibt es im Betriebssystem Mechanismen, um die jetzt unbenutzte App zu beenden oder »schlafen zu schicken«. So lässt sich sicherstellen, dass die Prozessorleistung auf ein Minimum reduziert wird. Durch geringere Last wird auch der Akku weniger belastet und der Anwender kann länger mit seinem Gerät arbeiten. Kehrt der Nutzer zu einer zuvor verwendeten App zurück, dann wird das Programm »wiedererweckt« und vom Betriebssystem in den ursprünglichen Zustand zurückversetzt. In unseren Projekten werden wir durch verschiedene Methodenaufrufe in der Klasse AppDelegate
über die beschriebenen Zustandsänderungen informiert. Wird eine App vom Anwender durch einen Druck auf den Home-Button beendet, erhält das Programm zuvor eine applicationWillResignActive:
-Nachricht. applicationWillEnterForeground:
wird dagegen aufgerufen, wenn eine inaktive Anwendung erneut aktiviert wird.
Was auf den ersten Blick nach einer guten Idee klingt, um Energie einzusparen, bringt jedoch einige Probleme mit sich. Gelegentlich benötigen Anwender Programme, die eben doch ununterbrochen ausgeführt werden müssen. Zumindest scheint es so, wenn man den Ansätzen der Entwicklung von Desktop-Anwendungen folgt. Ein gutes Beispiel sind Apps wie Kalender oder To-Do-Listen, in die der Anwender Termine einträgt, an die ihn sein Gerät zum vorgegebenen Zeitpunkt erinnern soll. Dies macht es zweifellos erforderlich, permanent oder zumindest in angemessenen zeitlichen Intervallen das aktuelle Datum zu ermitteln, um anschließend zu prüfen, ob der zu kontrollierende Termin erreicht wurde. Mit einer Anwendung, die nicht ausgeführt wird, funktioniert das natürlich nicht.
Ein Programm, das auf einem iPhone oder einem anderen iOS-Gerät ständig ausgeführt wird, ist das Betriebssystem. Schließlich erwartet man von einem Telefon Reaktionen auf eingehende Anrufe und Nachrichten, und daher sollte das System, selbst bei inaktivem Bildschirm, niemals vollständig »schlafen«. Die Aufgabe, den Anwender an fällige Termine zu erinnern, könnte das Betriebssystem daher auch übernehmen, sofern es von den verschiedenen Apps die dafür nötigen Informationen erhält. Dieser in der Theorie leicht nachvollziehbare Ansatz wurde von Apple in der Version 4.0 des iOS umgesetzt. Die Klasse UILocalNotification
aus dem UIKit Framework ermöglicht es, genau wie beschrieben, Nachrichten im Betriebssystem zu hinterlegen, die dem Anwender zu zuvor bestimmten Terminen zugestellt werden.
Die Konfiguration eines UILocalNotification
-Objekts ist nicht kompliziert und ähnelt ein wenig der Programmierung eines UIAlertView
. Bei der Handhabung der Nachrichten gibt es allerdings einige Besonderheiten, die wir uns in einem neuen Beispiel genauer ansehen sollten. Nennen Sie das Projekt LocalNotification
und erweitern Sie die grafische Oberfläche um eine Schaltfläche, die eine Methode mit dem Namen notificationButtonTouched:
im View Controller aufruft. Innerhalb der Methode werden wir die Benachrichtigung anschließend konfigurieren.
- (IBAction)notificationButtonTouched:(id)sender { }
Auf dem Weg zur Benachrichtigung müssen wir im ersten Schritt ein UILocalNotification
-Objekt anlegen und initialisieren und diesem anschließend den anzuzeigenden Text mit der Eigenschaft alertBody
zuweisen. Über das Property fireData
wird der Termin bestimmt, an dem die Nachricht angezeigt werden soll. Der Einfachheit halber berechnen wir hier einen Zeitpunkt, der nur 15 Sekunden in der Zukunft liegt. Eine Zeitzone, die Eigenschaft timeZone
, müsste in diesem Beispiel eigentlich nicht zugewiesen werden, denn die 15 Sekunden sind doch sehr eindeutig. Wird für eine Benachrichtigung allerdings eine konkrete Uhrzeit bestimmt, dann sollte man unbedingt sicherstellen, dass die eingegebene Zeit mit der Zeitzone des Systems übereinstimmt. Ist die Nachricht konfiguriert, dann muss sie nur noch an das Betriebssystem übergeben werden. Diese Aufgabe übernimmt die Anweisung scheduleLocalNotification:
.
- (IBAction)notificationButtonTouched:(id)sender { UILocalNotification *localNote = [[UILocalNotification alloc] init]; // Der anzuzeigende Text localNote.alertBody = @"Ein wichtiger Termin ist fällig"; // Der Termin, an dem die Nachricht angezeigt werden soll localNote.fireDate = [NSDate dateWithTimeIntervalSinceNow:15]; // Die Zeitzone localNote.timeZone = [NSTimeZone defaultTimeZone]; // Die Nachricht an das Betriebssystem übergeben [[UIApplication sharedApplication] scheduleLocalNotification:localNote]; }
Kompilieren und starten Sie die Anwendung. Berühren Sie anschließend den Button und warten Sie 15 Sekunden. Zu Ihrer vielleicht großen Verwunderung wird nichts geschehen und das System wird keine Nachricht anzeigen. Die von uns erzeugte UILocalNotification
wird nämlich nur dann angezeigt, wenn die zugehörige Anwendung inaktiv ist. Wenn Sie die Schaltfläche aktivieren und anschließend die App mit dem Home-Button verlassen, dann bekommen Sie nach 15 Sekunden eine Nachricht zu sehen. Diese wird allerdings nur kurz eingeblendet.
Sämtliche Nachrichten werden vom Betriebssystem in der Mitteilungszentrale verwaltet, die durch eine Wischgeste vom oberen Bildschirmrand nach unten angezeigt werden kann. Mit dieser Geste, die auch im Simulator funktioniert, erhält der Anwender schnell eine Zusammenfassung sämtlicher Benachrichtigungen.
Die Schwachstelle des Systems wird vermutlich sehr schnell deutlich. Zwar sind wir jetzt in der Lage, den Anwender zu informieren, wenn unsere App selbst nicht mehr läuft, bei einem aktiven Programm werden jedoch keine Benachrichtigungen angezeigt. Arbeitet der Nutzer mit dem Kalender, während ein Termin fällig wird, dann würde er über diese Tatsache also gar nicht informiert. Glücklicherweise sind die Umstände jedoch nicht so schlimm, wie sie auf den ersten Blick erscheinen. Die hinterlegte Benachrichtigung wird vom Betriebssystem weiterhin verarbeitet, allerdings erkennt das System, dass die betreffende App aktiv ist, und leitet die Nachricht entsprechend weiter. Solange unser Programm läuft, sind wir daher selbst dafür verantwortlich, auf Benachrichtigungen zu reagieren und diese bei Bedarf auch anzuzeigen. Beim Eintreffen einer lokalen Benachrichtigung zur Laufzeit wird unser AppDelegate
mit der Nachricht application: didReceiveLocalNotification:
informiert, die wir mit einer geeigneten Methode entgegennehmen können. Dort erhalten wir sogar unser zuvor angelegtes UILocalNotification
-Objekt zurück.
- (void)application:(UIApplication *)application didReceiveLocalNotification: (UILocalNotification *)notification { // Den eigenen Programmnamen ermitteln NSBundle *bundle = [NSBundle mainBundle]; NSDictionary *info = [bundle infoDictionary]; NSString *progName = [info objectForKey:@"CFBundleDisplayName"]; // Einen Alert View mit der Benachrichtigung anzeigen UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:progName message:notification.alertBody delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alertView show]; }
Die ersten drei Anweisungen im Listing ermöglichen es uns, aus dem Bundle der Anwendung den Programmnamen zu ermitteln. So wird die Methode etwas flexibler und der Code muss nicht angepasst werden, falls wir unsere LocalNotification
-App zu einem späteren Zeitpunkt umbenennen möchten.
In den meisten Fällen wird Ihre App beim Eintreffen einer Nachricht nicht aktiv sein und das System wird die Benachrichtigung anzeigen. Allerdings gibt es auch in dieser Situation die Möglichkeit, Informationen an die Anwendung weiterzureichen. Berührt der Anwender die Nachricht auf dem Bildschirm, wird die zugehörige inaktive App neu gestartet und wir können die Benachrichtigung in der Methode application: didFinishLaunchingWithOptions:
aus dem Parameter launchOptions
auslesen.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions { UILocalNotification *notification = [launchOptions objectForKey: @"UIApplicationLaunchOptionsLocalNotificationKey"]; if (notification) { NSLog(@"%@", notification.alertBody); } return YES; }
Da man selten genau weiß, wie inaktiv eine App tatsächlich ist – eine kurz zuvor unterbrochene Anwendung kann im Hintergrund noch laufen – sollte man Nachrichten in den beiden Methoden application: didReceiveLocalNotification:
und zusätzlich application: didFinishLaunchingWithOptions:
entgegennehmen. Ist das Programm noch im Hintergrund aktiv, dann werden Benachrichtigungen von der zweiten Methode verarbeitet. Muss die App jedoch komplett neu gestartet werden wird, so wird bei jedem Programmstart aus der Implementierungsdatei AppDelegate.m
die Methode application: didFinishLaunchingWithOptions:
aufgerufen.
Über weitere Eigenschaften lässt sich eine UILocalNotification
noch genauer konfigurieren. Die Eigenschaft soundName
bestimmt ein akustisches Signal, das zusammen mit der Nachricht ausgegeben werden soll, und über applicationIconBadgeNumber
kann der im Badge angezeigte Wert erhöht werden. Unter einem Badge versteht man die kleinen roten Erweiterungen des App-Icons, mit denen Programme wie beispielsweise Mail, die Anzahl der ungelesenen Nachrichten anzeigen.
... UILocalNotification *localNote = [[UILocalNotification alloc] init]; localNote.alertBody = @"Ein wichtiger Termin ist fällig"; localNote.fireDate = [NSDate dateWithTimeIntervalSinceNow:15]; localNote.timeZone = [NSTimeZone defaultTimeZone];// Zusätzliche Konfiguration:
// Ein akustisches Signal ausgeben
localNote.soundName = UILocalNotificationDefaultSoundName;
// Den Badge-Wert erhöhen
localNote.applicationIconBadgeNumber += 1;
...
Soll Ihre App den Anwender mehrmals an denselben Termin erinnern, so können Sie dieses Verhalten über die Eigenschaft repeatInterval
konfigurieren. Sie können bestimmen, nach welcher Zeitspanne die Nachricht erneut angezeigt wird, Von NSSecondCalendarUnit
bis zu NSYearCalendarUnit
bietet des Framework eine großzügige Auswahl an unterschiedlichen Zeitabständen. Um für die Nachricht ein Intervall zu bestimmen, genügt eine einfache Zuweisung:
// Die Benachrichtigung jede Stunde wiederholen localNote.repeatInterval = NSHourCalendarUnit;
Besondere Aufmerksamkeit sollte man bei der Konfiguration auch dem Parameter userInfo
schenken, über den sich zusammen mit der Nachricht weitere Daten übermitteln lassen. Nicht selten verwendet man dort ein Dictionary.
...// Weitere Daten mithilfe der userInfo übertragen
NSDictionary *dic
= @{@"ID":@"AB12", @"Ereignis":@"Gartenparty",
@"Ort":@"Bei Georg" };
localNote.userInfo = dic;
// Die Nachricht an das Betriebssystem übergeben [[UIApplication sharedApplication] scheduleLocalNotification:localNote];
Besonders nützlich erweist sich das Dictionary, wenn es darum geht, eine an das Betriebssystem übergebene Benachrichtigung wieder zu entfernen. Löscht der Anwender einen Termin aus seinem Kalender, dann möchte er vermutlich auch in Zukunft nicht mehr daran erinnert werden. Um korrekt zu funktionieren, benötigt die Methode cancelLocalNotification:
als Parameter allerdings genau das UILocalNotification
-Objekt, welches zuvor an das System weitergereicht wurde. Die einfachste Lösung ist es daher, zunächst alle der App zugeordneten Benachrichtigungen vom Betriebssystem abzufragen und die gewünschte Nachricht anschließend in dieser Auflistung zu suchen. Haben Sie zur Identifikation einer Nachricht oder eines Termins eine eindeutige Bezeichnung verwendet, dann kann die gesuchte UILocalNotification
mit geringem Aufwand aufgespürt werden.
// Sämtliche Benachrichtigungen der App holen NSArray* localNotifications = [[UIApplication sharedApplication] scheduledLocalNotifications]; // Mit einer Schleife alle Benachrichtigungen kontrollieren for (UILocalNotification *notification in localNotifications) { // Den zum Schlüssel "ID" gehörenden Wert // im Dictionary userInfo suchen NSString* notificationID = [notification.userInfo objectForKey:@"ID"]; if ([notificationID isEqualToString:@"AB12"]) { // Die Benachrichtigung entfernen [[UIApplication sharedApplication] cancelLocalNotification:notification]; break; } }
Auch ohne lokale Benachrichtigungen können Sie mit nur geringem Aufwand im Icon Ihrer App ein Badge anzeigen. Die folgende Anweisung zeigt dies beispielhaft für die Nummer 99
:
// Ein Badge manuell setzen [[UIApplication sharedApplication] setApplicationIconBadgeNumber:99];
Der für das Badge verwendete Datentyp ist ein NSInteger
und erlaubt somit auch die Verwaltung von sehr großen Zahlen. Allerdings ist 9999
die größte Zahl, die im Badge vollständig angezeigt werden kann. Für noch größere Werte ist der zur Verfügung stehende Platz nicht ausreichend und die Anzeige wird verkürzt. Durch Zuweisung von 0
kann ein Badge wieder entfernt werden.
Remote Notifications, besser bekannt unter dem Namen Push-Benachrichtigungen, sind eine weitere Möglichkeit den Anwender mit Nachrichten zu informieren, wenn die zugehörige App momentan nicht ausgeführt wird. Die Funktionsweise dieser Benachrichtigungen ist jedoch eine komplett andere, denn im Gegensatz zu lokalen Benachrichtigungen werden diese Nachrichten nicht von einer App im Betriebssystem hinterlegt, sondern von außen über ein Mobilfunk- oder WLAN-Netz an das iOS-Gerät übertragen.
Ein gutes Beispiel für einen solchen Anwendungsfall wäre ein System zur Paketverfolgung, wie es einige Logistikunternehmen bereits betreiben. Immer wenn eine Sendung ihren Standort wechselt, könnten Absender und Empfänger über diese Zustandsänderung mit einer Push-Benachrichtigung informiert werden. Leider ist die Implementierung einer Remote Notification sehr aufwendig und kann in diesem Buch nur kurz erwähnt werden. Neben der App benötigen Sie einen zertifizierten Server, der mit Apples Push Notification service (APNs) kommuniziert. Umfassende Informationen finden Sie in der Xcode-Hilfe im Dokument »Local and Push Notification Programming Guide«.
Sehr viele iOS-Apps neigen inzwischen zu einem verschwenderischen Umgang mit Benachrichtigungen, sodass man jedem Entwickler nur zur Zurückhaltung raten kann. Informieren Sie die Anwender, wenn es nötig ist, aber benutzen Sie die Nachrichten nicht, um Nutzer permanent an Ihre App zu erinnern. Das bringt selten das gewünschte Ergebnis. Innerhalb der Systemeinstellungen können die Benachrichtigungen für einzelne Apps auch explizit deaktiviert werden.
Wie behandelt das Betriebssystem eine lokale Benachrichtigung, wenn die zugehörige App bereits aktiv ist?
Wie können lokale Benachrichtigungen, die von einer Anwendung an das Betriebssystem übergeben wurden, wieder gelöscht werden?
Keines der Projekte in diesem Buch verrichtet Aufgaben, an die der Anwender erinnert werden muss. Das sollte uns jedoch nicht davon abhalten, ein wenig mit den lokalen Benachrichtigungen zu experimentieren. Möglich wäre eine Erweiterung der Uhr aus Kapitel 10 um eine Erinnerungsfunktion. Ergänzen Sie das Projekt um eine zweite Szene, in der eine »Weckzeit« angewählt werden kann. Das UIDatePicker
-Steuerelement kann Ihnen dabei eine Menge Arbeit abnehmen. Die erforderlichen Methoden finden Sie in der Xcode-Dokumentation.