如何在GLKView中使用SwiftUI?我正在使用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)
}
}
}发布于 2020-01-19 16:36:46
下面是使用GLKView在SwiftUI中工作的UIViewControllerRepresentable。
有几件事要记住。
GLKit被反对发布iOS 12。虽然我希望苹果不会在短期内杀死它(太多的应用程序仍在使用它),但他们建议使用金属或MTKView。这里的大多数技术仍然是SwiftUI的发展方向。CIFilter之类的东西编写代码。我假设您知道如何创建视图模型并使其成为EnvironmentObject。如果不看我在回购程序中的代码。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)
}
}
}这是非常古老的生产代码,取自2015年2月objc.io第21期!值得注意的是,它封装了一个CIContext,在使用它的draw方法之前需要自己定义的清晰颜色,并将图像呈现为scaleAspectFit。如果您想在UIKit中使用它,它会非常喜欢工作。
接下来是一个“包装器”UIViewController:
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的使用。去年,我花了一周的时间尝试着结合起来“只工作”--尤其是在让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视图)将发送一个通知来更新图像。
发布于 2022-06-11 17:28:23
苹果公司的WWDC 2022包含一个题为“用核心图像、金属和SwiftUI显示EDR内容”的教程/视频,其中描述了如何将核心图像与金属和SwiftUI混合。它指向一些名为“用核心图像呈现目标生成动画”(这里)的新示例代码。
虽然它没有解决您关于使用GLKView的问题,但它确实为在SwiftUI中使用金属提供了一些优雅的、干净的、经苹果认可的代码。
这个示例项目非常以核心图像为中心(它与您的背景与CIFilter相匹配),但我希望苹果会发布更多示例代码示例,显示金属与SwiftUI集成。
https://stackoverflow.com/questions/59728818
复制相似问题