irpas技术客

SwiftUI 使用相册的实践_JavaAndJava_swift ui 打开相册

未知 545

目标与计划 目标

假设一个场景,我们的app需要图片,这个图片的来源是相册或者通过拍照获取的,并且要将图片保留在app中,并且希望app打开时能遍历到某个目录下的所有图片,并且加载,以供我们的app使用。

计划

构建一个View,在这个View中选择相册中的图片,或者通过相机拍照 选中的图片保存到app中,并且在app打开时加载指定目录的图片 构造一个外层容器用来装载这个View 计划完成,开始动手写代码

实现 构建从相册/相机选择图片的View

SwiftUI没有这个样的控件,需要使用UIKit中的UIImage这个控件,那么这里需要一个技术,SwifiUI桥接UIKit控件。那么现在不讲原理,只讲如何实现。 定义一个Struct继承UIViewControllerRepresentable,在继承时,需要实现2个func,makeUIViewController和updateUIViewController一个是在构造时调用一个是在刷新时被调用.在这个Struct中可以打开相册/相机 (使用摄像头需要在info.plist中添加一个Privacy - Camera Usage Description 的授权描述)

struct ImagePicker: UIViewControllerRepresentable { @Binding var sourceType:UIImagePickerController.SourceType func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController { let picker=UIImagePickerController() picker.allowsEditing=false picker.sourceType=sourceType print("picker source \(sourceType.rawValue)") return picker } func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) { } }

在这段代码中定义了一个ImagePicker的View,继承了UIViewControllerRepresentable,添加了一个sourceType的属性,用来控制图片来源(相册还是相机)。在makeUIViewController函数中,创建UIImagePickerController的实例,并且设置图片是不可编辑的,设置了来源。 我们完成了一个打开相册或者相机的View。但打开之后,选择图片是没有任何反应的,这个就需要我们做另一个事情,添加一个代理处理选照片的这个事件。(具体原理需可查阅UIKit相关文档。这里只实现功能),定义一个类继承NSObject,UINavigationControllerDelegate,UIImagePickerControllerDelegate。

struct ImagePicker: UIViewControllerRepresentable { @Binding var sourceType:UIImagePickerController.SourceType let handlerImage:(_ image:UIImage)->Void func makeCoordinator() -> Coordinator { return Coordinator(self) } func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController { let picker=UIImagePickerController() picker.allowsEditing=false picker.delegate=context.coordinator picker.sourceType=sourceType print("picker source \(sourceType.rawValue)") return picker } func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) { } 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.handlerImage(image) if let p=info[.imageURL] { print("image = \(p)") } } } } }

代码中定义了Coordinator这个类,继承NSObject,UINavigationControllerDelegate,UIImagePickerControllerDelegate,这个类是ImagePicker的内部类,ImagePicker选中图片时会回调imagePickerController这个函数,info这个参数代表选中的图片,可以将info[.originalImage]转换成UIImage。保存图片到app中是调用了ImagePicker中的一个函数处理的,代码体现在parent.handlerImage(image)这里。 将Coordinator和ImagePicker关联起来的方法,在Coordinator添加一个指向ImagePicker 属性,并且在初始化时赋值;在ImagePicker中增加makeCoordinator这个函数,这个函数中创建Coordinator对象,并且将自己赋值给Coordinator的ImagePicker属性 这样我们完成了一个从相册/相机选择图片的View。下一步我们将选择的图片保存到app中

将图片保存到App中 先了解ios的关于文件概念。在ios系统中有一个沙箱(sandbox)的概念,app自身使用的文件都是在这个沙箱中,我们只需要打开沙箱,找到目录保存文件或者找到文件加载进来。沙箱中有4个目录Documents,Library,SystemData.tmp。我们需要将图片保存在documents这个目录下。

先找到沙箱目录

let filePath=NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last!

文件管理工具

let filemanager=FileManager.default

图片写入

try! filemanager.createDirectory(atPath: dir, withIntermediateDirectories: true, attributes: nil) let data:Data=image.pngData()! try! data.write(to: URL(fileURLWithPath: filename))

第一步创建目录,第二步从UIImage对象获取NSData对象,第三步写入文件 完整的代码

static func saveImage(image:UIImage)->String{ let filePath=NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last! let dir=filePath+"/images/"+BuildingPropertyDir+"/" let filemanager=FileManager.default try! filemanager.createDirectory(atPath: dir, withIntermediateDirectories: true, attributes: nil) let now=Date() let formatter=DateFormatter() formatter.dateFormat="YYYYMMddHHmmss" let time=formatter.string(from: now) let fn=time+".png" let filename=dir+fn let data:Data=image.pngData()! try! data.write(to: URL(fileURLWithPath: filename)) // print("filename \(filename)") return fn }

参数是UIImage,在Documents 目录下创建一个images+BuildingPropertyDir 的目录,按照时间生成文件名,将图片转成png文件,写入沙盒中,返回文件名。

从沙盒中加载图片 static func loadImages()->[(String,UIImage)]{ var images:[(String,UIImage)]=[] let filePath=NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last! let dir=filePath+"/images/"+BuildingPropertyDir let manager=FileManager.default let files=try! manager.contentsOfDirectory(atPath: dir) for f in files{ let filename=dir+"/"+f if let image=UIImage(contentsOfFile: filename){ let i=(f,image) images.append(i) } } return images }

第一步先找到沙盒目录。拼接需要加在的目录,第二步获取文件管理器FileManager.default,获取所有的文件名称,第三步,通过文件名称加载图片image=UIImage(contentsOfFile: filename),返回 (文件名称,UIImage) 的元数据数组。

构造外层容器 @State private var showPic=false @State private var sourceType:UIImagePickerController.SourceType=UIImagePickerController.SourceType.camera var body: some View { VStack(){ Button("添加图片"){ sourceType=UIImagePickerController.SourceType.photoLibrary self.showPic.toggle() } Button("拍照"){ sourceType=UIImagePickerController.SourceType.camera self.showPic.toggle() } }.frame(width: 300, height: 400, alignment: .center).sheet(isPresented: $showPic,onDismiss:{ print("dimiss") }){ HStack(){ ImagePicker(sourceType:$sourceType){ image in let id=saveImage(image: image) showPic=false } } } }

首先定义一个View,中间有2个button,一个是添加图片,一个是拍照。当我们点击其中一个按钮时,弹出一个sheet,相册或者是相机(ImagePicker)被放置在这个sheet中,sheet的出现和消失是通过showPic这个变量控制**(isPresented: $showPic),ImagePicker是相册还是相机是通过@State private var sourceType** 控制,这2个变量在button点击事件中被设置成对应的数值。在ImagePicker构造是传入了一个闭函数赋值给ImagePicker的handlerImage变量,用来处理选中的图片既保存图片到沙盒中,这里调用了saveImage这个函数。并且设置showPic为false,sheet就dismiss。

总结 在这个实践中有以下几个知识点需要了解SwiftUI如何与UIKit桥接,这个可以apple developer的官方文档和实例。UIImagePickerController 的使用ios沙箱文件的概念,以及如何操作。UIImage获取png,可以很简单的通过image.pngData()!获取sheet的使用


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #Swift #UI #打开相册 #iOS