Kamerazugriff via SwiftUI – Teil 1

Grundlagen

Bis heute fehlt SwiftUI (leider) ein direkter Zugriff auf die Kamera des iPhone. Erfreulicherweise ist es aber ein überschaubarer Aufwand, mittels Representable die UIKit-Elemente zum Kamerazugriff auch in SwiftUI verfügbar zu machen.

Die Grundlage stellt diesbezüglich die Klasse UIImagePickercontroller dar. Diese Klasse ist bereits seit iOS 2 Teil von UIKit und ermöglicht es in wenigen Schritten, die Kamera eines iPhone aufzurufen, ein Bild zu erstellen und dieses im Anschluss weiter zu verarbeiten.

Da es sich bei UIImagePickerController um eine Subklasse von UIViewController handelt, lässt er sich mithilfe des UIViewControllerRepresentable-Protokolls in SwiftUI einbinden. Die Grundlage dafür zeigt das folgende Listing:

// Listing 1: Grundlegende Integration von UIImagePickerController als UIViewControllerRepresentable
import SwiftUI
import UniformTypeIdentifiers

struct TSCameraView: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> UIImagePickerController {
        let imagePickerController = UIImagePickerController()
        imagePickerController.mediaTypes = [UTType.image.identifier]
        imagePickerController.sourceType = .camera
        return imagePickerController
    }
    
    func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
}

Wie bei allen Representables sind zwei Methoden des UIViewControllerRepresentable-Protokolls entscheidend: Mit makeUIViewController(context:) erstellt man eine Instanz des gewünschten UIViewController und liefert sie als Ergebnis zurück. In diesem Fall nutzen wir die Methode demnach, um eine UIImagePickerController-Instanz zu erstellen. Das wird bereits beim Rückgabetyp der Methode ersichtlich.

In der Implementierung der Methode finden sich drei wichtige Befehle:

  1. Zunächst kommt es zur Erstellung einer neuen UIImagePickerController-Instanz. Diese Instanz nutzen wir, um die Kameraansicht aus SwiftUI heraus einzublenden.
  2. Als unterstützte Media Types wird zunächst der Einfachheit halber festgelegt, dass nur Bilder (keine Videos) über die Kamera aufgenommen werden können. Dazu weist man der mediaTypes-Property von UIImagePickerController den passenden Wert dieses Media Types zu. Den erhält man am einfachsten aus dem UniformTypeIdentifiers-Framework (siehe auch entsprechende import-Anweisung zu Beginn des Listings). Der passende Media Type für Bilder lässt sich mithilfe des Befehls UTType.image.identifier auslesen.
    Wichtig: Da die mediaTypes-Property ein Array erwartet (beim Kamerazugriff kann nämlich mehr als nur ein Media Type unterstützt werden), muss der genannte Befehl auch entsprechend in ein Array verpackt werden.
  3. Zu guter Letzt legt man über die sourceType-Property des UIImagePickerController noch fest, dass man auf die Kamera zugreifen möchte. Alternativ könnte man hier auch mithilfe der Optionen photoLibrary und savedPhotosAlbum auf die Foto-Library des iPhone zugreifen. Doch das ist an dieser Stelle nicht relevant, es geht ausschließlich um den Kamerazugriff. (Darüber hinaus sollte man seit iOS 14 für den Zugriff auf die Foto-Library die neue Klasse PHPickerViewController des PhotosUI-Frameworks nutzen, aber das ist ein Thema für einen anderen Tag.)

Den so erstellen und konfigurierten UIImagePickerController liefert die makeUIViewController(context:)-Methode als Ergebnis zurück. Abschließend muss noch zwingend eine Implementierung der Methode updateUIViewController(_:context:) ergänzt werden. Diese Methode des UIViewControllerRepresentable-Protokolls wird immer dann aufgerufen, wenn es zu einer Aktualisierung des Representables kommt. Das ist für uns aber an dieser Stelle nicht relevant, weshalb die Implementierung der Methode schlicht leer bleibt.

Kamera aus SwiftUI heraus als Sheet einblenden

Mit dieser grundlegenden Konfiguration testen wir bereits einmal, wie sich unser Representable in der Praxis schlägt. Dazu blenden wir TSCameraView als Sheet nach Betätigung eines Buttons ein:

// Listing 2: Einblenden des Representables als Sheet
struct ContentView: View {
    @State private var showCameraView = false
    
    var body: some View {
        Button("Show camera") {
            showCameraView = true
        }
        .sheet(isPresented: $showCameraView) {
            TSCameraView()
        }
    }
}

Bevor man diesen Code testet, ist eine Sache aber noch essenziell: Innerhalb der Info.plist-Datei ist der NSCameraUsageDescription-Schlüssel mitsamt passendem String zu ergänzen. Dieser String beschreibt, warum eine App Zugriff auf die Kamera wünscht/benötigt. iOS blendet automatisch einen Alert mit diesem String ein, wenn es zum ersten Mal zu einem Zugriff auf die Kamera aus einer App heraus kommt.

Es ist zwingend notwendig, den NSCameraUsageDescription-Key mitsamt einer passenden Beschreibung in der Info.plist-Datei des iOS-Projekts zu ergänzen, möchte man auf die Kamera zugreifen.
Es ist zwingend notwendig, den NSCameraUsageDescription-Key mitsamt einer passenden Beschreibung in der Info.plist-Datei des iOS-Projekts zu ergänzen, möchte man auf die Kamera zugreifen.

Fehlt der NSCameraUsageDescription-Schlüssel in der Info.plist, kommt es beim Zugriff auf die Kamera zum Absturz der App; ein Szenario, das wir verständlicherweise gerne vermeiden möchten. 🙂

Mit diesen beiden Anpassungen (Code + Info.plist) lässt sich aber tatsächlich bereits die Kameraansicht als Sheet einblenden. Wie beschrieben fragt iOS zuvor noch, ob ein Zugriff auf die Kamera gewährt wird.

Beim erstmaligen Zugriff auf die Kamera erscheint die Meldung, die man in Form des NSCameraUsageDescription-Keys in der Info.plist-Datei hinterlegt hat.
Beim erstmaligen Zugriff auf die Kamera erscheint die Meldung, die man in Form des NSCameraUsageDescription-Keys in der Info.plist-Datei hinterlegt hat.

Interessant: Verweigert man den Kamerazugriff, ist das Kamera-Sheet trotzdem zu sehen, jedoch bleibt das Bild schlicht dauerhaft schwarz.

Fazit und nächste Schritte

Dieser Artikel hat gezeigt, wie man einen UIImagePickerController in seinen Grundzügen als Representable in SwiftUI implementiert und aufruft. Die Basis, um die Kamera aus SwiftUI heraus in Form eines Sheets aufzurufen, ist damit geschaffen.

In den folgenden Artikeln werden wir jenen UIImagePickerController weiter konfigurieren. Wir werden die aufgenommenen Bilder verarbeiten und anzeigen. Dazu kommen Optimierungen, um den Zugriff auf den UIImagePickerController bestmöglich zu regeln.

Den Code zu diesem Artikel findet ihr auf GitHub (siehe weiterführende Links am Ende).

Euer Thomas


Kommentare

2 Antworten zu „Kamerazugriff via SwiftUI – Teil 1“

  1. Klasse. Unbedingt weiterführen.
    Wie kann ich mittels Geste vergrößern? Wie ein Foto aufhellen?
    Wie Farben erkennen?

    1. Vielen Dank für das Feedback (und die vielen Fragen!). Die Reihe geht auch definitiv weiter, allerdings zunächst einmal rein mit Fokus auf die Einbindung und Konfiguration von UIImagePickerController in SwiftUI. Schauen wir mal, was das am Ende alles abdeckt und welche Punkte womöglich in separaten Artikeln/Videos behandelt werden.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert