My goal: To use geometry reader and a screenshot function (already written) to grab just the small portion of an image that falls behind a close button.
Setup: I have a ZStack with an Image
and a smaller button overtop of it (it is a system image for this example). Currently, I have the close button aligned to the top of the background image and everything looks good at first glance.
However, when the close button is tapped, I need to grab a screenshot of the close button itself (matching frame and size).
Everything worked when my ZStack
had a .center
alignment, but now that I have set alignment: .top
, my screenshot is still occurring in the center of the image.
How can I shift the GeometryReader
that encompasses the close button to allow for the screenshot to be of the close button (and a bit of extra background behind it) when it is tapped?
My Code:
struct ImageView: View {
var body: some View {
ZStack(alignment: .top) {
Image("ZoomedBuzz")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: UIScreen.main.bounds.width)
GeometryReader { geo in
Image(systemName: "xmark.circle.fill")
.font(.system(size: 80))
.border(Color.blue)
.onTapGesture {
let globalImage = takeScreenshot(origin: geo.frame(in: .global).origin,
size: geo.size)
print(globalImage)
}
}
.frame(width: 80, height: 80, alignment: .top)
}
}
}
Taking the screenshot:
extension UIView {
var renderedImage: UIImage {
// rect of capture
let rect = self.bounds
// create the context of bitmap
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
let context: CGContext = UIGraphicsGetCurrentContext()!
self.layer.render(in: context)
// get a image from current context bitmap
let capturedImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return capturedImage
}
}
extension View {
func takeScreenshot(origin: CGPoint, size: CGSize) -> UIImage {
let window = UIWindow(frame: CGRect(origin: origin, size: size))
let hosting = UIHostingController(rootView: self)
hosting.view.frame = window.frame
window.addSubview(hosting.view)
window.makeKeyAndVisible()
return hosting.view.renderedImage
}
}
Desired Result (but taken at the top of the ZStack
):
Current Result (screenshot being taken in the middle of the background image centered with the ZStack
):
If you want the GeometryReader to be close around the button instead of capturing the whole screen you can set it as the buttons background.
Unfortunately I don't know how your screenshot method works therefore I couldn't verify that it's working with your setup, but now you can simply screenshot the whole GeometryReader's frame.
struct ImageView: View {
@State private var geo: GeometryProxy!
var body: some View {
ZStack(alignment: .top) {
Image("ZoomedBuzz")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: UIScreen.main.bounds.width)
Image(systemName: "xmark.circle.fill")
.font(.system(size: 80))
.border(Color.blue)
.background(rectReader())
.onTapGesture {
let globalImage = takeScreenshot(origin: geo.frame(in: .global).origin,
size: geo.size)
print(globalImage)
}
}
}
private func rectReader() -> some View {
return GeometryReader { geometry -> AnyView in
DispatchQueue.main.async {
self.geo = geometry
}
return AnyView(Rectangle().fill(Color.clear))
}
}
}