这是我参与8月更文挑战的第15天,活动详情查看:8月更文挑战
一、目标
常规的布局,比如列表展示,比如菜单栏,在之前的章节都有多次演示,这里不再啰嗦,本节说一些新鲜的,可能在实现的过程中会遇到的问题。
SwiftUI
从发布到现在,经历了2个大的版本更迭(9月份会推出3.0,扩展大量新功能,此刻暂且不提
),但是当前还是存在一些组件的支持不够,某些在其他UI
中司空见惯的常用组件还需要自己来实现,或许以后SwiftUI
会陆续推出。
比如本节要描述的一个问题,图片的选择或者拍照。
二、在SwiftUI
中调用UIKit
组件
SwiftUI
原生组件支持的不够,势必会让开发者在使用过程中选择混用的方式来开发。
那么怎么才能使用SwiftUI
的声明式编程语法中调用呢?
使用UIViewControllerRepresentable
接口并实现它要求的两个接口方法makeUIViewController
和updateUIViewController
。
makeUIViewController
用来创建对应的组件对象并且做一些初始化的工作。updateUIViewController
往往用来创建好对象后,根据上下文情景做一些数据更新。
比如创建一个ImagePicker.swift
文件,在其中调用UIKit
组件之前,就需要做一些基础的转换工作,才能再SwiftUI
中进行调用。
import SwiftUI
struct ImagePicker: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIImagePickerController {
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
}
}
有了以上实现之后
import SwiftUI
struct BrochureView: View {
var body: some View {
ImagePicker()
}
}
ImagePicker
就可以跟其它SwiftUI
组件一样使用。
三、UIImagePickerController
组件
通过使用UIImagePickerController
组件,可以调用手机的相册或者拍照功能。
首先在makeUIViewController
创建它。
let imagePickerController = UIImagePickerController()
imagePickerController.sourceType = .photoLibrary
return imagePickerController
sourceType
用来指定图片选择的来源。photoLibrary
代表相册,camera
代表拍照获取。这里指定相册获取的方式。而且在info.plist
中要注明访问的权限和为什么访问的原因。1是后续上架APP的时候官方会审核;2是用户在首次打开的时候会弹出一个权限弹窗,会在征求用户同意后才能真正访问。
四、ImagePicker
的推荐调用方式
这里建议使用sheet
组件+Button
控制的方式弹出相册页的方式。
代码如下:
struct BoilingView: View {
@State private var selectPhotoPresneted: Bool = false
var body: some View {
Button(action: {
selectPhotoPresneted = true
}, label: {
Text("selected photo")
}).sheet(isPresented: $selectPhotoPresneted, content: {
ImagePicker()
})
}
}
五、怎么确定选择好的图片
以上都是铺垫,最终目的还是选择图片以作后用。
struct ImagePicker: UIViewControllerRepresentable {
@Binding var image: UIImage
func makeUIViewController(context: Context) -> UIImagePickerController {
let imagePickerController = UIImagePickerController()
imagePickerController.sourceType = .photoLibrary
imagePickerController.delegate = context.coordinator
return imagePickerController
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
}
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
var parent: ImagePicker
init(parent: ImagePicker) {
self.parent = parent
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
parent.image = image
}
}
}
}
这里通过@Binding var image: UIImage
传递的方式,可以在上层调用方在用户选择图片后感知到。
重写makeCoordinator
方法,并通过UIImagePickerControllerDelegate
协议在用户选择图片后,触发该方法imagePickerController
方法执行,然后将选择的图片赋值给image
变量。
最后通过imagePickerController.delegate = context.coordinator
注入到组件中。
六、关闭相册页,返回主页
struct ImagePicker: UIViewControllerRepresentable {
@Environment(\.presentationMode) var presentationMode
...
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
var parent: ImagePicker
init(parent: ImagePicker) {
self.parent = parent
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
parent.image = image
}
parent.presentationMode.wrappedValue.dismiss()
}
}
}