Übersetzung von Storyboard-Texten

Um in Xcode Lokalisierungen bzw. Übersetzungen von (statischen) Texten im Storyboard (z.B. Menü-Punkte oder allgemein Label-Texte) zu hinterlegen, bedarf es sehr wenigen Handgriffen. Folgende Schritte sind dazu notwendig:

  • Wählt das Projekt aus und schaut ob im Tab „Info“ unter dem Punkt „Localizations“ das Häkchen bei „Use Base Internationalization“ gesetzt ist (falls nicht: setzen)
  • Durch Klick auf das Plus-Symbol (etwas oberhalb von „Use Base Internationalization„) kann eine neue Sprache hinzugefügt werden
  • Nach Bestätigung des Dialoges wurden sowohl für das Storyboard („Main.storyboard“) als auch für den Launchscreen („Launchscreen.storyboard„) eine zusätzliche String-Datei angelegt (zu erkennen an dem kleinen Dreieck neben der jeweiligen Datei)
  • In diesen Dateien befinden alle bisher existierenden Titel und Textelemente der Views und können beliebig angepasst werden
  • Möchte man die Sprache für das Storyboard („Main.storyboard“) wieder deaktivieren, muss man lediglich im „File Inspector“ unter dem Punkt „Localization“ bei der Sprache entsprechend das Häkchen entfernen (die Sprachdatei geht dann nicht verloren, sie wird lediglich nicht mehr angezeigt)

Übersetzung von Storyboard-Texten weiterlesen

Hintergrund eines TabBarItems ändern

Wie man die Navigationsleiste (bzw. NavigationBar) in Swift ändert wurde bereits einem früheren Beitrag vorgestellt. Auf gleiche Weise kann man statt der NavigationBar auch die TabBar anpassen. Dafür benutzt man anstatt der Klasse UINavigationBar einfach die Klasse UITabBar.

Etwas tricky wird es, wenn man den jeweiligen Hintergrund eines einzelnen Bar Items anpassen möchte. Swift bietet dafür (leider) keine hauseigene Methode à la setBarItemBackground. Stattdessen muss man, mit Hilfe der Methode selectionIndicatorImage, den Umweg über eine Hintergrundgrafik gehen:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
  // Set different background for selected bar item
  // 1. Assign tab bar
  let tabBarController: UITabBarController = self.window?.rootViewController as! UITabBarController
  let tabBar: UITabBar = tabBarController.tabBar

  // 2. Set background image
  tabBar.selectionIndicatorImage = UIImage(named:"selected_baritem_background")
return true
}

Der Nachteil an diesem Weg ist, dass die Hintergrunddatei theoretisch von den verschiedenen Breiten und der Anzahl der Elemente der TabBars (iPhone 5, iPhone 6, …) abhängig ist. Die Breite der iPhone 5 TabBar beträgt 320px. Bei 3 TabBarItems muss also die Hintergrundgrafik 320/3 = 106 Pixel Breit sein (Höhe beträgt 49 Pixel).

Dynamische Daten + NumberOfSections in TableView und CoreData

Ein kleines Problem was mir direkt am Anfang bei der Arbeit mit dynamischen Daten (Quelle: CoreData) und TableView auf die Füße gefallen ist, war die Angabe der Anzahl der Bereiche (=“Sections“) einer Tabelle. Hierfür stellt Swift die Methode numberOfSectionsInTableView bereit:

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        // Return the number of sections.
        return 1
    }

Wenn man statt der 1 eine 0 zurückgibt, existiert gar kein Bereich und man bekommt eine leere View zu sehen.

Einen Segue (Übergang) stoppen um eine Alert-Box anzuzeigen

Bevor man einem User erlaubt Daten zu ändern bzw. zu speichern, möchte man ihn eventuell noch auf unvollständige Daten aufmerksam machen. Dafür bietet sich die Methode shouldPerformSegueWithIdentifier an. Diese Methode wird aufgerufen, bevor die App von einer View in eine andere View wechselt. Man hat hier als Entwickler die Möglichkeit einzugreifen um z.B. zu prüfen, ob alle User-Daten valide sind.
Möchte man die Logik innerhalb der Methode nur auf bestimmte Segues (=“Übergänge“) anwenden, muss man diesem Segue im Interfacebuilder einen entsprechenden Identifier zuweisen. Auf diesen Identifier kann man dann direkt in der Methode zugreifen.

override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool {
  // Check for desired identifier
  if identifier == "myIdentifier" {
    // Stop the segue
    return false
  } else {
    // Continue to next view for every other segue
    return true
  }
}

In diesem Beispiel wird der Übergang zur nächsten View unterbrochen (sprich: Der User verbleibt auf der aktuellen View), wenn der Segue/Übergang mit dem Identifier „myIdentifier“ ausgelöst wurde.

Dieses Prozedere kann man ausnutzen und dem User z.B. eine Alert-Box anzeigen:

override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool {
  // Check if name is not empty
  if self.nameTextBox == "" {
    // Show alert
    let alert = UIAlertController(title: "Empty name", message: "Please enter a name", preferredStyle: UIAlertControllerStyle.Alert)
    alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
    self.presentViewController(alert, animated: true, completion: nil)

    // Stop the segue
    return false
  } else {

    // Save user data and/or perform other logic
    ...

    // Continue to next view
    return true
  }
}

