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:
- Zunächst kommt es zur Erstellung einer neuen
UIImagePickerController
-Instanz. Diese Instanz nutzen wir, um die Kameraansicht aus SwiftUI heraus einzublenden. - 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 vonUIImagePickerController
den passenden Wert dieses Media Types zu. Den erhält man am einfachsten aus dem UniformTypeIdentifiers-Framework (siehe auch entsprechendeimport
-Anweisung zu Beginn des Listings). Der passende Media Type für Bilder lässt sich mithilfe des BefehlsUTType.image.identifier
auslesen.
Wichtig: Da diemediaTypes
-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. - Zu guter Letzt legt man über die
sourceType
-Property desUIImagePickerController
noch fest, dass man auf die Kamera zugreifen möchte. Alternativ könnte man hier auch mithilfe der OptionenphotoLibrary
undsavedPhotosAlbum
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 KlassePHPickerViewController
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.
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.
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
Weiterführende Links zum Artikel
- Beispiel-Code auf GitHub: https://github.com/Sillivan88/TS-CameraView-Example
- Ergänzendes Video auf YouTube: https://www.youtube.com/watch?v=ToKyVbLEcik
Schreibe einen Kommentar