SwiftUI中的GLKView?

问题描述 投票:0回答:1

如何在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 core-image cifilter glkview
1个回答
0
投票

这是在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中使用它,它将非常完美。

接下来,是一个“包装” 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的使用。去年,我花了一个星期的时间尝试让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视图)下面的按钮将发送通知以更新图像。
© www.soinside.com 2019 - 2024. All rights reserved.