SwiftUI实战-仿写掘金APP(三)

1,069 阅读3分钟

这是我参与8月更文挑战的第15天,活动详情查看:8月更文挑战

一、目标

image.png

常规的布局,比如列表展示,比如菜单栏,在之前的章节都有多次演示,这里不再啰嗦,本节说一些新鲜的,可能在实现的过程中会遇到的问题。

SwiftUI从发布到现在,经历了2个大的版本更迭(9月份会推出3.0,扩展大量新功能,此刻暂且不提),但是当前还是存在一些组件的支持不够,某些在其他UI中司空见惯的常用组件还需要自己来实现,或许以后SwiftUI会陆续推出。

比如本节要描述的一个问题,图片的选择或者拍照。

二、在SwiftUI中调用UIKit组件

SwiftUI原生组件支持的不够,势必会让开发者在使用过程中选择混用的方式来开发。

那么怎么才能使用SwiftUI的声明式编程语法中调用呢?

使用UIViewControllerRepresentable接口并实现它要求的两个接口方法makeUIViewControllerupdateUIViewController

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是用户在首次打开的时候会弹出一个权限弹窗,会在征求用户同意后才能真正访问。

image.png

四、ImagePicker的推荐调用方式

这里建议使用sheet组件+Button控制的方式弹出相册页的方式。

image.png

代码如下:


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()
        }
        
    }
    
}

屏幕录制2021-08-29 23.09.39.gif