Skip to content

Instantly share code, notes, and snippets.

@swiftui-lab
Created July 5, 2020 19:43
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save swiftui-lab/9078465e302ce467f4bfe850db45e622 to your computer and use it in GitHub Desktop.
Save swiftui-lab/9078465e302ce467f4bfe850db45e622 to your computer and use it in GitHub Desktop.
A method for converting a SwiftUI into a UIImage
//
// ContentView.swift
// Grabber
//
// SwiftUI-Lab
//
// For more information on the techniques used in this example, check
// The Power of the Hosting+Representable Combo (https://swiftui-lab.com/a-powerful-combo/)
import SwiftUI
struct ContentView: View {
@State private var beginCapture = false
@State private var image: UIImage? = nil
var body: some View {
VStack(spacing: 40) {
HStack(spacing: 5) {
ZStack {
RotatingSpheres(clockwise: false)
.frame(width: 300, height: 300)
RotatingSpheres(clockwise: true)
.onGrab(trigger: self.$beginCapture) { img in
DispatchQueue.main.async {
self.image = img
}
}
.frame(width: 300, height: 300)
.border(Color.black, width: 3)
RotatingSpheres(clockwise: false)
.frame(width: 300, height: 300)
}
// Captured Image
Image(uiImage: self.image ?? UIImage())
.resizable()
.frame(width: 300, height: 300)
.border(Color.black, width: 3)
}
Button("Capture") { self.beginCapture = true }
}
}
}
struct RotatingSpheres: View {
@State private var flag = false
let clockwise: Bool
var body: some View {
VStack {
HStack(spacing: 20) {
Circle()
.fill(self.clockwise ? Color.orange : Color.red)
.frame(width: 80, height: 80)
Circle()
.fill(self.clockwise ? Color.green : Color.blue)
.frame(width: 80, height: 80)
}
Circle()
.fill(self.clockwise ? Color.yellow : Color.purple)
.frame(width: 80, height: 80)
}
.onAppear {
withAnimation(Animation.linear(duration: 5.0).repeatForever(autoreverses: false)) {
self.flag = true
}
}
.rotationEffect(self.flag ? (self.clockwise ? Angle.degrees(360) : Angle.degrees(-360)) : Angle.degrees(0))
}
}
extension View {
func onGrab(rect: CGRect? = nil, trigger: Binding<Bool>, onCapture: @escaping (UIImage) -> Void) -> some View {
return CaptureWrapper(rect: rect, trigger: trigger, onCapture: onCapture) { self }
}
}
struct CaptureWrapper<Content>: View where Content : View {
let rect: CGRect?
let trigger: Binding<Bool>
let onCapture: (UIImage) -> Void
let content: () -> Content
init(rect: CGRect? = nil, trigger: Binding<Bool>, onCapture: @escaping (UIImage) -> Void, @ViewBuilder content: @escaping () -> Content) {
self.rect = rect
self.trigger = trigger
self.onCapture = onCapture
self.content = content
}
var body: some View {
CaptureRepresentable(rect: rect, trigger: self.trigger, onCapture: self.onCapture, content: self.content())
}
}
struct CaptureRepresentable<Content>: UIViewControllerRepresentable where Content: View {
typealias UIViewControllerType = CaptureNSHostingController<Content>
@Binding var trigger: Bool
let rect: CGRect?
let content: Content
let onCapture: (UIImage) -> Void
init(rect: CGRect?, trigger: Binding<Bool>, onCapture: @escaping (UIImage) -> Void, content: Content) {
self.rect = rect
self._trigger = trigger
self.onCapture = onCapture
self.content = content
}
func makeUIViewController(context: UIViewControllerRepresentableContext<CaptureRepresentable<Content>>) -> CaptureNSHostingController<Content> {
return CaptureNSHostingController(rootView: self.content)
}
func updateUIViewController(_ uiViewController: CaptureNSHostingController<Content>, context: UIViewControllerRepresentableContext<CaptureRepresentable<Content>>) {
if self.trigger {
self.onCapture(uiViewController.getAsImage(rect: rect))
DispatchQueue.main.async {
self.trigger = false
}
}
}
}
class CaptureNSHostingController<Content>: UIHostingController<Content> where Content : View {
override func preferredContentSizeDidChange(forChildContentContainer container: UIContentContainer) {
}
func getAsImage(rect: CGRect? = nil) -> UIImage {
if let rect = rect {
return self.view.asImage(rect: rect)
} else {
return self.view.asImage(rect: self.view.bounds)
}
}
}
extension UIView {
func asImage(rect: CGRect) -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: rect)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment