Eigene XIB in UITableViewCells nutzen

Oft kommt es vor dass das man ein eigenes Layout für TableView-Zellen benutzen möchte.
Hier möchte ich eine gute Möglichkeit schildern dies zum größten Teil über den Interface-Builder zu erledigen.

Zuerst muss man eine neue Datei vom Typ NSObject” anlegen, diese nennen wir z.B. “EigeneZelle”.

Danach erstellen wir auch eine “Exmpty XIB”-Datei, diese nennen wir auch “EmptyZelle.xib”, also so wie unsere Klassen-Datei.

Die Klassen-Datei muß umgeändert werden:

#import <Foundation/Foundation.h>
@interface EigeneZelle : NSObject {
}

ändern in:

#import <UIKit/UIKit.h>
@interface EigeneZelle : UITableViewCell {
}

Somit ist nun klar dass es sich um eine abgewandelte TableViewCell-Klasse handelt.

In diese Klasse fügt man noch “wie gewohnt” die IBOutlets und Properties für div. Labels und andere Views ein die man im Interface-Builder nutzen möchte.

Editiert nun die “EigeneZelle.xib” im interface-Builder, zieht ein “UITableViewCell” in das Dokumenten-Fenter und ändert die Klasse von “UITableViewCell” auf “EigeneZelle”. Nun müssen hier nur noch die Outlets verbunden werden.

Um das Ganze nutzen zu können muss man in der tableView:cellForRowAtIndexPath: Methode folgende Zeile nutzen:

EigeneZelle *cell = (EigeneZelle *)[tableView dequeueReusableCellWithIdentifier:@"EigeneZelleIdentifier"];
if (cell == nil) {
	NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"EigeneZelle" owner:self options:nil];
	for (id oneObject in nib)
		if ([oneObject isKindOfClass:[EigeneZelle class]])
			cell = (EigeneZelle *)oneObject;
}
// Zelle konfigurieren ...
cell.eigenesLabel.text = @"Test";
Veröffentlicht unter iOS | Verschlagwortet mit , , | Hinterlasse einen Kommentar

Probleme beim Ausführen auf iOS 3.1.x Geräten

Heute hatte ich das Problem dass mein Code nicht auf einem iOS 3.1.x Gerät lief. Ich hatte in meiner App bereits mehrere “neue” Klassen wie den UIPopoverController genutzt. Bei Ausführen auf einem iPod mit iOS 3.1.3 kam ein Fehler beim Start, ungefähr so:

dyld: Symbol not found: _OBJC_CLASS_$_UIPopoverController
Referenced from: /var/mobile/Applications/8F91E6E5-5174-3D11A025-777CFDEF1C05.iTANSS.app/iTANSS
Expected in: /System/Library/Frameworks/UIKit.framework/UIKit
in /var/mobile/Applications/8F91E6E5-5174-3D11A025-777CFDEF1C05/iTANSS.app/iTANSS
mi_cmd_stack_list_frames: Not enough frames in stack.

Der Fehler lässt sich relativ einfach beheben. Einfach das Framework UIKit.h “weak” linken. Dies lässt sich einstellen wenn man links in der Liste unter “Targets” das gewünschte Target auswählt und dann rechts die Einstellung unter “UIKit.h” vornimmt.

Ein weiterer Punkt könnte sein wenn als Instanzvariablen direkt Objekte wie UIPopoverController angeben wurden. Dies führt auch immer zu Compiler-Fehlern. Aber in Anwendungen die sowohl auf dem iPad als auch auf dem iPhone laufen sollen müssen diese ja trotzdem genutzt werden. Der Workaround ist auch relativ einfach: Anstelle dass man eine Instanz-Variable als “UIPopoverController” angebigt diese einfach als “id” definieren :-)

Veröffentlicht unter iOS | Hinterlasse einen Kommentar

Operationen parallel laufen lassen (Multi-Threading)

Im folgenden möchte ich beschrieben wie man seine Software aufwerten kann um ggf. längere Rechen-Operationen in einem eigenen Thread auszuführen. Als Beispiel: In einer meiner Apps führe ich eine längere Operation aus die diverse Termine im Kalender des Benutzers anlegegt. Mich hat daran stark genervt dass in der Zwischenzeit die Benutzeroberfläche “hängt”.
Die Lösung zu diesem Problem ist recht simpel: Mit “Multi-Threading” lässt sich der Task welcher die Termine anleget im Hintergrund ausführen und man die Software ganz normal weiter bedienen.

Dies geschieht mit ein paar neuen Objekten, hier der Beispiel-Code:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSInvocationOperation *termineAnlegenOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(legeTerminserieAnBackgroundTask) object:nil];
[queue addOperation:termineAnlegenOperation];
[termineAnlegenOperation release];
[queue release];

