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

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 9 Multimedia
Pfeil 9.1 Schönschrift
Pfeil 9.1.1 Texthervorhebungen über Attributed Strings
Pfeil 9.1.2 Weitere Anzeigemöglichkeiten
Pfeil 9.1.3 Text mit Hervorhebungen über Dokumente erzeugen
Pfeil 9.1.4 Zeichenketten in Farben umwandeln
Pfeil 9.2 Einbindung von HTML-Dokumenten
Pfeil 9.2.1 Anzeige von HTML-Dokumenten
Pfeil 9.2.2 Javascript-Dateien einbinden
Pfeil 9.2.3 Das Delegate des Webviews
Pfeil 9.2.4 Webviews und Scrollviews
Pfeil 9.2.5 Der Viewport
Pfeil 9.2.6 Dynamische HTML-Seiten
Pfeil 9.2.7 HTML-Sonderzeichen maskieren
Pfeil 9.2.8 Javascript ausführen
Pfeil 9.2.9 Ereignisübergabe an die Applikation
Pfeil 9.3 Antwortcaching und Offlinemodus
Pfeil 9.3.1 Bilder nachladen
Pfeil 9.3.2 Cache Me If You Can
Pfeil 9.3.3 Let’s go offline
Pfeil 9.3.4 Protokolle
Pfeil 9.3.5 Ein datenbankbasierter Offlinecache
Pfeil 9.4 Videos
Pfeil 9.4.1 YouTube-Videos einbetten
Pfeil 9.4.2 Wiedergabe über das Media-Player-Framework
Pfeil 9.4.3 Vorschaubilder erzeugen
Pfeil 9.4.4 Videos über Layer anzeigen

9MultimediaZur nächsten Überschrift

»Fernsehen bildet.
Immer, wenn der Fernseher an ist, gehe ich in ein anderes Zimmer
und lese.«
– Groucho Marx

Bislang haben die Beispielapplikationen dieses Buches Ausgabemöglichkeiten für relativ einfache Inhalte verwendet. Dazu gehören Texte, Bilder und gezeichnete Grafiken. Das Fototagebuch erlaubte außerdem die Ausgabe von selbstaufgenommenen Tönen. In diesem Kapitel geht es um die Ausgabe von komplexeren Inhalten, wie beispielsweise Texten mit Hervorhebungen, das Mischen von Text- und Bildinhalten und das Abspielen von Videos.

Texthervorhebungen

Wir verwenden den Begriff Texthervorhebung in diesem Kapitel für Änderungen des Schriftstils (z. B. kursiv, fett, unterstrichen), der Größe, der Farbe, aber auch des Zeichensatzes innerhalb eines angezeigten Textes.

Für die Darstellung dieser Daten bietet Cocoa Touch zwei unterschiedliche Wege an. Sie können diese Ausgaben über Layer und Views direkt programmieren, wie Sie es bisher schon gemacht haben. Alternativ können Sie auch entsprechende HTML-Dokumente anzeigen, über die sich in der Regel die Anordnung der einzelnen Elemente wesentlich einfacher beschreiben lässt.

Ein weiteres wichtiges Thema in diesem Kapitel ist schließlich das Nachladen und Verwalten der Daten über das Internet. Es baut damit auf Kapitel 8, »Datenserialisierung und Internetzugriff«, auf und erweitert dessen Themen um Zip-Archive, Caching und eigene URL-Protokolle.


Rheinwerk Computing - Zum Seitenanfang

9.1SchönschriftZur nächsten ÜberschriftZur vorigen Überschrift

Cocoa Touch bietet inzwischen vielfältige Möglichkeiten, Multimediadaten anzuzeigen. Seit iOS 6 unterstützen auch viele Klassen des UIKits die Anzeige von Texthervorhebungen, während es die grundsätzliche Unterstützung dafür bereits seit iOS 3.2 in öffentlichen APIs gibt.

Mit den Klassen NSAttributedString und NSMutableAttributedString schuf Apple bereits in iOS 3.2 die Möglichkeit, Hervorhebungen in einem Text auszuzeichnen. Allerdings erfolgte die Ausgabe vor iOS 6 über Core Text oder Core Animation.

Ein Objekt der Klasse NSAttributedString enthält eine Zeichenkette mit Attributen. Ein Attribut hat einen Namen und einen Wert und kann für einen beliebigen Bereich der Zeichenkette gelten, dabei kann der Name eine beliebige Zeichenkette der Klasse NSString sein. Eine neue Zeichenkette mit Attributen legen Sie über den Initializer initWithString:attributes: anlegen:

NSAttributedString *theAttributedString =  
[[NSAttributedString alloc] initWithString:@"Hallo Welt"
attributes:@{
@"name" : @"Brian", @"level" : @3,
@"values" : @[@1, @2, @4] }];

Listing 9.1 Anlegen einer Zeichenkette mit Attributen

»NSString« und »NSAttributedString«

Die Klasse NSAttributedString ist eine direkte Subklasse von NSObject und nicht von NSString, wie das der Name vielleicht suggeriert. Sie dürfen also nicht an Stellen, an die einen NSString erwarten, einen NSAttributedString einsetzen. Es gibt jedoch auch ein paar Ausnahmen, wie beispielsweise die Klasse CATextLayer, deren Property string sowohl Objekte der Klasse NSString als auch NSAttributedString akzeptiert. Allerdings hat diese Property auch den Typ id.

Listing 9.1 erzeugt eine neue Zeichenkette mit den drei Attributen name, level und values. Wie Sie an dem Beispiel sehen, kann der Attributwert eine beliebige Klasse haben. Über die Methode attributesAtIndex:effectiveRange: fragen Sie die Attribute an einer bestimmten Position der Zeichenkette ab. Die Methode liefert ein Verzeichnis mit den Namen und Werten aller Attribute, die an der angegebenen Position gesetzt sind, und der Rückgabewert effectiveRange enthält den Bereich um die Position, in dem diese Werte außerdem gelten.

NSRange theRange;
NSDictionary *theAttributes =
[theAttributedString attributesAtIndex:3
effectiveRange:&theRange];

Listing 9.2 Abfragen der Attribute einer Zeichenkette

