Symbol not found: __NSConcreteStackBlock

Dieser Fehler kann auftreten wenn Ihr eine App auf einem älteren iPhone mit iOS 3.1.3 compilieren wollt. Vermutlich kommt der Fehler daher weil Ihr irgendwo im Code “Blocks” nutzt.

Die Lösung ist sehr einfach: Einfach unter “Build Phases” bei “Link Binary with Libraries” die Library “libSystem.B.dylib” als “Optional” hinzufügen.

Ergänzung: Nachdem ich dies so genutzt habe lies sich die App unter Xcode4 ganz normal für iOS Geräte compilieren, brachte aber einen Compiler-Fehler wenn ich sie im Simulator compilieren wollten.

Die Lösung gab es natürlich auf .

Ins deutsche übersetzt müsst Ihr folgendes machen:

  • Das Terminal aufrufen und in den Ordner /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/usr/lib wechseln
  • Einen symbolischen Link anlegen mittels: sudo ln -s libSystem.dylib libSystem.B.dylib
  • Dann sollte die App wieder im Simulator laufen :-)
Veröffentlicht unter iOS | Verschlagwortet mit | Hinterlasse einen Kommentar

TabBarItem programmatisch entfernen

Wenn man in seinem XIB-File einen UITabBarController hinzugefügt hat kann es ja sein dass man gewissen Tab-Items doch nicht angezeigt haben möchte. Programmatisch kann man nicht einfach die items-Property der UITabBar modifizieren falls die TabBar einem TabBarController angehört. Dies muss dann wie folgt gemacht werden (Wir gehen davon aus dass tabController ein gültiger UITabBarController ist):

NSMutableArray *neueVC = [NSMutableArray arrayWithArray:tabController.viewControllers];
[neueVC removeObjectAtIndex:2];
tabController.viewController = neueVC;
Veröffentlicht unter iOS | Verschlagwortet mit , | Hinterlasse einen Kommentar

“Normale” und “Lite” Version einer App im gleichen Projekt erstellen

Ab und an kommt es vor dass man im gleichen Projekt ohne großes Duplizieren von Code zwei unterschiedliche Versionen einer App erstellen möchte. Beispiel hier: eine App mit dem vollen Funktionsumfang und eine “Lite” Version der App (mit eingeschränkter Funktionalität, dafür evtl. aber kostenlos.

Eine Möglichkeit dies zu realisiern ist z.B. mit Pre-Processor-Makros und einer einfachen Abfrage im Programmcode.

Hier die Vorgehensweise:

- Duplizieren des vorhandenen “Targets”, so dass man ein 2. Target namens “App copy” hat.
- In diesem neuen Target kann man komplett anderer Bundle Identifier etc. nutzen um die App unter einem anderen Namen im App-Store zu vertreiben
- Unter “Build Settings” des Targets einfach nach “other c” filtern und auf “Other C Flags” klicken:

- Hier einfach “-DKUNDE_VERSION” oder “-DLITE_VERSION” einfügen
- Das war’s auch schon :-)

Im Programmcode an sich kann man ganz einfach zwischen beiden Versionen unterscheiden, in dem man z.B. folgenden Programmcode verwendet:

#ifdef KUNDE_VERSION
// Code welcher in der LITE-Version ausgeführt werden soll
#else
// Code welcher in der "normalen" Version ausgeführt werden soll
#endif
Veröffentlicht unter iOS | Hinterlasse einen Kommentar

PHPMailer: Fehler in MAC OS-Mail-Clients bei Mails mit eingebetteten Bildern und normalen Attachments

Dieser Fehler hat mich Nerven gekostet, aber nach langer Suche im Internet bin ich auf ein paar brauchbare Resultate gestossen.

Zuerst der Fehler:
Wenn Ihr mit der Library/Klasse “PHPMailer” in PHP arbeitet und Mails damit versendet, so kommen Mails mit scheinbar “fehlendem Attachment” im Apple Mail-Client an wenn Ihr in der gleichen Mail auch mit eingebetteten Bildern (“Embedded Images”) arbeitet.

Lösung:
- Ihr benötigt zuerst die aktuelle Version vom PHPMailer, Version 5.1, diese könne Ihr hier herunterladen
- Es gibt zu diesem Problem ein extra Patch-File welches unter hier herunterzuladen ist
- Wendet einfach den Patch auf die Datei an (Unter MAC OS hierfür das Terminal öffnen, beide Dateien in ein Verzeichnis verschieben und in der Kommandozeile “patch class.phpmailer.php phpmailer.patch” ausführen)

Nun werden die Boundaries wieder korrekt verschachtelt und die Mail sollte im Apple-Mail-Client und anderen Mail-Clients wieder korrekt lesbar sein (inkl. aller Attachments)

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

Mehr als ein UIBarButtonItem als rightNavigationItem nutzen

In einigen Projekten habe ich gesehen dass in der rechten oberen Ecke (im NagigationBar) zwei Buttons vorhanden sind, auch mit schön “abgerundeten Ecken”. Mittels des navigationItem.rightBarButtonItem kann man ja leider nur ein UIBarButtonItem zuweisen.