Das Besipiel ist recht schnell erklärt:

  • Zuerst wird ein NSOperationQueue-Objekt erstellt. Dies stellt eine Liste von Aufgaben dar welche nacheinander abgearbeitet werden sollen – jedoch in einem seperaten Thread (quasi im Hintergrund)
  • Dann legen wir ein NSInvocationOperation-Objekt an, dies ist die einfachste Möglichkeit eine Operation zu bestimmen welche im Hintergrund laufen soll. In der Init-Methode wird angegeben welche Methode (selectior) in welchem Objekt aufgerufen werden soll
  • Dann wird die NSInvocationOperation der NSOperationQueue hinzugefügt und dadurch auch schon abgearbeitet. Man kann das Programm ganz normal weiter bedienen ohne dass die Ausführung verzögert wird.
  • Zuguterletzt werden die Objekte released
Veröffentlicht unter iOS | Verschlagwortet mit , | Hinterlasse einen Kommentar

Abfragen ob Code auf iPhone oder iPad läuft

Ich spiele gerade damit herum Universal-Apps auf dem iPad zu erstellen.
Hier ein kleiner Code-Schnipsel um abzufragen ob der iOS-Code auf einem iPhone oder iPad ausgeführt wird.:

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
    // Code für iPad hier
} else {
    // Code für iPhone hier
}

Ein ausführlicher und sehr interessanter Artikel findet Ihr hier: Artikel von Ray Wenderlich.

Veröffentlicht unter iOS | Hinterlasse einen Kommentar

JSON-Webantworten parsen

In den letzten Artikeln habe ich gezeigt wie man von Webservern Informationen ausliest. Im einfachsten Fall werden “normale” Zeichenketten heruntergeladen. Sollen jedoch mehrere Werte zurückgegeben werden eignet sich dafür am besten die JSON-Notation. In PHP gibt es ja die Funktion json_encode() um ein Array in JSON umzuwandeln.

Hier zeige ich nun ein Beispiel wie eine JSON-Antwort in Objective-C am besten wieder eingelesen werden und auf die jeweiligen Schlüssel-Wert-Paare zugegrifen werden kann. Ihr benötigt hierfür das Framework JSON welches ich an diesen Artikel angefügt habe. Das Framework wird kostenlos von Google zur Verfügung gestellt!

#import "JSON.h"

// Test-JSON-String, am besten als Antwort von einer Webseite holen
NSString *response = @"{\"id\":3, \"name\":\"Test\"}";

// Parser-Objekt erstellen
SBJSON *jsonParser = [SBJSON new];

// Dictionary erstellen um besser auf die Werte zugreifen zu können
NSDictionary *jsonDict = (NSDictionary *)[jsonParser objectWithString:response];

// Werte auslesen
NSInteger idWert = [(NSString *)[row valueForKey:@"id"] intValue];
NSString *nameWert = (NSString *)[row valueForKey:@"name"];

[jsonParser release];

Download-Link für JSON-Framework: JSON_2.2.3.dmg

Veröffentlicht unter iOS | Verschlagwortet mit | Hinterlasse einen Kommentar

Einfach Web-Requests erstellen

In diesem Artikel will ich ein Beispiel geben wie man einen einfachen Web-Request abschickt um eine Antwort von einem Webserver zu erhalten. Dies kann man nutzen um ein Skript auf einem Webserver aufzurufen welches einem gewissen Informationen (als Text oder HTML) zurückgibt. Die Info wird dann als String gespeichert:

