TabBar in Kombination mit einem SlideOut-Menü

Die TabBar-Navigation gehört bei der Swift-Entwicklung mit zum Standard. Wie man ein schönes und effektives SlideOut-Menü in seine App integriert, wurde bereits in einem früheren Artikel beschrieben. Beide Varianten lassen sich in wenigen Schritten kombinieren, so dass man bei Bedarf ein zusätzliches (Seiten-)Menü für einzelne Views dazu schalten kann. Dabei muss lediglich der TabBar-Controller als Front-Controller der SlideOut-Menüs gesetzt werden. Wer das ganze etwas schicker machen möchte, kombiniert die Seitennavigation mit einem Akkordeon-Menü.

iOS TabBar und SlideOut-Menü

Das entsprechende Beispiel-Projekt kann von Github geladen werden.

Akkordeon Menü erstellen

Ein simples aufklappbares Menü (aka „Akkordeon-Menü“) kann in Swift relativ einfach mit einer TableView erzeugt werden. Dazu gibt es bereits verschieden Code-Beispiele im Netz. Mir pers. hat dieser Artikel auf StackOverflow sehr gut gefallen (siehe Link), allerdings ist der von 2014. Da sich seitdem einige Sache in Swift geändert haben, hier die aktuell funktionierende Variante: Akkordeon-Menü in Swift

MD5 eines Strings erstellen und als Id nutzen

Da Swift standardmäßig keine MD5-Funktion anbietet, muss man sich mit einem kleinen Trick in Form einer Extension weiterhelfen:

// Create a MD5 out of a String
extension String  {
    var md5: String! {
        let str = self.cStringUsingEncoding(NSUTF8StringEncoding)
        let strLen = CC_LONG(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
        let digestLen = Int(CC_MD5_DIGEST_LENGTH)
        let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)
        CC_MD5(str!, strLen, result)
        let hash = NSMutableString()
        for i in 0..<digestLen {
            hash.appendFormat("%02x", result[i])
        }
        result.destroy()
        return String(format: hash as String)
    }
}

Wichtig:
Damit auf Typen wie CC_Long, CC_MD5 etc. zugegriffen werden kann, muss ein Bridging-Header erstellt werden, in dem folgende Codezeile stehen muss:
#import <CommonCrypto/CommonCrypto.h>

Der MD5-Hash kann z.B. verwendet werden, um für die entsprechenden Strings Ids zu vergeben (Objekte, Dictionaries, Arrays, …). Dies bietet sich an, da normale Objekt-Ids auch Slashes enthalten können, was bei Abfragen zu Problemen führen kann.
Aber aufgepasst: Die folgenden beiden Schreibweisen (für die selbe Objekt-Id!) ergeben unterschiedliche MD5-Strings:
let md5_1 = objectID.description.md5
let md5_2 = „\(objectID.description)“.md5

Ermitteln des aktuellen Zoom-Levels einer MapView

Um den aktuellen Zoom-Level einer MapView zu ermitteln, kann folgende Funktion genutzt werden:

// Get the current zoom level of the map
// mRect = self.map.visibleMapRect
// viewSizeInPixel = self.map.frame.size
func getZoomLevel(mRect: MKMapRect, viewSizeInPixel: CGSize) -> Int {
  let MAX_ZOOM = 20 // 20 is the maximum zoomlevel with MapKit
  var zoomLevel: Int = MAX_ZOOM
  let zoomScale: MKZoomScale = CGFloat(mRect.size.width) / viewSizeInPixel.width
  let zoomExponent: Double = log2(Double(zoomScale))

  zoomLevel = Int(MAX_ZOOM - ceil(zoomExponent))
  return zoomLevel
}

Die Original-Funktion (in Objective-C geschrieben) gibt es bei Stack Overflow, lediglich der Funktionsname wurde angepasst.

MapKit: Neue Koordinaten berechnen

