Ein anfaengerfreundliches Tutorial fuer alle

Eine Kamera-App zu erstellen ist eine typische Aufgabe fuer einen iOS-Entwickler, die dir irgendwann begegnen koennte. Es gibt viele Tutorials da draussen, die dir genau das zeigen. Ich habe auch eines, das mir besonders gefallen hat, im Ressourcenabschnitt verlinkt.

Fast alle, die ich finden konnte, verwenden UIKit und AVFoundation. Natuerlich wollte ich etwas in SwiftUI, und so einfach wie moeglich.

Auf Apples Seite zu Cameras and Media Capture steht ueber AVFoundation:

Build a custom camera UI to integrate shooting photos or videos into your app’s user experience.

Was ich wollte, war keine benutzerdefinierte Kamera-App, sondern eine sehr einfache, direkt einsatzbereit. Dieselbe Seite sagt auch:

To instead let the user capture media with the system camera UI within your app, see UIImagePickerController.

Nun, da haben wir es. Lass uns das etwas weiter erkunden.

Es gibt zwei Moeglichkeiten, ein Bild mit UIImagePicker auszuwaehlen:

  1. Indem man ein Foto aus der Fotobibliothek auswaehlt
  2. Indem man ein Foto mit der eingebauten Kamera des Telefons aufnimmt

Die erste ist fuer UIImagePicker veraltet und wurde durch PHPicker ersetzt.

Dieses Tutorial erklaert den zweiten Weg und fuehrt dich durch alle Schritte, die erforderlich sind, um eine leichtgewichtige Kamera-App fuer dein iOS-Geraet mit so wenig Code wie moeglich zu erstellen.

Je nach deinem Anwendungsfall koennte die Verwendung von UIImagePicker voellig ausreichend sein. Es besteht keine Notwendigkeit, eine komplexere Loesung mit AVFoundation zu verwenden.

Ein Nachteil von UIImagePicker ist, dass es nur im Hochformat aufnehmen kann. Hoffentlich wird der Querformat-Modus irgendwann unterstuetzt.

Wenn deine Anforderungen mit dieser Einschraenkung in Ordnung sind, so wird die App aussehen:

Sieht vertraut aus? Sicher!

Projekt-Setup

Lass uns mit der Erstellung eines neuen iOS-Projekts beginnen.

Dann waehle die Optionen. Nichts Besonderes hier - wir werden eine SwiftUI-Anwendung schreiben und brauchen weder Core Data noch Tests.

Pruefe die bereitgestellten Informationen und erstelle die Projektdateien.

Lass uns zum ersten Mal kompilieren und starten. Wir landen bei der Standard-”Hello World”-Anwendung.

Alles bereit! Bereit fuer die Entwicklung!

Entwicklung

Wie erwaehnt, werden wir UIImagePickerController verwenden. Leider koennen wir ihn nicht direkt als SwiftUI-View verwenden.

Gluecklicherweise gibt es UIViewControllerRepresentable, das hilft, UIKit-View-Controller in SwiftUI zu integrieren. Ich habe das Prinzip der Verwendung von UIKit-Views in SwiftUI in diesem Artikel erklaert:

https://itnext.io/using-uiview-in-swiftui-ec4e2b39451b

Zuerst brauchen wir eine neue Datei, die unsere CameraView enthalten wird:

Und natuerlich speichern wir sie entsprechend:

Schliesslich landen wir hier und sind bereit, unseren Code hinzuzufuegen:

Alles, was wir in unserer CameraView brauchen, ist der folgende Code:

import Foundation
import UIKit
import SwiftUI
 
struct CameraView: UIViewControllerRepresentable {
 
 typealias UIViewControllerType = UIImagePickerController
 
 
 func makeUIViewController(context: Context) -> UIViewControllerType {
  let viewController = UIViewControllerType()
  viewController.delegate = context.coordinator
  viewController.sourceType =.camera
  return viewController
 }
 
 func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
 
 }
 
 func makeCoordinator() -> CameraView.Coordinator {
  return Coordinator(self)
 }
}
 
extension CameraView {
 class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
  var parent: CameraView
 
  init(_ parent: CameraView) {
   self.parent = parent
  }
 
  func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
   print("Cancel pressed")
  }
 
  func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
   if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
    UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
   }
  }
 }
}

Fast geschafft, wir muessen nur noch die CameraView zu unserer ContentView hinzufuegen:

struct ContentView: View {
 var body: some View {
  CameraView()
 }
}

Das war’s. Nichts mehr hinzuzufuegen. Oh, warte mal…

Wenn wir die App zum ersten Mal ausfuehren, koennten wir diese Fehlermeldung bekommen:

2021-12-20 09:41:07.629751+0100 CameraTutorial[17091:5169429] [access] This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSCameraUsageDescription key with a string value explaining to the user how the app uses this data.

Lass uns die Nutzungsbeschreibung setzen:

Versuche es erneut. Sieht gut aus.

Wir muessen “OK” druecken und dann koennen wir die Kamera verwenden:

Wir koennen jetzt:

  • Den Blitz aktivieren
  • Zwischen Weitwinkel, normal, Zoom (wenn dein Geraet sie unterstuetzt) und der Selfie-Linse wechseln
  • Den “Abbrechen”-Button druecken
  • Den Ausloser druecken, um ein Foto zu machen

Nur zur Info, wenn wir das Geraet ins Querformat drehen, ist alles durcheinander:

Nichts, was wir nicht schon wussten. Hoffentlich wird Apple den Querformat-Modus hinzufuegen oder eine andere fertige Loesung bereitstellen.

Wenn wir den Ausloese-Button druecken, bekommen wir Folgendes:

Wir koennen jetzt entweder “Retake” druecken, um zur Kamera zurueckzukehren, oder “Use Photo” druecken, um es zu speichern.

Der Coordinator ist verantwortlich fuer die Reaktion auf “Cancel” und “Use Photo”.

Fuer Cancel haben wir diesen Delegaten implementiert, der nur “Cancel pressed” ausgibt:

func imagePickerControllerDidCancel(_ picker: UIImagePickerController)

Fuer Use Photo haben wir implementiert:

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any])

Dort speichern wir das Bild im Fotoalbum mit:

UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)

Bei der ersten Verwendung koennten wir einen weiteren Fehler bekommen:

This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSPhotoLibraryAddUsageDescription key with a string value explaining to the user how the app uses this data.

Daher muessen wir eine weitere Nutzungsbeschreibung setzen:

Dann bekommen wir folgenden Dialog:

Bestaetigen, indem du den Zugriff erlaubst.

Jetzt kannst du die Fotos-App pruefen, um zu sehen, ob das Bild dort ist.

Hoffentlich hat alles funktioniert und du kannst das neu aufgenommene Foto in deiner Fotos-App finden.

Fazit

Das war’s in etwa. Wir sind fertig fuer heute mit der minimalistischsten Kamera-App, die ich herausfinden konnte.

Danke fuers Lesen!

Ressourcen