5.7Inhalte teilen
Mit iOS 6 hat Apple die Klasse UIActivityViewController eingeführt, die eine einheitliche Anbindung von sozialen Netzwerken wie Twitter, Facebook oder Sina Weibo [Anm.: Sina Weibo ist eine chinesische Microblogging-Plattform, ähnlich wie Twitter.] erlaubt. Außerdem können Sie darüber Mails, Nachrichten und SMS versenden oder Daten an den Drucker, die Zwischenablage sowie das Fotoalbum schicken. Damit der Nutzer die gewünschte Aktivität auswählen kann, zeigt der Controller zunächst eine Auswahl aller verfügbaren und sinnvollen Aktivitäten an (siehe Abbildung 5.27).
Abbildung 5.27 Auswahl der Aktivität über den Activity-Viewcontroller
Accountdaten
Damit ein Nutzer die sozialen Netzwerke über den Activity-Viewcontroller ansprechen kann, muss er seine Zugangsdaten in den Systemeinstellungen des Geräts hinterlegen. Der Controller zeigt in seiner Auswahl also nur die sozialen Netzwerke an, zu denen der Nutzer Zugangsdaten hinterlegt hat.
5.7.1Integration in das Fototagebuch

Für die Verwendung des Activity-Viewcontrollers müssen Sie zunächst einmal die Daten in einem Array bereitstellen, die der Nutzer der App über den Controller teilen kann. Im Fototagebuch sind das der Text und das Bild eines Eintrags. Die Methode activityItems in der Klasse DiaryEntryViewController erstellt ein Array mit diesen Einträgen, wobei sie jedoch nur vorhandene Einträge einfügt.
- (NSArray *)activityItems {
NSMutableArray *theItems = [NSMutableArray array];
Medium *theMedium = [self.diaryEntry
mediumForType:kMediumTypeImage];
NSString *theText = self.diaryEntry.text;
if(theMedium != nil) {
CGFloat theScale = [[UIScreen mainScreen] scale];
UIImage *theImage = [UIImage imageWithData:
theMedium.data scale:theScale];
[theItems addObject:theImage];
}
if([theText length] > 0) {
[theItems addObject:theText];
}
return [theItems copy];
}
Listing 5.72 Bereitstellen der Daten für den Activity-Viewcontroller
Die Implementierung der Methode activityItems reicht als Basis für die Anzeige des Activity-Viewcontrollers aus. Sie übergeben dieses Array dazu bei der Erzeugung an die Initialisierungsmethode initWithActivityItems:applicationActivities: des Activity-Viewcontrollers, den Sie über die Methode presentViewController:animated:completion: anzeigen.
- (IBAction)shareDiaryEntry:(id)inSender {
NSArray *theItems = self.activityItems;
UIActivityViewController *theController =
[[UIActivityViewController alloc]
initWithActivityItems:theItems
applicationActivities:nil];
[self presentViewController:theController
animated:YES completion:NULL];
}
Listing 5.73 Anzeige des Activity-Viewcontrollers
Der Activity-Viewcontroller schränkt die Aktivitäten in der Auswahl auch abhängig von den zur Verfügung gestellten Einträgen ein. Wenn der Tagebucheintrag zum Beispiel kein Foto enthält, zeigt die Auswahl die Aktivitäten Bild sichern, Kontakt zuweisen [Anm.: Dieser Dienst weist ein Bild einem Eintrag im Adressbuch zu.] und Drucken nicht an, da diese Aktivitäten für Texte sinnlos sind.
Sie können die Aktivitäten in der Auswahl des Activity-Viewcontrollers über seine Property excludedActvityTypes auch einschränken. Dazu übergeben Sie dieser Property ein Array mit den Namen der Aktivitäten, die der Controller nicht anzeigen soll. Beispielsweise unterbinden Sie dem Controller aus Listing 5.73 das Kopieren in die Zwischenablage und das Drucken über die folgende Zeile:
theController.excludedActivityTypes =
@[UIActivityTypeCopyToPasteboard, UIActivityTypePrint];
Listing 5.74 Kopieren in die Zwischenablage und Drucken ausschalten
5.7.2Eigene Aktivitäten bereitstellen
Objekte der Klasse UIActivity repräsentieren die Aktivitäten in einem Activity-Viewcontroller. Zwar haben Sie keinen Zugriff auf die Systemaktivitäten, wie Twitter oder Drucken, dafür können Sie jedoch über Unterklassen von UIActivity Ihrer App applikationsspezifische Aktivitäten bereitstellen, um beispielsweise weitere soziale Netzwerke oder andere Dienste einzubinden.
Applikationsspezifische Aktivitäten
Sie können Ihre Apps mit beliebig vielen Aktivitäten erweitern. Allerdings ist es leider zurzeit nicht möglich, diese Aktivitäten auch anderen Apps zur Verfügung zu stellen oder spezielle Aktivitäten für andere Apps anzubieten; Sie können also beispielsweise keine Aktivität »Ins Fototagebuch einfügen« für andere Apps bereitstellen.
Das Fototagebuch enthält eine applikationsspezifische Aktivität: Der Nutzer kann Einträge aus dem Tagebuch in den Dokumentenordner der App exportieren. Die Implementierung dieser Aktivität übernimmt die Klasse DiaryEntryExportActivity, die Objekte der Klasse DiaryEntry als Einträge verwendet. Für die Einbindung müssen Sie die Methoden activityItems und shareDiaryEntry: aus Listing 5.72 und Listing 5.73 erweitern. Zu den Daten für den Activity-Viewcontroller fügen Sie zunächst den aktuellen Tagebucheintrag hinzu.
- (NSArray *)activityItems {
NSMutableArray *theItems = [NSMutableArray array];
Medium *theMedium = [self.diaryEntry
mediumForType:kMediumTypeImage];
NSString *theText = self.diaryEntry.text;
if(theMedium != nil) {
CGFloat theScale = [[UIScreen mainScreen] scale];
UIImage *theImage = [UIImage
imageWithData:theMedium.data scale:theScale];
[theItems addObject:theImage];
}
if([theText length] > 0) {
[theItems addObject:theText];
}
[theItems addObject:self.diaryEntry];
return [theItems copy];
}
Listing 5.75 Daten für den Activity-Viewcontroller erweitern
Außerdem muss der Activity-Viewcontroller eine Exportaktivität bei der Initialisierung übergeben bekommen. Der zweite Parameter der Methode initWithActivityItems:applicationActvities: dient zur Übergabe eines Arrays mit applikationsspezifischen Aktivitäten. Die Klasse DiaryEntryExportActivity besitzt eine Property storyboard, aus dem sie Viewcontroller der App nachlädt. Dieser Property müssen Sie vor der Anzeige des Activity-Viewcontrollers ein Storyboard zuweisen (siehe Listing 5.76).
- (IBAction)shareDiaryEntry:(id)inSender {
DiaryEntryExportActivity *theActivity =
[[DiaryEntryExportActvity alloc] init];
NSArray *theItems = self.activityItems;
UIActivityViewController *theController =
[[UIActivityViewController alloc]
initWithActivityItems:theItems
applicationActivities:@[theActivity]];
theActivity.storyboard = self.storyboard;
[self presentViewController:theController animated:YES
completion:NULL];
}
Listing 5.76 Einbindung einer applikationsspezifischen Aktivität
Jede applikationsspezifische Aktivität muss einen Typ, einen Titel und ein Bild über die Methoden activityType, activityTitle beziehungsweise activityImage bereitstellen. Dabei ist der Typ eine frei wählbare Zeichenkette, die allerdings nicht mit den Systemtypen übereinstimmen sollte.
NSString * const kActivityTypeExport =
@"de.cocoaneheads.DiaryEntryExportActvity";
- (NSString *)activityType {
return kActivityTypeExport;
}
- (NSString *)activityTitle {
return NSLocalizedString(@"Export", @"Activity Title");
}
- (UIImage *)activityImage {
return [UIImage imageNamed:@"export"];
}
Listing 5.77 Bereitstellung von Typ, Titel und Bild
Der Activity-Viewcontroller fragt alle verfügbaren Aktivitäten über die Methode canPerformWithActivityItems:, ob sie seine Einträge verarbeiten können. Die Klasse DiaryEntryExportActivity verarbeitet nur Einträge mit der Klasse DiaryEntry. Sie können diese Überprüfung mit Hilfe der Methode isKindOfClass: über eine Schleife oder ein Prädikat realisieren. Die App verwendet die zweite Möglichkeit, um die Vielseitigkeit von Prädikaten zu demonstrieren.
Wenn Sie Prädikate über filteredArrayUsingPredicate: auswerten, dürfen Sie auch Methoden der Objekte in dem Array verwenden, die einen Aufrufparameter haben. Beispielsweise filtern Sie folgendermaßen alle Objekte der Klasse DiaryEntry aus dem Array inItems:
NSPredicate *thePredicate = [NSPredicate predicateWithFormat:
@"self isKindOfClass:%@", [DiaryEntry class]];
NSArray *theEntries =
[inItems filteredArrayUsingPredicate:thePredicate];
Listing 5.78 Elemente nach ihrer Klasse filtern
Über die Methode evaluateWithObject: können Sie auch ein Prädikat auf einem Objekt auswerten; diese Methode liefert Ihnen das Ergebnis als booleschen Wert. Sie können mit Hilfe des ANY-Operators entscheiden, ob irgendein Element in dem Array die Klasse DiaryEntry implementiert. Damit lässt sich die Methode canPerformWithActivityItems: recht einfach umsetzen:
- (BOOL)canPerformWithActivityItems:(NSArray *)inItems {
NSPredicate *thePredicate = [NSPredicate
predicateWithFormat:@"ANY self isKindOfClass:%@",
[DiaryEntry class]];
return [thePredicate evaluateWithObject:inItems];
}
Listing 5.79 Kann die Aktivität einen Eintrag verarbeiten?
Wenn der Nutzer einen Tagebucheintrag exportieren will, soll ihn die Aktivität vorher fragen, welche Teile er exportieren möchte und unter welchem Namen er sie im Dokumentenordner ablegen möchte. Dazu muss die Aktivität einen Dialog anzeigen; dafür sieht Apple einen modalen Viewcontroller vor, den Sie über die Methode activityViewController bereitstellen können. Wenn die Methode activityViewController der Aktivität nil liefert, ruft Cocoa Touch stattdessen die Methode performActivity auf.
Die Export-Aktivität lädt einen Navigationcontroller mit einem Viewcontroller aus dem Storyboard nach. Diese Initialisierungsschritte finden Sie in der Methode prepareWithActivityItems: der Klasse DiaryEntryExportActivity. Außerdem ermittelt diese Methode den Tagebucheintrag aus den Einträgen und übergibt ihn an den Viewcontroller für die Exportparameter.
- (void)prepareWithActivityItems:(NSArray *)inItems {
DiaryEntryExportActvity *theActivity =
[[DiaryEntryExportActvity alloc] init];
NSArray *theItems = self.activityItems;
UIActivityViewController *theController =
[[UIActivityViewController alloc]
initWithActivityItems:theItems
applicationActivities:@[theActivity]];
theActivity.storyboard = self.storyboard;
[self presentViewController:theController animated:YES
completion:NULL];
}
Listing 5.80 Vorbereitung des Viewcontrollers für die Exportparameter
Der Export-Viewcontroller zeigt das Bild, den Text und einen Button für die Tonaufnahme an, sofern der Tagebucheintrag diese Elemente enthält. Zu jedem Element zeigt er außerdem einen Schalter an, über den der Nutzer festlegen kann, ob er das entsprechende Element exportieren möchte. Außerdem gibt es ein Textfeld, um den Namen für die exportierten Dateien festzulegen. Abbildung 5.28 zeigt den Export-Viewcontroller in Aktion.
Abbildung 5.28 Der Dialog für die Exportparameter
Nach der Methode prepareWithActivityItems: zeigt der Activity-Viewcontroller den Export-Viewcontroller an, und dieser übernimmt das Regiment. Er initialisiert die Views für die Elemente und aktiviert beziehungsweise deaktiviert die Export-Schalter. Da das alles auf bereits besprochenen Themen beruht, verzichten wir hier auf eine genauere Darstellung.
Der Nutzer kann nur dann Dateien exportieren, wenn er einen Namen im Textfeld angegeben hat und mindestens einen Schalter aktiviert hat. Der Export-Viewcontroller soll nur in dieser Situation den Button zum Sichern freigeben und diesen ansonsten sperren. Dazu muss der Controller mitbekommen, wann sich der Zustand des Textfeldes und der Schalter ändert. Im Falle der Schalter reicht eine Action-Methode, die auf das Value-Changed-Ereignis lauscht. Auch das Textfeld kann über das Editing-Changed-Ereignis die Action-Methode benachrichtigen. Die Implementierung der Action-Methode finden Sie in Listing 5.81.
– (IBAction)updateSaveButton {
self.navigationItem.leftBarButtonItem.enabled =
self.nameField.text.length > 0 &&
(self.imageSwitch.on || self.textSwitch.on ||
self.soundSwitch.on);
}
Listing 5.81 Aktivierung des Sichern-Buttons
Wenn Sie das Textfeld antippen, öffnet Cocoa Touch es, und Sie können einen Namen
eingeben. Sobald Sie ein Zeichen eingeben haben, aktiviert die Action-Methode auch
den Button zum Sichern. Soweit funktioniert alles wie gewünscht. Allerdings können
Sie die Tastatur nicht mehr schließen, auch nicht über die -Taste.
Um die -Taste zum Schließen der der Tastatur zu verwenden, müssen Sie die Methode textFiledShouldReturn: des Textfeld-Delegates implementieren. Dort können Sie über die Methode endEditing: die Tastatur schließen. Über den Rückgabewert dieser Methode legen Sie fest, ob Cocoa
Touch noch das Standardverhalten für die
-Taste ausführen soll. Da das nicht notwendig ist, geben Sie hier NO zurück.
- (BOOL)textFieldShouldReturn:(UITextField *)inTextField {
[self updateSaveButton];
[self.view endEditing:YES];
return NO;
}
Listing 5.82 Schließen der Tastatur beim Drücken der Return-Taste
Für die Implementierung fehlen jetzt nur noch das Sichern der Daten und Schließen des Export-Viewcontrollers. Die Sicherung erfolgt in einem Unterverzeichnis des Dokumentenordners der App. Das Unterverzeichnis bekommt dabei den Namen, den der Nutzer in das Textfeld eingegeben hat. Dazu legt die App vor dem Sichern dieses Verzeichnis über die Methode createDirectoryAtPath:withIntermediateDirectories:attributes:error: der Klasse NSFileManager an.
NSFileManager *theManager =
[NSFileManager defaultManager];
NSString *thePath =
[theManager applicationDocumentsDirectory];
NSError *theError = nil;
thePath = [thePath stringByAppendingPathComponent:
self.nameField.text];
if([theManager createDirectoryAtPath:thePath
withIntermediateDirectories:YES attributes:nil
error:&theError]) {
// Sichern der Daten
}
else {
NSLog(@"save: error=%@", theError);
}
Listing 5.83 Verzeichnis für den Export anlegen
Das Sichern ist für alle Einträge relativ ähnlich. Während der Viewcontroller die Daten der Medien über writeToFile:atomically: sichert, verwendet er für den Text die Methode writeToFile:atomically:encoding:error: der Klasse NSString. Bei beiden Methoden bewirkt der Wert YES für den Parameter atomically, dass die Methode die Daten atomar schreibt; dabei erzeugt die Methode erst eine Zwischendatei mit den Daten, und wenn sie das erfolgreich geschafft hat, benennt sie die Datei um, und zwar mit dem Namen im ersten Parameter.
DiaryEntry *theEntry = self.diaryEntry;
if(self.imageSwitch.on) {
NSData *theImage = [[theEntry
mediumForType:kMediumTypeImage] data];
NSString *theFile = [thePath
stringByAppendingPathComponent:@"image.jpg"];
[theImage writeToFile:theFile atomically:YES];
}
if(self.textSwitch.on) {
NSString *theFile = [thePath
stringByAppendingPathComponent:@"text.txt"];
[theEntry.text writeToFile:theFile atomically:YES
encoding:NSUTF8StringEncoding error:NULL];
}
if(self.soundSwitch.on) {
NSData *theAudio = [[theEntry
mediumForType:kMediumTypeAudio] data];
NSString *theFile = [thePath
stringByAppendingPathComponent:@"sound.caf"];
[theAudio writeToFile:theFile atomically:YES];
}
}
Listing 5.84 Sichern der Daten eines Tagebucheintrags
Das Schließen des Viewcontrollers muss über die Methode activityDidFinish: der Aktivität erfolgen, da diese Methode der Aktivität das Ende der Operation anzeigt. Dabei gibt der Parameter YES an, dass der Viewcontroller die Operation erfolgreich durchgeführt hat, und NO zeigt einen Fehler oder einen Abbruch an. Listing 5.85 zeigt die Integration der Aufrufe dieser Methode in die Action-Methoden save: und cancel: des Export-Viewcontrollers.
- (IBAction)save:(id)inSender {
...;
if([theManager createDirectoryAtPath:...]) {
...;
[self.activity activityDidFinish:YES];
}
else {
NSLog(@"save: error=%@", theError);
[self.activity activityDidFinish:NO];
}
}
- (IBAction)cancel:(id)inSender {
[self.activity activityDidFinish:NO];
}
Listing 5.85 Beenden der Aktivität
Damit haben Sie die wesentlichen Schritte für die Implementierung einer eigenen Aktivität kennengelernt, für die Sie eine Unterklasse der Klasse UIActivity erstellen müssen. Den Ablauf der Operation implementieren Sie dann auf Basis von Viewcontrollern. Alternativ können Sie über die Methode performActivity auch Implementierungen realisieren, bei denen keine Nutzinteraktionen notwendig sind.
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.