Wenn Ihr mehrere Buttons haben wollt könnt Ihr ein UISegmentedControll erstellen und dies quasi als UIBarButtonItem nutzen. Hier der Beispiel-Code:

    // Segmented Control erstellen
    UISegmentedControl* segmentedControl = [[UISegmentedControl alloc] initWithItems:[NSArray array]];

    // Man soll nicht sehen dass ein Segment selektiert wurde
    [segmentedControl setMomentary:YES];

    // Fügt Segmente hinzu, alternativ kann man auch insertSegmentWithImage:atIndex:animated: nutzen um Bilder zu benutzen
    [segmentedControl insertSegmentWithTitle:@"Historie" atIndex:0 animated:NO];
    [segmentedControl insertSegmentWithTitle:@"Bearbeiten" atIndex:1 animated:NO];

    // Wird für Autoresizing genutzt
    segmentedControl.autoresizingMask = UIViewAutoresizingFlexibleWidth;

    // Setzt des Stil der SegmentedControl
    segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;

    // Aktion welche erfolgt wenn man auf die SegmentedControl klickt
    [segmentedControl addTarget:self action:@selector(eineAktion:) forControlEvents:UIControlEventValueChanged];

    // Erstellt ein BarButtonItem und fügt es als rightBarButtonItem hinzu
    UIBarButtonItem *segmentBarItem = [[UIBarButtonItem alloc] initWithCustomView: segmentedControl];
    self.navigationItem.rightBarButtonItem = segmentBarItem;

    // Objekt releasen
    [segmentedControl release];
Veröffentlicht unter iOS | Verschlagwortet mit | 1 Kommentar

Fehler beim compilen: Duplicate symbol _OBJC_IVAR

Heute hatte ich einen Fehler beim compilieren meines Projektes: Duplicate symbol _OBJC_IVAR.

Ich hatte dummerweise ein .m-File mittels #import ins Projekt eingebunden. Kann ja mal aufgrund der Auto-Completion passieren :-)

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

Kontakte aus dem Adressbuch auswählen

Ich habe etwas herumprobiert wie man sich mit Hilfe des “AddressBook“-Frameworks Adressen aus dem Adressbuch herausholen kann. Hier ein kurzes Beispiel welches den Vorgang dokumentiert.

Ich habe ein neues Projekt (view-based Application) angelegt und ein ganz einfaches Interface gestaltet: Auf den View habe ich einen Button angelegt – und wenn man auf diesen klickt wird die Action getAddress: aufgerufen.
So sieht das Header-File meines ViewControllers aus:

#import <UIKit/UIKit.h>
#import <AddressBook/AddressBook.h>
#import <AddressBookUI/AddressBookUI.h>

@interface ABPickerTestViewController : UIViewController <ABPeoplePickerNavigationControllerDelegate> {

}
- (IBAction)getAddress:(id)sender;

Man muss also das Protokoll “ABPeoplePickerNavigationControllerDelegate” implementieren. Hier der komplett Code der .m-Files. Ich erkläre diesen später:

- (IBAction)getAddress:(id)sender {
    ABPeoplePickerNavigationController *pp = [[ABPeoplePickerNavigationController alloc] init];
    pp.peoplePickerDelegate = self;
    [self presentModalViewController:pp animated:YES];
    [pp release];
}

#pragma mark -
#pragma mark ABPeoplePickerNavigationControllerDelegate

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
      shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
    // "Normale" Infos
    NSString *vorname = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
    NSString *nachname = (NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
    NSString *firma = (NSString *)ABRecordCopyValue(person, kABPersonOrganizationProperty);

    NSLog(@"Vorname: %@", vorname);
    NSLog(@"Nachname: %@", nachname);
    NSLog(@"Firma: %@", firma);

    [vorname release];
    [nachname release];
    [firma release];

    // Infos zu Telefonnummern
    ABMultiValueRef multi = ABRecordCopyValue(person, kABPersonPhoneProperty);
    for (CFIndex i = 0; i < ABMultiValueGetCount(multi); i++) {

        NSString *telLabel = (NSString *)ABMultiValueCopyLabelAtIndex(multi, i);
        NSString *telNr = (NSString *)ABMultiValueCopyValueAtIndex(multi, i);

        NSString *typ = @"";
        if ([telLabel rangeOfString:@"Work"].location != NSNotFound) {
            typ = @"Arbeit";
        }
        else if ([telLabel rangeOfString:@"Mobile"].location != NSNotFound) {
            typ = @"Mobil";
        }
        else if ([telLabel rangeOfString:@"Home"].location != NSNotFound) {
            typ = @"Heim";
        }
        else {
            typ = @"unbekannt";
        }

        NSLog(@"Label: %@ Nr: %@ Typ: %@", telLabel, telNr, typ);

        [telLabel release];
        [telNr release];
    }
    CFRelease(multi);

    // Infos zu Adressen
    ABMultiValueRef address = ABRecordCopyValue(person, kABPersonAddressProperty);
    for (CFIndex i = 0; i < ABMultiValueGetCount(address); i++) {

        NSString *addrLabel = (NSString *)ABMultiValueCopyLabelAtIndex(address, i);
        NSDictionary *addrDict = (NSDictionary *)ABMultiValueCopyValueAtIndex(address, i);

        NSString *strasse = [addrDict objectForKey:@"Street"];
        NSString *plz = [addrDict objectForKey:@"ZIP"];
        NSString *ort = [addrDict objectForKey:@"City"];

        NSLog(@"Label: %@ Str: %@, PLZ: %@, Ort: %@", addrLabel, strasse, plz, ort);

        [addrLabel release];
        [addrDict release];
    }
    CFRelease(address);

    // ModalViewController dismissen
    [self dismissModalViewControllerAnimated:YES];
    return NO;
}

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
     shouldContinueAfterSelectingPerson:(ABRecordRef)person
                               property:(ABPropertyID)property
                             identifier:(ABMultiValueIdentifier)identifier
{
    return NO;
}

- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker
{
    [self dismissModalViewControllerAnimated:YES];
}

Nehmen wir uns zuerst einmal die Action vor die aufgerufen wird wenn der Button geklickt wird:

- (IBAction)getAddress:(id)sender {
    ABPeoplePickerNavigationController *pp = [[ABPeoplePickerNavigationController alloc] init];
    pp.peoplePickerDelegate = self;
    [self presentModalViewController:pp animated:YES];
    [pp release];
}

Hier wird zuerst ein Objekt initialisiert welches im Framework “AddressBookUI” vorkommt, nämlich den “ABPeoplePickerNavigationController“. Ihr kennt diesen Controller, das ist der gleiche wenn man das Adressbuch öffnet und in den Kontakten sucht.
Diesem Controller wird ein Delegate bekannt gemacht, der Einfachheit halber nehmen wir das gleiche Objekt (self), im Interface haben wir ja auch gesagt dass das Protokoll “ABPeoplePickerNavigationControllerDelegate” eingehalten wird.
Zuguterletzt wird der Controller als “ModalView” angezeigt.

Das Verhalten des “ABPeoplePickerNavigationController” richtet sich nach den Delegate-Methoden welche wir eingeführt haben.

Fangen wir mit den einfachsten an:

- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker
{
    [self dismissModalViewControllerAnimated:YES];
}

Diese Delegate-Methode wird aufgerufen wenn der “Abbruch” Button geklickt wird, in unserem Falle entfernen wir einfach den ModalView.

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
     shouldContinueAfterSelectingPerson:(ABRecordRef)person
                               property:(ABPropertyID)property
                             identifier:(ABMultiValueIdentifier)identifier
{
    return NO;
}

Diese Delegate Methode wird aufgerufen wenn der User eine der User-Eigenschaften anklickt. In unserem Fall tritt dies nicht ein da die nächste Delegate-Methode gar nicht zulässt dass man in einen Benutzer reinspringt:

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
      shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
    // Jede Menge Code hier, s. oben

    // ModalViewController dismissen
    [self dismissModalViewControllerAnimated:YES];
    return NO;
}

Diese Delegate-Methode wird aufgerufen sobald man in der Kontakt-Liste einen Kontakt anklickt. Die Rückgabe NO gibt an dass nicht in den Benutzer reingesprungen wird sondern er direkt ausgewählt wird. Im Quellcode sind einige Beispiele aufgeführt wie man an Informationen des Eintrags kommt, ich hoffe Ihr könnt damit was anfangen :-)

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

Prüfen ob Substring in einem String vorkommt

Hier eine einfach Methode um zu prüfen ob ein String in einem anderen STring vorkommt:

if ([haystackString rangeOfString:@"needle"].location != NSNotFound) {
    // Das Wort "needle" kommt in dem String haystackString vor!
}
Veröffentlicht unter iOS | Verschlagwortet mit | Hinterlasse einen Kommentar

NSString (HTML-Inhalt) in UIWebView laden

Wenn man in einem NSString den HTML-Ingalt gespeichert hat kann man dies folgendermaßen in eine UIWebView laden:

NSString *imagePath = [[NSBundle mainBundle] resourcePath];
imagePath = [imagePath stringByReplacingOccurrencesOfString:@"/" withString:@"//"];
imagePath = [imagePath stringByReplacingOccurrencesOfString:@" " withString:@"%20"];
NSURL *baseURL = [NSURL URLWithString:[NSString stringWithFormat:@"file:/%@//", imagePath]];

[webView loadHTMLString:htmlString baseURL:baseURL];
Veröffentlicht unter iOS | Verschlagwortet mit | Hinterlasse einen Kommentar

Fixed und Flexible Space UIBarButtonItems erstellen

Hier der Code um UIBarButtonItems mit Fixed und Flexible Space zu erstellen:

UIBarButtonItem *fixedSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
Veröffentlicht unter iOS | Verschlagwortet mit | Hinterlasse einen Kommentar