首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >GLKView in SwiftUI?

GLKView in SwiftUI?
EN

Stack Overflow用户
提问于 2020-01-14 06:54:57
回答 2查看 1.1K关注 0票数 2

如何在GLKView中使用SwiftUI?我正在使用CIFilter,但希望通过GLKit / OpenGL应用过滤器。有什么想法吗?

代码语言:javascript
复制
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)
        }
    }
}
EN

回答 2

Stack Overflow用户

发布于 2020-01-19 16:36:46

下面是使用GLKView在SwiftUI中工作的UIViewControllerRepresentable

有几件事要记住。

  • 将近2年前,GLKit被反对发布iOS 12。虽然我希望苹果不会在短期内杀死它(太多的应用程序仍在使用它),但他们建议使用金属或MTKView。这里的大多数技术仍然是SwiftUI的发展方向。
  • 我与SwiftUI合作,希望我的下一个CoreImage应用程序成为一个“纯”的SwiftUI应用程序,直到我有太多的UIKit需要引入。关于Beta 6,我停止了这方面的工作。代码可以工作,但显然还没有准备好生产。这里的回购是这里
  • 我更愿意使用模型,而不是在视图中直接使用CIFilter之类的东西编写代码。我假设您知道如何创建视图模型并使其成为EnvironmentObject。如果不看我在回购程序中的代码。
  • 您的代码引用了一个SwiftUI Image视图--我从未发现任何文档表明它使用GPU (就像GLKView那样),所以您不会在我的代码中找到类似的东西。如果您想在更改属性时寻找实时性能,我发现它非常好用。

从GLKView开始,下面是我的代码:

代码语言:javascript
复制
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:

代码语言:javascript
复制
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公开为可代表的:

代码语言:javascript
复制
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是可能的,但是我对这个设置比较满意。

接下来,我的模型:

代码语言:javascript
复制
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

代码语言:javascript
复制
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视图)将发送一个通知来更新图像。

票数 2
EN

Stack Overflow用户

发布于 2022-06-11 17:28:23

苹果公司的WWDC 2022包含一个题为“用核心图像、金属和SwiftUI显示EDR内容”的教程/视频,其中描述了如何将核心图像与金属和SwiftUI混合。它指向一些名为“用核心图像呈现目标生成动画”(这里)的新示例代码。

虽然它没有解决您关于使用GLKView的问题,但它确实为在SwiftUI中使用金属提供了一些优雅的、干净的、经苹果认可的代码。

这个示例项目非常以核心图像为中心(它与您的背景与CIFilter相匹配),但我希望苹果会发布更多示例代码示例,显示金属与SwiftUI集成。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59728818

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档