【译】[SwiftUI 100 天]集成 Core Image 到 SwiftUI


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

更多内容,欢迎重视大众号 「Swift花园」
喜爱文章?不如来个 ➕三连?重视专栏,重视我

正如 Core Data 是 Apple 内建的操作数据的结构,h f 1 F . j & W [Core Image 是操作图片的结构。它不是关于制作,至少其间的大部分不是关于制作,而是关于修正现有的图片:运用锐化,模糊,暗角,像素化,等等。假设你从前运用过 Apple 的 Photo Bs i sooth 运用里的各种作用,那你关于 Core Image 擅长的东西应该就比较有概念了。

不过,Core Image 并没有很6 r [ W t好地集成到 SwiftUI,实践上,即便是 UIKit,也不算集成地很完善 —— Apple 的确提供了一些辅助,但仍是挺费心思的。但请你跟随我的脚步:一旦你了解它的作业机制,你会发现从此就打开了一扇新世界的大门。

首要,咱们要放一些基础的代码,显现E y B V t一张图片。我会以一种略微有点奇怪的方法构筑它,但关于 C] Z B 9 nore Image 将是合理的:咱们要以可选的 @State 特点来创立图画,强制它铺X M Y @ q q 1满屏幕宽度,C / A F而且在 onAppear(): h i b f mo} ( E i / [difier 里实践加载B S 3 g G 0 | { V图画。

增加一个示例图片到你的 asset catalog,然后把 ContentView 修正成这样:

struct Content] M 9 e BView: Vies e ? [w {
@State private var image: Image?
var body: some View {
VStack {
image?
.resizable()
.scaledTo8 Y 9 D L * 4Fit()
}
.onAppear(w ) i 4 ) !perform: loads Z # I , B G rImage)
}
fun? # Ac loadImage() {
image = Image("Example")
}
}

首要$ ` C ` $ w { !,留意一下 SwiftUI 是如何处理可选视图的_ x 5 @ ~ K 2 y —— 它竟然能作业!可是你注意到没,我是在 VStack~ n – / c * o U onAppear() modifik ) R 2 P V c ~ Fer 里加载图片,而不是 image,这是由# v ` Y v于假设可选图画自身是 nil,就无法触发 onAppear() 函数了。

不管怎样说,当代码运转起来后,应该要显现你放的示例图片,而且整齐地缩放成铺满屏幕宽度的状态。

然后是杂乱的部分:一个 Image? 本质上是什么。正如你知道的,它是一个 视图,意味着咱们能够在 SwiftUI 视图层级里放置它。它也处理从咱们的 asset catalog 和 SF Symbols 加载图片的M I S ;事情,同时也能够从许多其他资源里加载。可是,终究它是一种用于显现的东西 —— 咱们不能把它的内} a x + $ | R容写进磁盘,或者给它运用滤镜。

假设要运用 Core Image,SwiftUI 的 Image 视图是一个很好的结尾,但并不能在各个环节都发挥作用,包括动态地创立图画,运用 Core Image 滤镜,保存到用户相册,等等。SwiftUI 的图画对这些是力不从心的。

Apple 给了n k S咱们三种图画类型,在咱们运t = + 6 9 r用 Core Image 时,需求巧妙地选择。它们听w T ] u 6 P 6 t 9起来很相似,但有微妙的差异,正确运用它们很重要。

除了 SwiftUI 的 Image 视图,还有三种其他类型的图画:

  • UIImage,来自 UIKit。这是能够处理各种图片的强大的图画类型,包括像位图(比方 PNG),向量f : t 5 5 Z 5 7(比方 SVG),甚至形成动画的序列帧。UIImage 是 UIKit 的规范图画类型,也是三种之中最接近 Sc G A 0 f F L } 0wiftUI 的U F i Image 类型的图画。
  • CGImage,来自 Core Graphics。这是一种更简略的图画类型,只需二维的像素数组。
  • CIImage,来自 Core Image。它存储了= T & d [ p t s R用于发生图画的一切信息,但只需在被请求时才实践转换成像素。Apple 称 CIImage 为“图画菜谱”,而非图画自身w ) h %

