Created
July 5, 2020 19:43
-
-
Save swiftui-lab/9078465e302ce467f4bfe850db45e622 to your computer and use it in GitHub Desktop.
A method for converting a SwiftUI into a UIImage
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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