Wer mit MapKit arbeitet und Koordinaten berechnen möchte, die z.B. xy Meter weit von bestehenden Koordinaten weg sind (die man z.B. durch eine Touch-Geste bekommen hat), der kann folgende Funktion nutzen:

    // Calculate new coordinates that are xy meters away from old coordinates
    func calculateNewCoordinatesByDistance(oldCoordinates: CLLocationCoordinate2D, latDistanceInMeter: Double, longDistanceInMeter: Double) -> CLLocationCoordinate2D? {
        var newCoordinates = CLLocationCoordinate2D()

        let tempRegion: MKCoordinateRegion = MKCoordinateRegionMakeWithDistance(oldCoordinates, latDistanceInMeter, longDistanceInMeter)
        let tempSpan: MKCoordinateSpan = tempRegion.span

        newCoordinates.latitude = oldCoordinates.latitude + tempSpan.latitudeDelta
        newCoordinates.longitude = oldCoordinates.longitude + tempSpan.longitudeDelta

        return newCoordinates
    }

Die Originalfunktion (in Objective-C) gibt es auf Stack Overflow, allerdings wurden die Variablennamen noch etwas sprechender gemacht.

Swift vs. Objective-C

Vor kurzem bin ich wieder über einen Artikel gestoßen, der 10 Vorteile von Swift gegenüber Objective-C hervorhebt (und der vor allem für Querein- und Umsteiger in der iOS-Entwicklung interessant sein könnte). Da dieser sehr schön und einfach zu lesen ist, möchte ich ihn nicht vorenthalten. Zusammengefasst bietet Swift dabei folgende Verbesserung gegenüber seinem Vorgänger:
Swift vs. Objective-C weiterlesen

Long Press Gesture einsetzen

Um zu erkennen, ob ein User lange mit dem Finger in einer View (z.B. auf einer Map) gedrückt hat, bietet Swift den Tap Gesture Recognizer bzw. den Long Press Gesture Recognizer. Der Recognizer kann entweder über das Storyboard eingebunden oder direkt im Code erstellt werden.

Storyboard:
Im Storyboard kann der Recognizer über die Objekt Bibliothek einfach in eine View reingezogen und via IBActions genutzt werden:

@IBAction func tapped(sender: UITapGestureRecognizer)
{
  print("Kurzer Drücker")
}
@IBAction func longPressed(sender: UILongPressGestureRecognizer)
{
  print("Langer Drücker")
}

Im Code
(Am Beispiel einer MapKitView)

let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(ViewController.handleLongPress(_:)))

longPressRecognizer.minimumPressDuration = 2.0
// self.map = @IBOutlet weak var map: MKMapView!
self.map.addGestureRecognizer(longPressRecognizer)

func handleLongPress(gestureRecognizer: UIGestureRecognizer) {
// Consider the states (.Began,.Ended, ...) otherwise the code will be execute multiple times
  if( gestureRecognizer.state == UIGestureRecognizerState.Began ) {
     print("So it begins ...")
  } else if( gestureRecognizer.state == UIGestureRecognizerState.Ended ) {
     print("So it ends ...")
  }
}

SlideOut-Navigation mit SWRevealController

Wer eine SlideOut Navigation in Swift einsetzen möchte, der kann auf das Projekt von John Lluch zurückgreifen. Das ist zwar in Objective-C geschrieben, kann allerdings – wie jeder Objective-C Code – via Bridging-Header eingebunden werden. Der Code selbst ist sehr gut dokumentiert und lesbar. Zusätzlich können im Issue-Bereich eigene Frage gestellt werden (die nach eigener Erfahrung sehr schnell beantwortet werden).

Das fertige Menü:
finished-swrevealviewcontroller-menu
SlideOut-Navigation mit SWRevealController weiterlesen

Guard Statement in Swift 2

Ab Swift 2 steht einem das Schlüsselwort guard zur Verfügung, welches in Ergänzung zur allseits bekannten if-Bedingung hinzugefügt wurde. Mit Hilfe von guard können Entwickler auf Bedingungen prüfen, die erwünscht sind anstatt nicht erwünscht, bedeutet: Der eigentliche Code wird nur ausgeführt, wenn die Bedingung nicht zutrifft.

Beispiel:

guard age > 21 else {
    return false
}
// Continue IF age is > 21

....

// Optional
guard let z = z where z > 0 else {
    // Requirements not met
    return false
}

// Work with z
print(z.name)

Vorteile beim Einsatz von guard:

  • Die eigentlichen Absichten des Entwicklers sind eindeutiger: Man teilt guard mit welcher Fall konkret eintreten soll.
  • Durch guard ausgepackte (=“unwrapped“) Optionals sind anschließend weiterhin verfügbar.
  • Der Code insgesamt ist kürzer und übersichtlicher (Stichwort: „Pyramid of Doom„)