三种图画之间的互操作性:

  • 咱们能够从 CGImage 中创立 UIImage,也A w % %能够从 UIImage 中创立 CGImage
  • 咱们能够从 UIImage 或者 CGImage 中创立 CIImage,也能够从 CIImage 中创立 CGImage
  • SwiftUI 的 Image 既能够经过 UIImage 创立,也能够经过 CGImage 创立。

是不是有点绕晕了?期望你看到代码的时候感d b T k )觉好一点。其间的关键在于,这些图画类型都是纯数据 —— 咱们无法将它们直接放置到 SwiftUI 的视图层级中,但咱们能够自由地维护这些图画,然后以 SwiftUI 的 Image 来出现。

咱们即将修正 loadImage() 以便从示例5 Y B 1 0图片从创立出 UIImage,然后用 Core Image 维护它。具体地,包含两个任务:

  1. 咱们需求将示例图片载入 UIImage6 1 x,它有一个叫 UIImage(named:) 的结构器,能够从咱们的 asset catalog 中加载u X x 5 , K [图画。由于图画或许不存在,所以它回来的是 UIImage 可选型。
  2. 咱们将转换成 CIImage,以便运用 Core Image 处理。

现在,把 loadIma: ; U - 1ge? i D G() 完成替换成下面这样:

func loadIma* 5 ! D Q + r Mge() {
guard let inputImage = UIImage(named: "Example") else { return }
let: h  N F l x beginImage = CIImage(image: inputImage)
// more codq l =e to come
}

下一步是创立 Core Image 上下文和 Core Image 滤镜。滤镜担任实践改换图画数据作业的东西v P % } B m,比方模糊图画,锐化图画,调整色彩,等等,而上下文则担任将处理好} E i的数据转成 CGImage,以便咱们能运用。

: – r T ) b D D 面两个数据类型都来自 C= x Xore Image,因此9 w m你需求增加两条 import:

import CoreImage
import CoreImage.CIFilterBuiltins

关于这个比如,咱们要运用墨色彩滤镜,它会运用一个墨色彩到B 6 }照片上,让照片看起来就像老照片。

// more code to come 注释替换成:

let context = C% y c d z ( l mIContext()
let currentFilter = CIFilter.sepiaTone()

咱们能够+ G h v自定义滤镜的参数,墨色彩滤镜相对简略,只需两个特点:inputImage 是咱们要改变的A ] } L [图画, 而 intensity 是墨色作用要运用的强度,规模从 0 (原始图画)到 1(彻底的墨色彩)。

把下面两行代码增加到之前的两行代码之后:

currentF) 9 ` 5 2 O A ^ilter.inputImage = beginImage
currentFilM ] r B N = xter.intensity = 1

一切这些都不难, $ L L,不过这儿就要转折了:咱们需求把滤镜的输出转成一个 SwiftUI Image,以用于视图的显现。

  • 从滤镜中读取输出文件,它是一个 CIImage,由于或许失利,所以回来可选型。
  • 让上下文基于输出图画创 + # 2立一个 CGImage,这相同或许失利,所以也是回来可选型。
  • CGImage 生成 UIImage
  • 再把 UIImage 变成 SwiftUI Image

你能够直接从 CGI! c |mage 得到 SwiftUI Image,但需求传入额定的参数,这样只会更杂乱。

loadImage() 的终究代码N s j : J 2如下:

// get a CIImage from our filter or exit if that fails
guard let outputImage = currentFilter.outputImage^  Q * else { return }
/|  H z { r ^ H/ attempt to get a CGImage from our CIIma6 L r | v = F mge
if let cgimg = context.createCGImage(outputImage, fro 2 h @ j _om: o^ m +utputImage2 v C ~ : }.extent) {
// convert that to a UIImage
let uiImage = UIImage(cgImage: cgimg)
// and convem D N i y frt that to a SwiftUI image
image = Image(uiImage: uiImage)
}

再次运转运用,你会看到一张看起来像老照片的示例图片,这都要归功于 Core Image。

现在,你不免会开始想,我只需这么简略的一个结果,怎样需求这么f C D T ( 2 r N多的作业?但想想看,上面的代码根本上了覆盖了 Core Image 的惯例操作% c ~ [ L k,相对来说,你要切换不同的滤镜作用是很简略的。

前面提到过,Core I1 Y 6mage 有一点… 怎样说呢?… 不如说是 “创造性” 吧。它在 iOS 5.0 就引入了,彼时 Swift 已经在苹果内部开发了,但你不会想知道 —— 长期以来, Core Image 的 API 对 Swift 不可思议地不友好。虽然苹果现在已经逐步擦除其间的遗留 API,但 Core Image 仍有一些地方的行为比较奇怪。

让咱们举例说明吧,把墨色彩滤镜替换成像素风滤镜:

let currentFilter = CIFilter.pixellate()
currentFilter.inputImage = beginImage
currentFilter.scale = 100

运转代码,你会发现图画看起来就像棋盘格相同。100 的 scale 表明跨度是 100 个点,但由于我的图画很大,像素相对来很小。

Y % g Q % n a在,让咱# u 2 : 1 U *们测验一下水晶作用:

let currentFilter = CIFilter.crystallize()
currentFilter.inputImage = beginA ` m H P wImage
currentFilter.radius = 200