Wichtig:
Neben shouldPerformSegueWithIdentifier gibt es auch noch die Methode prepareForSegue, die einem ähnlichen Zweck dient. Allerdings kann hier der Übergang nicht abgebrochen werden, wie es bei shouldPerformSegueWithIdentifier der Fall ist.

Anpassen der UINavigationBar

Um die Navigation einer App (Titel, Vor- und Zurück-Buttons, Hintergrundfarbe, …) individueller zu gestalten, kann man direkt auf die Klasse UINavigationBar zugreifen. In dieser Klasse wird die sprechende Methode UINavigationBar.appearance() zur Verfügung gestellt.
Damit die Navigation in allen Views (sofern gewünscht!) gleich aussieht, bietet es sich an, die Layout-Änderungen in der Klasse AppDelegate.swift vorzunehmen. Dort sollte man den Code direkt in der folgenden Methode implementieren:

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.
        
        // Implement layout customization
        // Navigation bar color
        UINavigationBar.appearance().barTintColor = UIColor.blueColor()
        // Nav item color
        UINavigationBar.appearance().tintColor = UIColor.whiteColor()
        // Title color
        UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName: UIColor.whiteColor()]
        // END OF Implement layout customization
        
        return true
    }

Arbeiten mit User-Settings

In Swift ist es sehr einfach mit User-Settings zu arbeiten. Das kann sehr nützlich sein, wenn man z.B. für eine App allgemeine User-Informationen oder -Einstellungen speichern möchte.
Die User-Informationen können mit Hilfe der Funktion NSUserDefaults.standardUserDefaults() gespeichert und gelesen werden. Mir persönlich ist dieses immer etwas zu lang zum Arbeiten, weshalb ich es in eine entsprechende Variable auslagere:

let userDefaults = NSUserDefaults.standardUserDefaults()

Um mit dieser Methode Boolwerte lesen und schreiben zu können, werden die Methoden setBool() bzw. boolForKey zur Verfügung gestellt:

// Ist der Switch vom User eingeschaltet worden (="on"), wird "true" als Wert gespeichert
// Ansonsten "false"
if self.switch.on {  // self.switch = Switch-Element  
    self.userDefaults.setBool(true, forKey: "switch")
} else {
    self.userDefaults.setBool(false, forKey: "switch")
}

Ähnliche Funktionen gibt es auch für Integer (setInteger), Float (setFloat), Double (setDouble) und URL (setURL). Für Strings muss auf die Methode setObject zurückgegriffen werden.

Differenz zwischen zwei Daten berechnen

Die Differenz zwischen zwei Daten kann in Swift relativ einfach berechnet werde. Im folgendem Beispiel wird mit dem selben Code die Differenz der Stunden, sowie der Minuten zwischen dem aktuellem Zeitpunkt und einem anderen Zeitpunkt berechnet. Egal ob dieser Zeitpunkt in der Vergangenheit oder der Zukunft liegt. Der zweite Zeitpunkt (=pickedDate) wird in unserem Beispiel durch einen DatePicker zur Verfügung gestellt.

// Hole aktuellen Zeitpunkt (today) und User-definierten Zeitpunkt
let today = NSDate()
let pickedDate = self.datePicker.date // Vom User vorgegeben
let calendar = NSCalendar.currentCalendar()

// Berechne wieviele Stunden bzw. Minuten zwischen beiden Daten liegen
let timeComponentFromDiffDate = calendar.components([NSCalendarUnit.Hour, NSCalendarUnit.Minute], fromDate: today, toDate: pickedDate, options: [])
print("\(timeComponentFromDiffDate.hour) Stunden liegen zwischen beiden Daten")
print("\(timeComponentFromDiffDate.minute!) Minuten liegen zwischen beiden Daten")

Wichtiger Hinweis: Die Stunden und Minuten in diesem Beispiel ergänzen sich nicht gegenseitig, sondern sind zwei unabhängige Werte. Beträgt die Differenz zwischen den angegebenen Daten zwei Stunden, bekommt man für den Minuten-Wert auch „120“ raus.

Den ersten Buchstaben eines Strings groß schreiben

Ab und an möchte man den ersten Buchstaben es Strings großschreiben. In Swift gibt es nativ keine Methode dafür, allerdings kann man z.B. mit Hilfe einer Extension eine entsprechende Methode allen Strings zur Verfügung stellen:

extension String {
    var capitalizeFirstLetter:String {
        var result = self
        result.replaceRange(startIndex...startIndex, with: String(self[startIndex]).capitalizedString)
        return result
    }
}

Sprache und Region auslesen

Die Sprache und Region eines Gerätes kann in Swift mit der Methode preferredLanguages der Klasse NSLocale ausgelesen werden. Das Ergebnis ist ein String, in der Sprache und Region durch ein Bindestrich getrennt sind. Für ein Gerät mit englischer Sprache, aber aus der Region „Deutschland“ sieht dieser String so aus: en-DE

Durch die Methode componentsSeparatedByString kann man das Ergebnis gleich als Array auslesen und so separat auf die Werte zugreifen:

let systemLocal = NSLocale.preferredLanguages()[0].componentsSeparatedByString("-")
print systemLocal[0] // en
print systemLocal[1] // DE