在 SwiftUI 中实现拍照功能,需要结合 UIViewControllerRepresentable
和 UIImagePickerController
来实现相机功能。下面是一个详细的示例,展示如何使用 SwiftUI 来实现拍照功能:
1. 创建一个 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) {}
}
2. 使用 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
来存储选取的图片。 - 当点击 “Take Photo” 按钮时,显示
ImagePicker
。 -
sheet
修饰符用于在isImagePickerPresented
为true
时呈现ImagePicker
。
- 使用
通过这种方式,你可以在 SwiftUI 应用中实现拍照功能。请注意,拍照功能只能在真实设备上使用,因为模拟器不支持摄像头。
在 iOS 应用中访问相机需要在 Info.plist
文件中添加 NSCameraUsageDescription
键,以告知用户为什么需要访问相机。否则,应用在尝试访问相机时会崩溃。
添加 NSCameraUsageDescription
到 Info.plist
-
打开你的 Xcode 项目。
-
在项目导航中,找到并点击你的
Info.plist
文件。 -
在
Info.plist
中,添加一个新的键值对:- 键:
NSCameraUsageDescription
- 值:解释你的应用需要使用相机的原因,比如 “This app requires access to the camera to take photos.”
- 键:
示例:
<key>NSCameraUsageDescription</key>
<string>This app requires access to the camera to take photos.</string>
如果你使用 Xcode 的图形化界面,可以按以下步骤操作:
- 打开
Info.plist
文件。 - 点击右键选择 “Add Row”。
- 在新行的键列中输入
NSCameraUsageDescription
。 - 在值列中输入对用户的说明,比如 “This app requires access to the camera to take photos.”
更新后的示例代码
完成上述步骤后,你可以重新运行之前的代码:
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
,应用在请求访问相机时会向用户显示一条提示,解释为什么需要访问相机,从而避免因未声明权限而导致的崩溃。