[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSInteger testParameter = 10;
NSString *urlString = [NSString stringWithFormat:@"http://www.testdomain.local/index.php?t=%i", testParameter];
NSURL *url = [NSURL URLWithString:urlString];
NSString *antwort = [[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
// Hier irgendetwas mit der Antwort machen
[antwort release];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

Die nun folgende Methode ist etwas fortgeschrittener. Mit dieser Methode kann man Anfragen an einen Server senden und mittels POST auch Werte übertragen. Die Methode ist ähnlich wie wenn man in einer HTML-Seite das form-Tag benutzt um Formular-Daten an ein Skript zu übertragen. Hier die Lösung in Objective-C:

// Netzwerk-Aktivitäts-Indikator setzen
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;

// URL erstellen
NSString *urlString = [NSString stringWithString:@"http://www.testdomain.local/upload.php"];
NSURL *url = [NSURL urlWithString:urlString];

// URL-Request-Objekt erstellen
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
[request setHTTPMethod:@"POST"];

// Hier wird ein NSMutableData-Objekt erstellt welches die POST-Werte aufnimmt
NSMutableData *body = [NSMutableData data];
NSString *postWerte = @"seite=1&name=Sven Buchberger&test=0";
[body appendData:[postWerte dataUsingEncoding:NSUTF8StringEncoding]];	

// Die POST-Werte dem Request-Objekt übergeben
[request setHTTPBody:body];
[leistungWerte release];

// Abfrage ans Web senden und Rückgabe in Variable speichern
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
Veröffentlicht unter iOS | Verschlagwortet mit | 1 Kommentar

Singleton-Klassen erstellen

Wenn Ihr das Singleton-Pattern einsetzen möchtet könnt Ihr dies auch ganz einfach umsetzen. Als Beispiel hier eine kleine “Registry”-Klasse in der Informationen gespeichert werden die an div. Stellen der Applikation genutzt werden:

Registry.h

@interface Registry : NSObject {
  NSString *info;
  NSInteger nummer;
}

@property (nonatomic, copy) NSString *info;
@property NSInteger nummer;

+ (Registry *)instance;

Registry.m

#import "Registry.h"

@implementation Registry
@synthesize info, nummer;

+ (Registry *)instance {
  static Registry* instance;
  @synchronized(self) {
    if(!instance) {
      instance = [[Registry alloc] init];
    }
  }
  return instance;
}

In der Applikation kann man dann immer wie folgt auf das Objekt zugreifen (es muss aber immer in der jeweiligen Klasse importiert werden dass es genutzt werden kann):

Registry *reg = [Registry instance];

// Beispiel Zugriff auf Variablen
NSString *variableAusRegistry = reg.info;
Veröffentlicht unter iOS | Verschlagwortet mit | Hinterlasse einen Kommentar

Datum Komponenten

Um aus einem Datum z.B. Stunden und Minuten zu extrahieren (oder Tag, Monat, Jahr etc.) muss dies in iOS über die Objekte NSCalender und NSDateComponents durchgeführt werden:

NSDate *aktuellesDatum = [NSDate date];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
unsigned unitFlags = NSHourCalendarUnit | NSMinuteCalendarUnit;
NSDateComponents *comps = [gregorian components:unitFlags fromDate:tmpPauseAnfang];
NSInteger stunde = [comps hour];
NSInteger minuten = [comps minute];
[gregorian release];

Zuerst wird ein Kalender-Objekt erstellt:

NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

Wir wählen hier aus welche Komponenten aus dem Datum herausgeholt werden sollen:

unsigned unitFlags = NSHourCalendarUnit | NSMinuteCalendarUnit;

Diese Zeile holt aus dem Datum “tmpPauseAnfang” die Datumskomponenten heraus die eine Zeile weiter oben definiert wurden:

NSDateComponents *comps = [gregorian components:unitFlags fromDate:tmpPauseAnfang];

Und auf die einzelnen Komponenten kann man ganz einfach zugreifen:

NSInteger stunde = [comps hour];
NSInteger minuten = [comps minute];

In dem folgenden Beispiel bauen wie ein Datum aus Datumskomponenten zusammen, dies geschieht ebenfalls über die Objekte NSCalendar und NSDateComponents:

NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setHour:10];
[comps setMinute:8];
[comps setDay:1];
[comps setMonth:4];
[comps setYear:2010];
NSDate *neuesDatum = [gregorian dateFromComponents:comps];
[comps release];
[gregorian release];

Die o.g. Beispiele haben also die gleiche Funktion wie die Funktionen date() und mktime() in PHP.

Veröffentlicht unter iOS | Verschlagwortet mit , | Hinterlasse einen Kommentar

Datum formatieren

Um ein Datum korrekt formatiert darzustellen empfiehlt sich der Einsatz des Objektes NSDateFormatter. Hier erst einmal ein recht einleuchtendes Beispiel:

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"dd.MM.yyyy"];
NSDate *datum = [NSDate date];
NSString *stringVonDatum = [dateFormatter stringFromDate:datum];
[dateFormatter release];

Mit der Methode setDateFormat: können auch andere Formate definiert werden, hier die wichtigsten Platzhalter:
dd = Tag
MM = Monat
yyyy = Jahr
HH = Stunde
mm = Minute

Tip: Wenn Ihr eine Applikation schreibt die Ihr lokalisieren wollt dann empfiehlt es sich stark “Standard-Wert” für das Format zu nutzen, z.B.:

[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setDateStyle:NSDateFormatterShortStyle];

Der Vorteil ist dass diese sich nach dem Ändern der Region anpassen!

Veröffentlicht unter iOS | Verschlagwortet mit | Hinterlasse einen Kommentar