SwiftUI中的GLKView?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SwiftUI中的GLKView?相关的知识,希望对你有一定的参考价值。
如何在SwiftUI中使用GLKView
?我正在使用CIFilter
,但想通过GLKit / OpenGL应用过滤器。有什么想法吗?
struct ContentView: View {
@State private var image: Image?
var body: some View {
VStack {
image?
.resizable()
.scaledToFit()
}
.onAppear(perform: loadImage)
}
func loadImage() {
guard let inputImage = UIImage(named: "squirrel") else {
return
}
let ciImage = CIImage(image: inputImage)
let context = CIContext()
let blur = CIFilter.gaussianBlur()
blur.inputImage = ciImage
blur.radius = 20
guard let outputImage = blur.outputImage else {
return
}
if let cgImg = context.createCGImage(outputImage, from: ciImage!.extent) {
let uiImg = UIImage(cgImage: cgImg)
image = Image(uiImage: uiImg)
}
}
}
这是在SwiftUI中使用UIViewControllerRepresentable
的GLKView。
一些注意事项。
GLKit
在将近12年前的ios 12版本中已弃用。虽然我希望Apple不会很快终止它(太多的应用程序仍在使用它),但他们还是建议使用Metal或MTKView
。这里的大多数技术仍然是SwiftUI可以采用的方法。我与SwiftUI一起工作,希望将我的下一个CoreImage应用程序变成“纯” SwiftUI应用程序,直到需要引入太多UIKit为止。我在Beta 6左右停止了此工作。代码可以工作,但显然不能准备生产。这个仓库是here。
我更喜欢使用模型,而不是将代码直接用于诸如在视图中使用
CIFilter
的代码。我假设您知道如何创建视图模型并将其设置为EnvironmentObject
。如果没有看回购中的代码。您的代码引用了SwiftUI
Image
视图-我从未找到任何文档暗示它使用GPU(就像GLKView
一样),因此您不会在我的代码中找到类似的东西。如果您在更改属性时正在寻找实时性能,我发现它可以很好地工作。
从GLKView开始,这是我的代码:
class ImageView: GLKView {
var renderContext: CIContext
var myClearColor:UIColor!
var rgb:(Int?,Int?,Int?)!
public var image: CIImage! {
didSet {
setNeedsDisplay()
}
}
public var clearColor: UIColor! {
didSet {
myClearColor = clearColor
}
}
public init() {
let eaglContext = EAGLContext(api: .openGLES2)
renderContext = CIContext(eaglContext: eaglContext!)
super.init(frame: CGRect.zero)
context = eaglContext!
}
override public init(frame: CGRect, context: EAGLContext) {
renderContext = CIContext(eaglContext: context)
super.init(frame: frame, context: context)
enableSetNeedsDisplay = true
}
public required init?(coder aDecoder: NSCoder) {
let eaglContext = EAGLContext(api: .openGLES2)
renderContext = CIContext(eaglContext: eaglContext!)
super.init(coder: aDecoder)
context = eaglContext!
}
override public func draw(_ rect: CGRect) {
if let image = image {
let imageSize = image.extent.size
var drawFrame = CGRect(x: 0, y: 0, width: CGFloat(drawableWidth), height: CGFloat(drawableHeight))
let imageAR = imageSize.width / imageSize.height
let viewAR = drawFrame.width / drawFrame.height
if imageAR > viewAR {
drawFrame.origin.y += (drawFrame.height - drawFrame.width / imageAR) / 2.0
drawFrame.size.height = drawFrame.width / imageAR
} else {
drawFrame.origin.x += (drawFrame.width - drawFrame.height * imageAR) / 2.0
drawFrame.size.width = drawFrame.height * imageAR
}
rgb = (0,0,0)
rgb = myClearColor.rgb()
glClearColor(Float(rgb.0!)/256.0, Float(rgb.1!)/256.0, Float(rgb.2!)/256.0, 0.0);
glClear(0x00004000)
// set the blend mode to "source over" so that CI will use that
glEnable(0x0BE2);
glBlendFunc(1, 0x0303);
renderContext.draw(image, in: drawFrame, from: image.extent)
}
}
}
这是非常旧的生产代码,取自objc.io issue 21 dated February 2015!值得注意的是,它封装了CIContext
,需要使用其draw
方法定义为[[before的自己的清晰颜色,并将图像呈现为scaleAspectFit
。如果您应该尝试在UIKit中使用它,它将非常完美。
class ImageViewVC: UIViewController {
var model: Model!
var imageView = ImageView()
override func viewDidLoad() {
super.viewDidLoad()
view = imageView
NotificationCenter.default.addObserver(self, selector: #selector(updateImage), name: .updateImage, object: nil)
}
override func viewDidLayoutSubviews() {
imageView.setNeedsDisplay()
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
if traitCollection.userInterfaceStyle == .light {
imageView.clearColor = UIColor.white
} else {
imageView.clearColor = UIColor.black
}
}
@objc func updateImage() {
imageView.image = model.ciFinal
imageView.setNeedsDisplay()
}
}
我这样做是出于几个原因-几乎加起来我不是Combine
专家。首先,请注意,视图模型(
model
)无法直接访问EnvironmentObject
。那是一个SwiftUI对象,UIKit对此一无所知。我认为ObservableObject
*可能有效,但从未找到正确的方法。第二,请注意
NotificationCenter
的使用。去年,我花了一个星期的时间尝试让Combine正常工作-尤其是在按下UIButton
的相反方向上通知我的更改模型-并且发现这确实是最简单的方法。比使用委托方法甚至更容易。接下来,将VC公开为可代表的:
struct GLKViewerVC: UIViewControllerRepresentable { @EnvironmentObject var model: Model let glkViewVC = ImageViewVC() func makeUIViewController(context: Context) -> ImageViewVC { return glkViewVC } func updateUIViewController(_ uiViewController: ImageViewVC, context: Context) { glkViewVC.model = model } }
唯一需要注意的是,这里是我在VC中设置model
变量的位置。我敢肯定有可能完全摆脱VC并使用UIViewRepresentable
,但是我对此设置比较满意。接下来,我的模特:
class Model : ObservableObject { var objectWillChange = PassthroughSubject<Void, Never>() var uiOriginal:UIImage? var ciInput:CIImage? var ciFinal:CIImage? init() { uiOriginal = UIImage(named: "vermont.jpg") uiOriginal = uiOriginal!.resizeToBoundingSquare(640) ciInput = CIImage(image: uiOriginal!)?.rotateImage() let filter = CIFilter(name: "CIPhotoEffectNoir") filter?.setValue(ciInput, forKey: "inputImage") ciFinal = filter?.outputImage } }
什么也没看到,但要理解,在实例化此的SceneDelegate
中,它将触发init
并设置过滤后的图像。最后,
ContentView
:
struct ContentView: View { @EnvironmentObject var model: Model var body: some View { VStack { GLKViewerVC() Button(action: { self.showImage() }) { VStack { Image(systemName:"tv").font(Font.body.weight(.bold)) Text("Show image").font(Font.body.weight(.bold)) } .frame(width: 80, height: 80) } } } func showImage() { NotificationCenter.default.post(name: .updateImage, object: nil, userInfo: nil) } }
[SceneDelegate实例化现在具有更改的CIImage
的视图模型,并且GLKView(GLKViewVC
的实例,它只是一个SwiftUI视图)下面的按钮将发送通知以更新图像。
以上是关于SwiftUI中的GLKView?的主要内容,如果未能解决你的问题,请参考以下文章
在UI视图顶部的OpenGL GLKView覆盖 - iOS / Xcode
为啥在 ViewDidLoad 中没有设置 GLKView 的 DrawableWidth?