In Listing 9.2 sehen Sie ein Beispiel für die Anwendung dieser Methode. Mit der Variablen theAttributedString aus Listing 9.1 enthält das Verzeichnis theDictionary die gleichen Attribute wie oben, und die Variable theRange hat die Startposition 0 und die Länge 10. Falls Sie der Bereich nicht interessiert, können Sie anstelle von &theRange auch NULL angeben.

Über die Methode attribute:atIndex:effectiveRange: können Sie auch einzelne Attribute über deren Namen in der Zeichenkette abfragen:

NSRange theRange;
id theValue = [theAttributedString attribute:@"name"
atIndex:3 effectiveRange:&theRange];

Listing 9.3 Abfrage eines einzelnen Attributs

Die Variable theValue verweist also nach dem Aufruf auf die Zeichenkette »Brian«, und theRange hat die gleichen Werte wie oben.

Allerdings können Sie Objekte der Klasse NSAttributedString immer nur mit Attributen anlegen, die für die komplette Zeichenkette gelten. Wenn Sie nur einen Bereich mit Attributen auszeichnen möchten, müssen Sie stattdessen die Klasse NSMutableAttributedString verwenden, die sowohl eine Änderung der enthaltenen Zeichenkette als auch der Attribute erlaubt. Dazu verwenden Sie die Methode setAttributes:range:, mit der Sie Attribute in einem Bereich setzen können und alle bereits vorhandenen löschen. Möchten Sie hingegen die vorhandenen Attribute erweitern, dann können Sie dazu die Methode addAttributes:range: verwenden.

NSMutableAttributedString *theString = 
[[NSMutableAttributedString alloc]
initWithString:@"Hallo Welt"];
[theString setAttributes:@{@"level" : @3}
range:NSMakeRange(0, 5)];
[theString setAttributes:@{@"name" : @"Brian"}
range:NSMakeRange(6, 4)];
[theString addAttributes:@{@"values" : @[@1, @2]}
range:NSMakeRange(3, 5)];

Listing 9.4 Hinzufügen von Attributen in einer Zeichenkette

Das Beispiel in Listing 9.4 setzt das Attribut level für das Wort Hallo und das Attribut name für das Wort Welt. Außerdem setzt es das Attribut values für die Teilzeichenkette »lo We«. Da diese Operation nicht die vorhandenen Attribute level und name löschen soll, verwendet das Listing hierfür die Methode addAttributes:range:. Das Ergebnis können Sie sich wie in Abbildung 9.1 dargestellt vorstellen.

Abbildung

Abbildung 9.1 Zeichenkette mit auf Bereiche eingeschränkten Attributen

Die drei Klammern über der Zeichenkette zeigen jeweils die Bereiche der einzelnen Attribute an, und die Kästchen unter Zeichenkette stellen die Ergebnisse der Methode attributesAtIndex:effectiveRange: dar. Wenn Sie diese Methode beispielsweise mit dem Index 0 aufrufen, erhalten Sie das Attribut level mit dem entsprechenden Wert und die Position 0 und die Länge 3 als Ergebnis. Für die Positionen 6 und 7 erhalten Sie jeweils die beiden Attribute name und values mit dem effektiven Bereich mit der Position 6 und der Länge 2 als Ergebnis.


Rheinwerk Computing - Zum Seitenanfang

9.1.1Texthervorhebungen über Attributed StringsZur nächsten ÜberschriftZur vorigen Überschrift

Sie können zwar beliebige Attribute in Objekten der Klasse NSAttributedString verwenden, allerdings ist der mit Abstand häufigste Anwendungsbereich sicherlich die Darstellung von Texthervorhebungen. Dafür stellt das UIKit ab iOS 6 Zeichenkettenkonstanten für die Attributnamen bereit. Da diese Konstanten aus dem AppKit von OS X stammen, beginnen sie alle mit dem Präfix NS. In Tabelle 9.1 finden Sie eine Übersicht aller derzeit unterstützten Attribute.

Tabelle 9.1 Attributnamen für die Texthervorhebungen des UIKits

Attributname Beschreibung

NSFontAttributeName

Legt den Zeichensatz des Textes fest. Als Werte verwenden Sie UIFont-Objekte.

NSParagraphStyleAttributeName

Damit passen Sie das Aussehen von Absätzen über Objekte der Klasse NSParagraphStyle an.

NSForegroundColorAttributeName

Legt die Textfarbe über ein UIColor-Objekt fest.

NSBackgroundColorAttributeName

Legt die Texthintergrundfarbe über ein UIColor-Objekt fest.

NSLigatureAttributeName

Schaltet Ligaturen über den Wert 1 ein und über den Wert 0 aus.

NSKernAttributeName

Fügt einen zusätzlichen Abstand zwischen den einzelnen Buchstaben. Dieser Fließkommawert darf auch negativ sein.

NSStrikethroughStyleAttributeName

Streicht den Text durch. Mögliche Werte sind NSUnderlineStyleSingle und NSUnderlineStyleNone.

NSUnderlineStyleAttributeName

Unterstreicht den Text; hier können Sie die gleichen Werte wie beim Durchstreichen verwenden.

NSStrokeColorAttributeName

Legt die Umrissfarbe für den Text fest.

NSStrokeWidthAttributeName

Legt die Breite für den Umriss fest. Der Wert 0 schaltet die Darstellung des Umrisses aus. Negative Werte füllen die Buchstaben mit der Umrissfarbe.

NSShadowAttributeName

Über ein NSShadow-Objekt lässt sich hiermit ein Schatten festlegen.

NSVerticalGlyphFormAttributeName

Legt die Textrichtung (horizontal/vertikal) fest; iOS 6 unterstützt zurzeit nur horizontal.

Werte mit einfachen Datentypen (z. B. Enums, int oder float) müssen Sie zum Einfügen natürlich in ein entsprechendes Objekt der Klasse NSNumber umwandeln (z. B. @(NSUnderlineStyleSingle), @3.0).

Die Anwendung dieser Attribute veranschaulicht das Beispielprojekt Markup, das Ihnen auch den Umgang mit Klassen NSPageStyle und NSShadow zeigt.