代码运转起来咱们本应该看到一个Y K 9水晶作用,但实践上代码会崩溃。咱们的代码是合法的 Swif7 r # r F 7 Ht 和 Core Image 代码,但便是无法作业。

这其实是一个 bug,在你阅览本文F A K时或许已经修复了,B * I + ` _ *假设咱们切换到一个更老的 APf K 8 R @ e }I,像下面这样:

let curren_  v _ -tFilter = CIFilter.crystallize()
currentFilter.setV7 ? g R $alue(beginImage, forKey: kCIInputImageKey)
currentFilter.radius = 200

kCIInputImageKey 是一个指定滤镜要处理的图片的常量,深挖进去你会发现它实践上是一个字符串 —— Core Image 在幕& T Oi V A ?其实一套基于字符串的 API。

当你发现只需部分 Apple 的 Core Image 滤镜是很好地适配了 Swift API 这个事实时,就会发现我提出的问题益发显着了。举个比如,假设你想要一个旋转扭曲的滤镜作用,你只能运用老版的 API,这就有点? j k U C } 7痛苦了:

  1. 用滤镜名创立 CIFilter 实例
  2. 重复屡次调用 setValue(),每次用不同的 key。
  3. 由于 CIFilter 不是一个特定的滤镜,Swift 答应咱们传入这个滤镜或许并不支持的参数。

举个比如,运用旋转扭曲滤镜的代码如下:

guard let currentFilter = CIFilter(name: "CITwirlDi, B l X [ Y Tstortion") else { re= c xturn }
currentFilter.setValue(beginImage, forKey: kCIInputImageKey)
currentFilter.setValue(2000, forKey: kCIInputRadJ J . K | J iusKey)
currentFilter.setValue(CIVector(x: inputImage.size.width / 2, y: inputImage.size.height / 2),1 q % W forKey: kCIInputCenterKey)

V b (示: CIVectora C 3 P – O i 9 3 Core Image 中存储点和方向的类型。

运转代D 3 { T , 0码,你会看到结果T ~ L u p N v 2仍是不赖的,期望 Apple 在未来的时间里继续u 2 & 8 1 (整理 API。

虽然新的 API 更易用,为了能够运用恣意种类的滤镜,咱们在这个项目中主要会运用老 API。


我的大众号 这儿有Sw* _ # m ! U . L +ift及计算机编程的相关文章,以及v % G m l {优秀国外文章翻译4 F p _ K n,欢迎重视~

【译】[SwiftUI 100 天]集成 Core Image 到 SwiftUI

发表评论

提供最优质的资源集合

立即查看 了解详情