在 SwiftUI 中实现拍照功能,需要结合 UIViewControllerRepresentable 和 UIImagePickerController 来实现相机功能。下面是一个详细的示例,展示如何使用 SwiftUI 来实现拍照功能:
ImagePicker 组件首先,创建一个 UIViewControllerRepresentable 结构,用于包装 UIImagePickerController。
import SwiftUI
import UIKit
struct ImagePicker: UIViewControllerRepresentable {
@Binding var selectedImage: UIImage?
@Environment(\.presentationMode) var presentationMode
var sourceType: UIImagePickerController.SourceType = .camera
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
let parent: ImagePicker
init(parent: ImagePicker) {
self.parent = parent
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[.originalImage] as? UIImage {
parent.selectedImage = image
}
parent.presentationMode.wrappedValue.dismiss()
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
parent.presentationMode.wrappedValue.dismiss()
}
}
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}
func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.delegate = context.coordinator
picker.sourceType = sourceType
return picker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
}
ImagePicker 组件接下来,在你的主视图中使用 ImagePicker 组件来实现拍照功能。
import SwiftUI
struct ContentView: View {
@State private var isImagePickerPresented = false
@State private var selectedImage: UIImage?
var body: some View {
VStack {
if let selectedImage = selectedImage {
Image(uiImage: selectedImage)
.resizable()
.scaledToFit()
.frame(width: 300, height: 300)
} else {
Text("No Image Selected")
.frame(width: 300, height: 300)
.background(Color.gray)
}
Button(action: {
isImagePickerPresented = true
}) {
Text("Take Photo")
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
.padding()
}
.sheet(isPresented: $isImagePickerPresented) {
ImagePicker(selectedImage: $selectedImage)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
ImagePicker 组件:
UIViewControllerRepresentable 协议用来将 UIImagePickerController 引入 SwiftUI。makeUIViewController 和 updateUIViewController 方法创建和更新 UIImagePickerController。Coordinator 类作为 UIImagePickerController 的代理,处理图片选择和取消操作。ContentView:
@State 属性包装变量 isImagePickerPresented 来控制 ImagePicker 的显示。@State 属性包装变量 selectedImage 来存储选取的图片。ImagePicker。sheet 修饰符用于在 isImagePickerPresented 为 true 时呈现 ImagePicker。通过这种方式,你可以在 SwiftUI 应用中实现拍照功能。请注意,拍照功能只能在真实设备上使用,因为模拟器不支持摄像头。
在 iOS 应用中访问相机需要在 Info.plist 文件中添加 NSCameraUsageDescription 键,以告知用户为什么需要访问相机。否则,应用在尝试访问相机时会崩溃。
NSCameraUsageDescription 到 Info.plist打开你的 Xcode 项目。
在项目导航中,找到并点击你的 Info.plist 文件。
在 Info.plist 中,添加一个新的键值对:
NSCameraUsageDescription<key>NSCameraUsageDescriptionkey>
<string>This app requires access to the camera to take photos.string>
如果你使用 Xcode 的图形化界面,可以按以下步骤操作:
Info.plist 文件。NSCameraUsageDescription。完成上述步骤后,你可以重新运行之前的代码:
import SwiftUI
import UIKit
struct ImagePicker: UIViewControllerRepresentable {
@Binding var selectedImage: UIImage?
@Environment(\.presentationMode) var presentationMode
var sourceType: UIImagePickerController.SourceType = .camera
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
let parent: ImagePicker
init(parent: ImagePicker) {
self.parent = parent
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[.originalImage] as? UIImage {
parent.selectedImage = image
}
parent.presentationMode.wrappedValue.dismiss()
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
parent.presentationMode.wrappedValue.dismiss()
}
}
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}
func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.delegate = context.coordinator
picker.sourceType = sourceType
return picker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
}
struct ContentView: View {
@State private var isImagePickerPresented = false
@State private var selectedImage: UIImage?
var body: some View {
VStack {
if let selectedImage = selectedImage {
Image(uiImage: selectedImage)
.resizable()
.scaledToFit()
.frame(width: 300, height: 300)
} else {
Text("No Image Selected")
.frame(width: 300, height: 300)
.background(Color.gray)
}
Button(action: {
isImagePickerPresented = true
}) {
Text("Take Photo")
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
.padding()
}
.sheet(isPresented: $isImagePickerPresented) {
ImagePicker(selectedImage: $selectedImage)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
通过添加 NSCameraUsageDescription,应用在请求访问相机时会向用户显示一条提示,解释为什么需要访问相机,从而避免因未声明权限而导致的崩溃。