Projektinformation

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

Das Beispielprojekt stellt zwei Texte mit Hervorhebungen dar. Abbildung 9.2 zeigt einen Screenshot der App. Seit iOS 6 unterstützen die Klassen UILabel und UITextView auch die Anzeige von Texthervorhebungen, so haben Sie ab Xcode 4.5 auch die Möglichkeit, die Texte dieser Views mit Hervorhebungen zu versehen.

Abbildung

Abbildung 9.2 Beispiel-App mit hervorgehobenen Texten

Dazu müssen Sie zunächst im Attributinspektor im Pop-up-Menü unter Text den Eintrag Attributed auswählen (siehe Abbildung 9.3). Sie können dann über das Untermenü EditFormatFont die Textdarstellung des Textes ändern, indem Sie eine beliebige Textstelle selektieren und die gewünschte Hervorhebung über den entsprechenden Untereintrag des Menüs auswählen. Über den Menüpunkt EditFormatFontShow Fonts öffnen Sie den in Abbildung 9.3 darstellten Dialog, der eine detailliertere Auswahl des Schriftstils und der Hervorhebungen erlaubt.

Abbildung

Abbildung 9.3 Text mit Hervorhebungen bearbeiten

Die Hervorhebungen des Textes in dem Text-View erzeugt die App zur Laufzeit. Dafür verwendet sie einen SAX-Parser, der eine textuelle Beschreibung der Hervorhebungen erlaubt. Dabei lassen sich die Namen der Tags frei konfigurieren; die App verwendet beispielsweise eine an HTML angelehnte Menge von Tags. Das XML für den Text-View in der Beispiel-App sehen Sie in Listing 9.5.

<?xml version="1.0" encoding="UTF-8"?>
<text>Dies ist ein Beispieltext mit <u>Attributen</u>.
Sie können mit dem Markup-Parser beliebige Tags definieren;
z. B. <em>em</em>, <strong>strong</strong> oder
<blink>blink</blink>. Außerdem können Sie bei jedem Tag die
<em color="red">Text-</em> und die <strong
background-color="#20CC33">Hintergrundfarbe</strong>
über Attribute setzen. Durchstreichen ist
<strike>nicht</strike> möglich, wobei Sie
<strike>Strike</strike> aber nicht mit
<stroke>Stroke</stroke> verwechseln sollten. Auch
<shadow>Textschatten</shadow> lassen sich problemlos
darstellen.
<p>Eigene Absätze sind über <code>NSParagraphStyle</code>
möglich. Das ist eine eigene Klasse, die viele Attribute
bereitstellt.</p></text>

Listing 9.5 Das XML für den Text in der Beispiel-App

Die zentrale Komponente dieser Umwandlung ist die Klasse MarkupParser, die das Protokoll NSXMLParserDelegate implementiert. Die Klasse speichert den Text mit den Hervorhebungen intern in der Property text der Klasse NSMutableAttributedString. Außerdem besitzt sie einen Stapel, um sich die Startpositionen der Tags im Text zu merken, da die Klasse die Hervorhebungen erst mit dem schließenden Tag erzeugen kann. Alle über die Delegate-Methode parser:foundCharacters: gelesenen Zeichen fügt der Parser direkt an die Property text an.

- (void)parser:(NSXMLParser *)inParser 
foundCharacters:(NSString *)inCharacters {
NSAttributedString *theString = [[NSAttributedString
alloc] initWithString:inCharacters];

[self.text appendAttributedString:theString];
}

Listing 9.6 Anfügen von Zeichen an einen Text mit Hervorhebungen

Die Implementierung in Listing 9.6 erzeugt dazu aus den übergebenen Zeichen ein Objekt der Klasse NSAttributedString und hängt es über die Methode appendAttributedString: an den Inhalt der Property text an. Diese Property enthält also den gesamten Text, der im XML-Dokument zwischen den Tags steht.

Die Verarbeitung der Tags und das Einfügen der Hervorhebungen lässt sich am besten anhand von Beispielen erläutern. Zu den Bestandteilen des XML-Dokuments

<text>Dies ist ein <strong>Beispiel</strong>.<text>

stellt Tabelle 9.2 die entsprechenden SAX-Ereignisse dar. Die dritte Spalte zeigt den Inhalt der Property text nach dem entsprechenden Verarbeitungsschritt.

Tabelle 9.2 SAX-Ereignisse für ein einfaches Beispieldokument

XML-Daten Ereignis Property »text«

Start Document

»«

<text>

Start Element

»«

Dies ist ein

Characters

»Dies ist ein «

<strong>

Start Element

»Dies ist ein «

Beispiel

Characters

»Dies ist ein Beispiel«

</strong>

End Element

»Dies ist ein Beispiel«

.

Characters

»Dies ist ein Beispiel

</text>

End Element

»Dies ist ein Beispiel

End Document

»Dies ist ein Beispiel

Der Markup-Parser soll den Text »Beispiel« innerhalb des strong-Tags durch entsprechende Attribute markieren. Dazu muss der Markup-Parser die Positionen des Start- und des End-Tags kennen. Das einfache Zwischenspeichern in einer Property reicht hier jedoch nicht aus, da Sie ja die Tags wie beispielsweise in

<text>Dies ist ein <u>schönes 
<strong>Beispiel</strong></u>.</text>


Listing 9.7 XML-Dokument mit verschachtelten Tags

verschachteln können. Der Parser muss sich also während der Verarbeitung immer die Positionen aller öffnenden Tags merken können, für die er noch kein entsprechendes schließendes Tag gelesen hat. Dazu eignet sich wie bereits in Kapitel 8, »Datenserialisierung und Internetzugriff«, ein Stapel. Tabelle 9.3 zeigt die SAX-Ereignisse, den Inhalt der Property text und des Stapels während der Verarbeitung für das Dokument aus Listing 9.7. Die Zahlen in der letzten Spalte geben jeweils die Zeichenposition im Text an, an denen der Parser die öffnenden Tags u und strong gefunden hat. Diese Werte muss er sich bis zu den zugehörigen schließenden Tags merken.

Tabelle 9.3 SAX-Ereignisse für verschachtelte Tags

