I am trying to share a subview as an image. This question combines code from these two questions conversion of a subview to an image and showing a UIActivityController
in swift.
The view and minimal working example are below. I am trying to share SubView
, here, the rectangle with the text as an image. The problem is that self.uiimage
evaluates to nil
whenever I press the button and causes the app to crash.
I know that an image can be created because I can add this line of code UIImageWriteToSavedPhotosAlbum(self.uiimage!, nil, nil, nil)
in the action
of the button and the image gets saved to the photos album.
My best guess is that the ActivityViewController
is created when the entire UI is first rendered, at which point self.uiimage
would be nil
. I suppose I could make uiimage
a binding property to ActivityViewController
, but I would rather not do that since it would complicate creating the activityItems in the parent controller. Any ideas on what I can do to fix this?
import SwiftUI
struct SubView: View {
var body: some View {
VStack{
Text("Hello, world!")
.padding()
Rectangle()
.foregroundColor(.blue)
}
}
}
struct ContentView: View {
@State private var isSharePresented: Bool = false
@State private var rect: CGRect = .zero
@State private var uiimage: UIImage? = nil
var body: some View {
SubView()
.frame(width: 150, height: 150)
.background(RectGetter(rect: $rect))
Button(action: {
self.uiimage = UIApplication.shared.windows[0].rootViewController?.view.asImage(rect: self.rect)
self.isSharePresented = true
}) {
Label("Share", systemImage: "square.and.arrow.up")
}
.sheet(isPresented: $isSharePresented, onDismiss: {
print("Dismiss")
}, content: {
ActivityViewController(activityItems: [self.uiimage!])
})
}
}
struct ActivityViewController: UIViewControllerRepresentable {
var activityItems: [Any]
var applicationActivities: [UIActivity]? = nil
@Environment(\.presentationMode) var presentationMode
func makeUIViewController(context: UIViewControllerRepresentableContext<ActivityViewController>) -> UIActivityViewController {
let controller = UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities)
controller.completionWithItemsHandler = { (activityType, completed, returnedItems, error) in
self.presentationMode.wrappedValue.dismiss()
}
return controller
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext<ActivityViewController>) {}
}
struct RectGetter: View {
@Binding var rect: CGRect
var body: some View {
GeometryReader { proxy in
self.createView(proxy: proxy)
}
}
func createView(proxy: GeometryProxy) -> some View {
DispatchQueue.main.async {
self.rect = proxy.frame(in: .global)
}
return Rectangle().fill(Color.clear)
}
}
extension UIView {
func asImage(rect: CGRect) -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: rect)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}