[SwiftUI 100 天] Instafilter 保存滤镜后的图片

2020/6/1 23:27:18

本文主要是介绍[SwiftUI 100 天] Instafilter 保存滤镜后的图片,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

译自 www.hackingwithswift.com/books/ios-s…

更多内容,欢迎关注公众号 「Swift花园」

喜欢文章?不如来个 🔺💛➕三连?关注专栏,关注我 🚀🚀🚀

用 UIImageWriteToSavedPhotosAlbum() 来保存滤镜后的图片

为了完成这个项目,我们最后需要实现保存按钮:将滤镜处理后的照片保存到用户的相册,以便用户可以进一步编辑照片,或者分享照片。

我在前面解释过,UIImageWriteToSavedPhotosAlbum() 函数可以实现我们要的功能,但需要一些跟 SwiftUI 不太兼容的代码:需要一个继承自 NSObject 的对象,提供以 @objc 标记的回调方法,以及用 #selector 编译器指令指向的方法。

我们要在一个单独的,可重用的类中实现这个目标。请创建一个名叫 ImageSaver.swift 的新 Swift 文件,把导入 Foundation 修改为导入 UIKit,然后提供以下代码:

class ImageSaver: NSObject {
    func writeToPhotoAlbum(image: UIImage) {
        UIImageWriteToSavedPhotosAlbum(image, self, #selector(saveError), nil)
    }

    @objc func saveError(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
        // save complete
    }
}
复制代码

我们稍后会回到上面的代码做修改。现在需要先确保从用户那里请求到保存照片的权限:我们需要添加一个键到 Info.plist。

  • 打开 Info.plist
  • 在空白区域右键
  • 选择 Add Row
  • 选择 “Privacy - Photo Library Additions Usage Description” 作为键名
  • 输入 “我们希望保存滤镜照片。” 作为值

接下来是考虑如何利用 ImageSaver 类来保存照片。眼下我们设置 image 属性的方式如下:

if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) {
    let uiImage = UIImage(cgImage: cgimg)
    image = Image(uiImage: uiImage)
}
复制代码

实际上我们可以直接由 CGImage 生成 SwiftUI Image 视图,之前我也说过之所以借助 UIImage 中转是因为直接从 CGImage 生成需要更多的参数。这个理由当然是真实的,但更重要的原因是:我们需要用到一个 UIImage,以便传给 ImageSaver 类,而这里正是创建这个对象最好的地方。

把下面这个新属性添加到 ContentView,以存储中转的 UIImage

@State private var processedImage: UIImage?
复制代码

然后,我们要修改 applyProcessing() 方法,暂存 UIImage 以便后续使用:

if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) {
    let uiImage = UIImage(cgImage: cgimg)
    image = Image(uiImage: uiImage)
    processedImage = uiImage
}
复制代码

然后是保存按钮中的逻辑:

Button("Save") {
    guard let processedImage = self.processedImage else { return }

    let imageSaver = ImageSaver()
    imageSaver.writeToPhotoAlbum(image: processedImage)
}
复制代码

到这里其实我们已经可以收工了,但我们单独做了 ImageSaver 类的目的正是我们可以读取保存是否成功的结果。而这个方法是通过 ImageSaver 里下面这个方法来报告的:

@objc func saveError(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
    // save complete
}
复制代码

为了利用这个结果,我们需要将结果传递给 ContentView。但是,我不希望这个讨厌的 @objc 逃出这个类,所以我们要用闭包来报告成功或者失败 —— 对于 Swift 开发者来说这个解决方案更友好。

首先添加下面这两个属性到 ImageSaver 类,用以表示处理成功和失败的闭包:

var successHandler: (() -> Void)?
var errorHandler: ((Error) -> Void)?
复制代码

然后,完善 didFinishSavingWithError 方法,根据成功和失败两种情况分别调用对应的闭包:

if let error = error {
    errorHandler?(error)
} else {
    successHandler?()
}
复制代码

现在,我们就可以在使用 ImageSaver 时这样处理:

let imageSaver = ImageSaver()

imageSaver.successHandler = {
    print("Success!")
}

imageSaver.errorHandler = {
    print("Oops: \($0.localizedDescription)")
}

imageSaver.writeToPhotoAlbum(image: processedImage)
复制代码

尽管代码不一样,但概念和原来在 ImagePicker 里的做法是一样的:我们以对 SwiftUI 更友好的方式封装 UIKit 功能以获取我们想要的行为。更棒的是,这种方式让我们可以获得能够在其他项目中复用的代码 —— 我们实际上构建了一个库!

到此我们已经完成了项目,可以从头尝试应用,导入照片,应用滤镜,保存到相册。干得漂亮!


我的公众号 这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~



这篇关于[SwiftUI 100 天] Instafilter 保存滤镜后的图片的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程