XML-Daten Ereignis Property »text« Stapel

Start Document

»«

[]

<text>

Start Element

»«

[]

Dies ist ein

Characters

»Dies ist ein «

[]

<u>

»Dies ist ein «

[13]

schönes

»Dies ist ein schönes «

[13]

<strong>

Start Element

»Dies ist ein schönes «

[20, 13]

Beispiel

Characters

»Dies ist ein schönes Beispiel«

[20, 13]

</strong>

End Element

»Dies ist ein schönes Beispiel«

[13]

</u>

»Dies ist ein schönes Beispiel«

[]

.

Characters

»Dies ist ein schönes Beispiel

[]

</text>

End Element

»Dies ist ein schönes Beispiel

[]

End Document

»Dies ist ein schönes Beispiel

[]

In der Methode parser:didStartElement:namespaceURI:qualifiedName:attributes: lässt sich diese Position jeweils über die Länge des aktuellen Textes, also den Ausdruck [self.text length], berechnen. Der Parser soll im Stapel aber nicht nur die Position, sondern auch die Attribute für die Texthervorhebung speichern, wozu er die Klasse Markup verwendet, deren Objekte diese beiden Werte speichern können.

- (void)parser:(NSXMLParser *)inParser 
didStartElement:(NSString *)inElementName
namespaceURI:(NSString *)inNamespaceURI
qualifiedName:(NSString *)inQualifiedName
attributes:(NSDictionary *)inAttributes {
NSDictionary *theAttributes =
[self attributesForTagName:inElementName];

if(theAttributes != nil) {
NSUInteger thePosition = [self.text length];
Markup *theMarkup =
[Markup markupWithPosition:thePosition
attributes:theAttributes];

[self.stack addObject:theMarkup];
}
}

Listing 9.8 Verarbeitung der Start-Tags

Die Methode prüft zuerst, ob der Parser für das empfangene Tag inElementName Attribute besitzt, wozu er die Methode attributesForTagName: verwendet. Bei vorhandenen Attributen bestimmt er die aktuelle Position, erzeugt ein Markup-Objekt und legt es auf den Stapel. Auch die Verarbeitung des End-Tags (Listing 9.9) prüft zunächst, ob Attribute für das empfangene Tag vorliegen, und bestimmt die aktuelle Position im Text. Diese Position ist die Endposition der Hervorhebung.

- (void)parser:(NSXMLParser *)inParser 
didEndElement:(NSString *)inElementName
namespaceURI:(NSString *)inNamespaceURI
qualifiedName:(NSString *)inQualifiedName {
if([self.attributesByTagName objectForKey:inElementName]
!= nil) {
NSUInteger thePosition = [self.text length];
Markup *theMarkup = [self.stack lastObject];
NSRange theRange = NSMakeRange(
theMarkup.position,
thePosition – theMarkup.position);

[self.stack removeLastObject];
[self.text addAttributes:theMarkup.attributes
range:theRange];
}
}

Listing 9.9 Verarbeitung des End-Tags

Das oberste Markup-Objekt auf dem Stapel enthält die Attribute und die Startposition des Textes, den das aktuelle Tag hervorheben soll. Der Parser liest dieses Element aus dem Stapel und löscht es von dort. Der Bereich der Hervorhebung beginnt an der Startposition, und seine Länge ist die Differenz von End- und Startposition. Schließlich fügt der Parser über einen Aufruf der Methode addAttributes:range: die Attribute zum Text hinzu.

Das Mitführen der Attribute im Markup-Objekt ergibt in dieser Version noch keinen rechten Sinn, da sich ja die Attribute zu dem Tag auch hier über die Methode attributesForTagName: bestimmen lassen. Allerdings erlaubt der Parser auch, die Text- und die Hintergrundfarbe bei jedem Tag über Attribute festzulegen, z. B.:

<text>Dies ist ein 
<strong color="#FF0000">Beispiel</strong>.<text>


Listing 9.10 Tag mit Attribut für Hervorhebung

Dazu muss der Parser die Attribute bei der Verarbeitung des Start-Tags auslesen, in Objekte der Klasse UIColor umwandeln und über die Methode addAttribute:forName: zum Markup-Objekt hinzufügen. Die Erweiterungen für Listing 9.8 können Sie Listing 9.11 entnehmen.

NSUInteger thePosition = [self.text length];
Markup *theMarkup =
[Markup markupWithPosition:thePosition
attributes:theAttributes];
UIColor *theTextColor = [UIColor colorWithString:

[inAttributes valueForKey:@"color"]];
UIColor *theBackgroundColor = [UIColor colorWithString:

[inAttributes valueForKey:@"background-color"]];

[theMarkup addAttribute:theTextColor

forName:NSForegroundColorAttributeName];
[theMarkup addAttribute:theBackgroundColor

forName:NSBackgroundColorAttributeName];
[self.stack addObject:theMarkup];

Listing 9.11 Attribute für Text- und Hintergrundfarbe verarbeiten

Die Attributwerte für die Farben sind natürlich Zeichenketten und keine UIColor-Objekte. Für die Umwandlung enthält das Beispielprojekt die Kategorie UIColor(StringParsing), die den Convenience-Konstruktor colorWithString: bereitstellt. Auf diese Konvertierung gehen wir in Abschnitt 9.1.4, »Zeichenketten in Farben umwandeln«, noch genauer ein. Die Textfarbe fügt Listing 9.11 unter dem Schlüssel NSForegroundColorAttributeName zu den Attributen des Markup-Objekts hinzu. Für die Hintergrundfarbe verwendet das Listing hingegen NSBackgroundColorAttributeName.

Da nur der Parser die Klasse Markup verwendet und diese auch recht übersichtlich ist, finden Sie ihre Schnittstelle und Implementierung in der Datei MarkupParser.m des Beispielprojekts. Die Klasse MarkupParser verwaltet die Attribute für die Hervorhebungen in einem Dictionary, dessen Schlüssel Tag-Namen und dessen Werte die entsprechenden Dictionarys mit den Attributen für die Hervorhebungen sind. Auf dieses Dictionary lässt sich außerhalb der Klasse über die Methoden attributesForTagName: und setAttributes:forTagName: zugreifen, deren Implementierung Sie in Listing 9.12 finden.

