How can I use GLKView in SwiftUI? I'm using CIFilter but would like to apply filters through GLKit / OpenGL. Any ideas?
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)
}
}
}
Here's a working GLKView in SwiftUI using
UIViewControllerRepresentable.A few things to keep in mind.
GLKitwas deprecated with the release of iOS 12, nearly 2 years ago. While I hope Apple won't kill it anytime soon (way too many apps still use it), they recommend using Metal or anMTKViewinstead. Most of the technique here is still the way to go for SwiftUI.I worked with SwiftUI in hopes of making my next CoreImage app be a "pure" SwiftUI app until I had too many UIKit needs to bring in. I stopped working on this around Beta 6. The code works but is clearly not production ready. The repo for this is here.
I'm more comfortable working with models instead of putting code for things like using a
CIFilterdirectly in my views. I'll assume you know how to create a view model and have it be anEnvironmentObject. If not look at my code in the repo.Your code references a SwiftUI
Imageview - I never found any documentation that suggests it uses the GPU (as aGLKViewdoes) so you won't find anything like that in my code. If you are looking for real-time performance when changing attributes, I found this to work very well.Starting with a GLKView, here's my code:
This is very old production code, taken from objc.io issue 21 dated February 2015! Of note is that it encapsulates a
CIContext, needs it's own clear color defined before using it'sdrawmethod, and renders an image asscaleAspectFit. If you should try using this in UIKit, it'll like work perfectly.Next, a "wrapper" UIViewController:
I did this for a few reasons - pretty much adding up to the fact that i'm not a
Combineexpert.First, note that the view model (
model) cannot access theEnvironmentObjectdirectly. That's a SwiftUI object and UIKit doesn't know about it. I think anObservableObject*may work, but never found the right way to do it.Second, note the use of
NotificationCenter. I spent a week last year trying to get Combine to "just work" - particularly in the opposite direction of having aUIButtontap notify my model of a change - and found that this is really the easiest way. It's even easier than using delegate methods.Next, exposing the VC as a representable:
The only thing of note is that here's where I set the
modelvariable in the VC. I'm sure it's possible to get rid of the VC entirely and have aUIViewRepresentable, but I'm more comfortable with this set up.Next, my model:
Nothing to see here at all, but understand that in
SceneDelegate, where you instantiate this, it will trigger theinitand set up the filtered image.Finally,
ContentView:SceneDelegate instantiates the view model which now has the altered
CIImage, and the button beneath the GLKView (an instance ofGLKViewVC, which is just a SwiftUI View) will send a notification to update the image.