- (NSDictionary *)attributesForTagName:(NSString *)inName {
return [self.attributesByTagName valueForKey:inName];
}

- (void)setAttributes:(NSDictionary *)inAttributes
forTagName:(NSString *)inName {
[self.attributesByTagName setValue:inAttributes
forKey:inName];
}

Listing 9.12 Verwaltung der Attribut-Dictionarys im Markup-Parser

Über die Methode setAttributes:forTagName: lassen sich somit die Tags für die Hervorhebungen festlegen. Das geschieht im Beispielprojekt in der Methode markupParser der Klasse MarkupViewController, die unter anderem Attribute für die Tags u, em, strong, code und strike anlegt. Diese Tags sind den gleichnamigen HTML-Tags nachempfunden. Das u-Tag dient, zumindest in älteren HTML-Versionen, zum Unterstreichen des eingeschlossenen Texts. Dementsprechend setzt Listing 9.13 ein Dictionary mit dem Schlüssel NSUnderlineStyleAttributeName und dem Wert NSUnderlineStyleSingle, den der Boxing-Operator @() in ein NSNumber-Objekt umwandelt.

MarkupParser *theParser = [[MarkupParser alloc] init];

[theParser setAttributes:@{ NSUnderlineStyleAttributeName :
@(NSUnderlineStyleSingle)
} forTagName:@"u"];
[theParser setAttributes:@{ NSFontAttributeName :
[UIFont fontWithName:@"Helvetica-Oblique" size:17]
} forTagName:@"em"];
[theParser setAttributes:@{ NSFontAttributeName :
[UIFont fontWithName:@"Helvetica-Bold" size:17]
} forTagName:@"strong"];
[theParser setAttributes:@{ NSFontAttributeName :
[UIFont fontWithName:@"Courier" size:17]
} forTagName:@"code"];
[theParser setAttributes:@{
NSStrikethroughStyleAttributeName :
@(NSUnderlineStyleSingle)
} forTagName:@"strike"];

Listing 9.13 HTML-ähnliche Tags deklarieren

Die Tags em, strong und code ändern den Zeichensatz des Textes, indem sie dem Schlüssel NSFontAttributeName ein UIFont-Objekt zuweisen. Das ist für em ein kursiver Zeichensatz, für bold eine fette Schriftart und für code eine nicht-proportionale Schrift. Mit dem strike-Tag können Sie den enthaltenen Text durchstreichen; und so enthält dessen Attribut-Dictionary den Schlüssel NSStriketroughStyleAttributeName mit dem Wert NSUnderlineStyleSingle.

Das stroke-Tag demonstriert Buchstaben mit einer Umrandung. Hierfür müssen Sie einerseits die Breite und andererseits die Farbe der Umrandung festlegen, wie das Listing 9.14 zeigt.

[theParser setAttributes:@{ 
NSStrokeWidthAttributeName : @3.0,
NSStrokeColorAttributeName : [UIColor redColor]
} forTagName:@"stroke"];

Listing 9.14 Tag mit mehreren Attributen

Die Konfiguration von Schatten und Absätzen erfolgt über Objekte mit den speziellen Klassen NSShadow, NSParagraphStyle beziehungsweise NSMutableParagraphStyle. Bei einem Textschatten haben Sie drei Einstellungsmöglichkeiten. Über die Property shadowOffset können Sie den Versatz des Schattens festlegen. Für einen Schatten nach unten rechts können Sie beispielsweise den Wert CGSizeMake(1.0, 1.0) verwenden. Für die Farbe übergeben Sie ein Objekt der Klasse UIColor an die Property shadowColor, und über shadowBlurRadius legen Sie die Stärke der Verwischung fest. Wenn Sie diesen Wert auf 0 setzen, zeichnet das UIKit den Schatten ohne Verwischung. Bei höheren Werten verschwimmt der Schatten zum äußeren Rand hin immer mehr. Das Beispielprojekt legt einen leichten blauen Schatten nach unten links für das Tag shadow an:

NSShadow *theShadow = [[NSShadow alloc] init];

theShadow.shadowColor =
[UIColor colorWithRed:0.0 green:0.0 blue:0.5 alpha:0.6];
theShadow.shadowBlurRadius = 2.0;
theShadow.shadowOffset = CGSizeMake(-1.0, 1.0);
[theParser setAttributes:@{
NSShadowAttributeName : theShadow
} forTagName:@"shadow"];

Listing 9.15 Anlegen und Konfiguration eines Textschattens

Ein Text mit Hervorhebungen kann auch Absätze enthalten, die Sie allerdings nicht über Attribute markieren. Sie trennen Absätze über einen einfachen Zeilenvorschub '\n' beziehungsweise (char)10 voneinander. Der XML-Parser ermöglicht die explizite Trennung der Absätze über das Tag p, das in der Methode parser:didStartElement:namespaceURI:qualifiedName:attributes: fest eingebaut ist. Listing 9.16 zeigt die Änderungen gegenüber Listing 9.8.

NSDictionary *theAttributes = [self 
attributesForTagName:inElementName];

if([inElementName isEqualToString:@"p"]) {

[self.text.mutableString appendString:@"\n"];
}

if(theAttributes != nil) {

Listing 9.16 Erzeugung von Absätzen

Über Objekte der Klasse NSParagraphStyle können Sie das Aussehen der Absätze beeinflussen. Hier gibt es sehr viele Möglichkeiten, die Sie allerdings nur bei der Unterklasse NSMutableParagraphStyle mit eigenen Werten belegen können. Listing 9.17 setzt den Abstand für Absätze nach oben auf 5 Punkte und legt eine zentrierte Textausrichtung fest.

NSMutableParagraphStyle *theStyle = 
[[NSMutableParagraphStyle alloc] init];

theStyle.alignment = NSTextAlignmentCenter;
theStyle.paragraphSpacing = 5.0;
[theParser setAttributes:@{
NSParagraphStyleAttributeName : theStyle
} forTagName:@"p"];

Listing 9.17 Absatzstil konfigurieren

Damit die Beispiel-App den Text auch anzeigt, muss Sie das XML-Dokument laden, einen NSXMLParser erzeugen und konfigurieren, der das XML-Dokument verarbeitet und schließlich dem Text-View der App den erzeugten Text zuweisen. Die Klasse MarkupParser besitzt die Methode attributedStringWithContentsOfURL:error:, die die Verwaltung des XML-Parsers übernimmt. Sie liefert dem Aufrufer einen eventuell aufgetretenen Fehler zurück, wenn er im Parameter error einen Zeiger auf eine entsprechende Variable übergibt. Die Rückgabe des NSError-Objekts funktioniert hier also wie bei vielen Methoden aus Cocoa Touch auch, die Sie bereits in Kapitel 2, »Die Reise nach iOS«, kennengelernt haben.

- (NSAttributedString *) 
attributedStringWithContentsOfURL:(NSURL *)inURL
error:(NSError **)outError {
NSXMLParser *theParser =
[[NSXMLParser alloc] initWithContentsOfURL:inURL];

theParser.delegate = self;
[theParser parse];
if(outError != NULL) {
*outError = theParser.parserError;
}
return theParser.parserError == nil ?
[self attributedString] : nil;
}

Listing 9.18 Parser konfigurieren und ausführen

Die Methode aus Listing 9.18 erleichtert die Verwendung des Markup-Parsers im Viewcontroller, wofür Sie in Listing 9.19 ein Beispiel sehen. Dabei erzeugt die Methode markupParser das Parserobjekt mit den beschriebenen Tags. Abhängig vom Ergebnis des XML-Parsings zeigt die App entweder den Text mit Hervorhebungen oder die Fehlermeldung an.

- (void)viewDidLoad {
[super viewDidLoad];
NSURL *theURL = [[NSBundle mainBundle]
URLForResource:@"text" withExtension:@"xml"];
NSError *theError = nil;
NSAttributedString *theString = [[self markupParser]
attributedStringWithContentsOfURL:theURL
error:&theError];

if(theError == nil) {
self.label.attributedText = theString;
}
else {
self.label.text = [NSString
stringWithFormat:@"error=%@", theError];
}
}

Listing 9.19 Verwendung der Klasse »MarkupParser«

Größenberechnung

In Kapitel 4, »Alles unter Kontrolle«, haben Sie die Methode sizeWithFont: kennengelernt. Die Kategorie NSAttributedString(StringDrawing) stellt mit der Methode size das Gegenstück zu sizeWithFont: für NSAttributedString bereit. Mit den Anweisungen

NSAttributedString *theText = [[NSAttributedString alloc] 
initWithString:theString attributes:@{
NSFontAttributeName : theFont }];
CGSize theSize = [theText size];

erhalten Sie also in der Variablen theSize den gleichen Wert wie für [theString sizeWithFont:theFont].

Die Variante mit NSAttributedString hat jedoch den Vorteil, dass sie auch funktioniert, wenn das Programm sie nicht im Main-Thread ausführt.


Rheinwerk Computing - Zum Seitenanfang

9.1.2Weitere AnzeigemöglichkeitenZur nächsten ÜberschriftZur vorigen Überschrift

Objekte der Klasse NSAttributedString lassen sich jeweils über die Property attributedText zu Views der Klassen UILabel, UITextView und UITextField zuweisen und dadurch anzeigen. Eine Darstellung dieser Zeichenketten ist aber auch über Zeichnen in einen Grafikkontext oder über die Klasse CATextLayer aus Core Animation möglich.

Die Kategorie NSAttributedString(NSStringDrawing) stellt Ihnen ab iOS 6 Methoden zur Darstellung von Texten mit Hervorhebungen zur Verfügung. Über die Methode drawAtPoint: können Sie den Text an einem beliebigen Punkt im aktuellen Grafikkontext zeichnen. Wenn Sie den Text innerhalb eines vorgegebenen Rechtecks darstellen möchten, können Sie dafür die Methode drawInRect: verwenden.


Rheinwerk Computing - Zum Seitenanfang

9.1.3Text mit Hervorhebungen über Dokumente erzeugenZur nächsten ÜberschriftZur vorigen Überschrift

Apple hat in iOS 7 die Kategorie NSAttributedString(NSAttributedStringDocumentFormats) eingeführt, die eine Erzeugung von Texten mit Hervorhebungen direkt aus bekannten Dokumentformaten wie beispielsweise HTML erlaubt. Dazu verwenden Sie die Initialisierungsmethode initWithData:options:documentAttributes:error: beziehungsweise initWithFileURL:options:documentAttributes:error:, die die Daten des Dokuments aus einem Datenobjekt beziehungsweise über eine Datei-URL lesen.

In den meisten Fällen brauchen Sie bei beiden Methoden nur für den ersten Parameter ein entsprechendes Objekt anzugeben und können für den Parameter options den Wert nil und die Ausgabeparameter documentAttributes und error jeweils den Wert NULL verwenden, sofern Sie an diesen Werten kein Interesse haben. Die Methoden erkennen den Dokumenttyp automatisch, und die Methoden unterstützen folgende Formate:

Tabelle 9.4 Unterstützte Formate für Texte mit Hervorhebungen

Format Dateiendung Bemerkung

Reiner Text

txt

keine Hervorhebungen

Rich-Text-Format

rtf

Rich-Text-Format-Directory

rtfd

erlaubt die Einbettung von Anhängen (z. B. Bildern)

Hypertext-Markup-Language

html

Projektinformation

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

Das Beispielprojekt Documents zeigt die Darstellung von Texten mit Hervorhebungen, die sie jeweils aus einem der vier unterstützten Dokumenttypen erzeugt. In Abbildung 9.4 sehen Sie die Ausgabe des Programms im Simulator.

Abbildung

Abbildung 9.4 Texte mit Hervorhebungen und Bildern

Wie Sie an den Beispielen für RTFD und HTML sehen können, kann die Klasse NSAttributedString auch Bilder darstellen. Allerdings unterstützt sie nicht alle HTML-Formatierungsoptionen, wie Sie an dem HTML-Dokument sehen können: Obwohl das Img-Tag im Quelltext das Attribut valign="top" hat, richtet Cocoa Touch nicht die Oberkante des Bildes an der Oberkante des daneben stehenden Textes aus. Abbildung 9.5 zeigt die Formatierung des gleichen Dokuments in einem Browser (Safari).

Abbildung

Abbildung 9.5 Formatierung des HTML-Dokuments im Browser


Rheinwerk Computing - Zum Seitenanfang

9.1.4Zeichenketten in Farben umwandelnZur vorigen Überschrift

Die Klasse MarkupParser wertet in jedem konfigurierten Tag die Attribute color und background-color aus, über die Sie die Text- beziehungsweise die Hintergrundfarbe festlegen können. Die möglichen Attributwerte sind entweder englische Farbnamen, wie red, green oder blue, oder hexadezimale Angaben mit dem Präfix #. Diese Syntax lehnt sich dabei an die Syntax von Farbwerten in HTML an.

Die Kategorie UIColor(StringParsing) stellt mit der Methode colorWithString: einen Convenience-Konstruktor zur Verfügung, der eine Umwandlung einer Zeichenkette in ein Objekt der Klasse UIColor erlaubt. Dabei ist eine Umwandlung der Farbnamen relativ einfach, da die Klasse UIColor für viele Farben entsprechende Convenience-Konstruktoren, wie redColor, greenColor oder blueColor, besitzt.

Sie brauchen also nur an den Farbnamen die Zeichenkette Color anzuhängen, das Ergebnis in einen Selektor umzuwandeln und damit die entsprechende Methode aufzurufen. Über die Funktion NSSelectorFromString können Sie dabei eine Zeichenkette in einen Selektor umwandeln.

+ (UIColor *)colorWithString:(NSString *)inString {
if(inString == nil) {
return nil;
}
else {
UIColor *theColor = nil;
NSString *theName = [NSString
stringWithFormat:@"%@Color", inString];
SEL theSelector = NSSelectorFromString(theName);

if([self respondsToSelector:theSelector]) {
theColor = [self performSelector:theSelector];
}
return theColor;
}
}

Listing 9.20 Umwandlung eines Farbnamens in eine Farbe

Da sich die Implementierung in Listing 9.20 in einer Kategorie der Klasse UIColor befindet, verweist self auf das Klassenobjekt von UIColor. Deshalb prüft der Ausdruck [self respondsToSelector:theSelector], ob die Klasse UIColor eine Klassenmethode besitzt, die auf den Selektor theSelector passt.

Klassendynamik

Anstelle von self können Sie in Listing 9.20 natürlich auch UIColor verwenden, wodurch sich scheinbar keine Änderung des Verhaltens ergibt. Sie dürfen Klassenmethoden allerdings nicht nur bei der implementierenden Klasse, sondern auch bei deren Unterklassen aufrufen, und hier macht die Verwendung von self beziehungsweise UIColor einen Unterschied, da hier die Laufzeitumgebung die Methodenaufrufe durch die Verwendung von self an das Klassenobjekt der Unterklasse sendet.

Wenn Sie beispielsweise eine Unterklasse MyColor von UIColor ableiten und die Anweisung [MyColor colorWithString:@"red"] ausführen, dann sucht [self performSelector:theSelector] zuerst in der Klasse MyColor nach der passenden Klassenmethode redColor zum Selektor. Der Ausdruck [UIColor performSelector:theSelector] sucht hingegen nur in der Klasse UIColor und gegebenenfalls ihren Oberklassen.

Diese Dynamik ist besonders wichtig bei Convenience-Konstruktoren. Beispielsweise erzeugt [NSArray array] ein leeres Array mit der Klasse NSArray, und entsprechend hat das Ergebnis des Ausdrucks [NSMutableArray array] die Klasse NSMutableArray. Durch die Verwendung von self braucht Apple hier den Convenience-Konstruktor nur einmal zu implementieren:

+ (id)array {
return [[self alloc] init];
}

Der Convenience-Konstruktor erzeugt dadurch immer ein Objekt der Klasse, die Sie im Konstruktoraufruf angeben.

Die Implementierung in Listing 9.20 erlaubt Ihnen nun die Erzeugung von UIColor-Objekten zu englischen Farbnamen, also beispielsweise [UIColor colorWithString:@"green"]. Wenn Ihnen die Standardfarben nicht ausreichen, können Sie einfach über Kategorien neue Convenience-Konstruktoren für neue Farben hinzufügen, z. B.:

+ (id)oliveColor {
return [UIColor colorWithRed:0.5 green:0.7 blue:0.2
alpha:1.0];
}

Listing 9.21 Convenience-Konstruktor für Oliv

Solange diese Klassenmethoden auf »Color« enden, können Sie sie auch immer über colorWithString: ansprechen; also erzeugt [UIColor colorWithString:@"olive"] die Farbe über die Methode aus Listing 9.21. Allerdings ist es auf Dauer etwas mühselig, für jede neue Farbe eine entsprechende Methode zu schreiben.

Aus diesem Grund soll die Methode colorWithString: noch die Eingabe von hexadezimalen Farbwerten unterstützen. Um sie leichter von den symbolischen Namen unterscheiden zu können, sollen die Farbwerte wie in HTML mit einem Doppelkreuz (#) beginnen. Ein Farbwert besteht hier der Einfachheit halber aus genau sechs Hexadezimalziffern, wobei jeweils zwei aufeinanderfolgende Ziffern den Wert für die Komponenten Rot, Grün und Blau beschreiben. Die Zeichenkette @"#FF0000" steht also für ein reines, leuchtendes Rot, @"#00FF00" für das entsprechende Grün und @"#0000FF" für Blau.

Für die Berechnung des Farbwertes müssen Sie also die hexadezimale Darstellung einer Zahl auswerten. Dabei hilft Ihnen die Klasse NSScanner, die das Zerlegen von Zeichenketten in Zahlen und andere Symbole erlaubt. Dabei behandelt der Scanner die Zeichenkette wie ein Fließband, von dem er die Symbole der Reihe nach einliest. Dabei rückt der Scanner immer symbolweise vorwärts und geht niemals zurück. Jedes Symbol kann dabei eine unterschiedliche Länge haben. In Abbildung 9.6 sehen Sie die Zeichenkette @"#20CC33", die wir mit Hilfe des Scanners in die Symbole # und 20CC33 zerlegen. Dabei wandelt uns der Scanner das zweite Symbol praktischerweise direkt in die ganze Zahl mit dem Dezimalwert 2.149.427 um.

Abbildung

Abbildung 9.6 Zerlegung einer Zeichenkette in Symbole

Sie können ein neues Objekt der Klasse NSScanner über die Methode scannerWithString: erzeugen. Dabei übergeben Sie die Zeichenkette, die Sie zerlegen möchten, als Parameterwert. Die Methode scanString:intoString: prüft, ob das aktuelle Symbol auf dem Fließband dem ersten Parameterwert entspricht. Wenn das zutrifft, rückt das Fließband zum nächsten Symbol vor, und die Methode liefert YES. Andernfalls bleibt das Band stehen, und die Methode gibt NO zurück. Der zweite Parameter ist ein Ausgabeparameter für ein NSString-Objekt. Im Erfolgsfall enthält er das gelesene Symbol. Da dessen Wert in diesem Fall jedoch immer dem ersten Parameterwert entspricht, ist er in den meisten Fällen überflüssig. Aus diesem Grund ist hier auch der Wert NULL erlaubt.

+ (UIColor *)colorWithString:(NSString *)inString {
if(inString == nil) {
return nil;
}
else {
NSScanner *theScanner =
[NSScanner scannerWithString:inString];
UIColor *theColor = nil;

if([theScanner scanString:@"#" intoString:NULL]) {

}

else {
NSString *theName = [NSString
stringWithFormat:@"%@Color", inString];
SEL theSelector = NSSelectorFromString(theName);

if([self respondsToSelector:theSelector]) {
theColor =
[self performSelector:theSelector];
}
}
return theColor;
}
}

Listing 9.22 Unterscheidung zwischen Farbnamen und -werten

In Listing 9.22 sehen Sie die Änderungen gegenüber Listing 9.20, um mit Hilfe eines Scanners zwischen numerischen Farbwerten und Farbnamen zu unterscheiden. Dabei trifft die Bedingung [theScanner scanString:@"#" intoString:NULL] in der zweiten if-Anweisung nur zu, wenn das erste Symbol im Scanner das Doppelkreuz ist. In diesem Fall soll die Methode die restlichen Zeichen als Hexadezimalzahl interpretieren und daraus den Farbwert berechnen. Hierzu verwenden Sie die Methode scanHexInt:, die alle Zeichen vom Band einliest, die hexadezimale Ziffern [Anm.: Also die Zeichen 0 bis 9, A bis F und a bis f.] darstellen. Die gelesenen Zeichen wandelt die Methode dann in eine vorzeichenlose, ganze Zahl vom Typ unsigned int um und liefert sie im Ausgabeparameter zurück.

Der Rückgabewert der Methode ist YES, wenn sie hexadezimale Ziffern gelesen hat, und andernfalls NO. Listing 9.23 zeigt die Erweiterungen gegenüber Listing 9.22, um den Farbwert aus der Zeichenkette auszulesen.

if([theScanner scanString:@"#" intoString:NULL]) {
unsigned theValue;

if([theScanner scanHexInt:&theValue]) {

}

}

Listing 9.23 Auswertung einer Hexadezimalzahl

Sie haben nun den Farbwert in einer ganzen Zahl vorliegen und müssen ihn in drei Fließkommazahlen zwischen 0 und 1 umwandeln. Der hexadezimale Farbwert für eine Komponente hat den Wertebereich von 0x0 bis 0xFF (bzw. 255), was genau dem Wertebereich eines Bytes entspricht. Sie können über den Ausdruck theValue & 0xFF das unterste Byte aus einer Zahl auslesen. Mit theValue >> 8 können Sie das unterste Byte (entspricht 8 Bit) löschen, denn Sie verschieben damit die Bits der Zahl so, dass dadurch genau die acht untersten herausfallen.

Die einzelnen Bytewerte brauchen Sie dann nur noch durch 255.0 zu teilen, um die Werte für die Farbkomponenten zu erhalten. Diese Divisionen müssen Sie jedoch mit Fließkommawerten durchführen, damit Sie auch Werte zwischen 0 und 1 erhalten. Aus diesem Grund teilen Sie durch 255.0 (Fließkommazahl) und nicht nur durch 255 (Ganzzahl).

if([theScanner scanHexInt:&theValue]) {
CGFloat theComponents[3];

for(int i = 0; i < 3; ++i) {

theComponents[i] = (theValue & 0xFF) / 255.0;
theValue >>= 8;
}
theColor = [self colorWithRed:theComponents[2]
green:theComponents[1]
blue:theComponents[0]
alpha:1.0];
}

Listing 9.24 Farbkomponenten berechnen und Farbe erzeugen

Für die Zwischenspeicherung verwendet Listing 9.24 ein C-Array mit drei Elementen. Das Auslesen beginnt mit dem niederwertigsten Byte, das in der Hexadezimalzahl am weitesten rechts und somit für Blau steht. Aus diesem Grund müssen Sie die Komponentenwerte bei der Konstruktion des Farbobjekts in der umgekehrten Reihenfolge verwenden. Die Berechnung der Farbkomponenten veranschaulicht Tabelle 9.5 an dem Hexadezimalwert 0x20CC33. Dabei enthalten die Zeilen jeweils die Werte der Ausdrücke für den entsprechenden Schleifendurchlauf.

Tabelle 9.5 Berechnung der Farbkomponenten

i theValue theValue & 0xFF (theValue & 0xFF) / 255.0 theValue >> 8

0

0x20CC33
2149427

0x33
51

0,2

0x20CC
8396

1

0x20CC
8396

0xCC
204

0,8

0x20
32

2

0x20
32

0x20
32

0,125

0x0



Ihr Kommentar

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

>> Zum Feedback-Formular
<< 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


  Zum Katalog
Zum Katalog: Apps programmieren für iPhone und iPad






Neuauflage: 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: 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


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo