I have a view which uses Geometry Reader to calculate how large the image area should be:
GeometryReader { metrics in
ZStack{
self.image
.resizable()
.frame(width: (metrics.size.height - 10) * 0.561403509 , height: metrics.size.height - 10, alignment: .top)
.clipped()
}
}
But I have a function where I want to use the frame height and width calculated by the GeometryReader in order to crop the image.
I have a separate function which crops the image when a button is pressed:
DownloadImageButton(prepareImagefunction: { self.prepareImage() })
Which then calls a prepareImage function:
func prepareImage( ) {
// In order for image cropping to work, we need the equivalent of view.bounds in order to adjust the crop so that it correctly measures the crop area. We need metrics.width and metrics.height
var adjustmentScaleForDisplaySize = targetSize.width / metrics.width
Note that the GeometryReader is a child of the parent view where prepareImage is called. Therefore, ideally, the child would save the metrics values in an EnvironmentObject or Binding to the parent.
The second tip is to use GeometryReader inside an overlay or background of any view. SwiftUI keeps overlay and background views in the same size as the view that you apply them. It limits the size of GeometryReader and doesn’t allow it to grow and fill all the available space.
In the implementation above, <container geometry> is an instance of GeometryProxy. GeometryProxy simply encapsulates the container's frame and safe area insets, provided at runtime by SwiftUI. In this example, GeometryReader is used to create a view scaled down to exactly half of its parent container:
Usually, SwiftUI views place content in the center of its coordinate space. As you can see, GeometryReader ’s @ ViewBuilder closure has the parameter called geometry. This parameter is an instance of GeometryProxy struct.
GeometryReader is a view that gives you access to the size and position of it's parent. For example:
Is there a way to pass the value calculated by GeometryReader to a function?
Yes, you can do this. The var metrics
is a GeometryProxy
, so function signature should be like in the following example
private func resizedImage(for metrics: GeometryProxy) -> some View {
self.image
.resizable()
.frame(width: (metrics.size.height - 10) * 0.561403509 , height: metrics.size.height - 10, alignment: .top)
.clipped()
}
so your usage
GeometryReader { metrics in
ZStack{
self.resizedImage(for: metrics)
}
}
Update: for changed request
Assuming you have in child view binding as
@Binding var cropSize: CGSize
the above function can be modified as follow
private func resizedImage(for metrics: GeometryProxy) -> some View {
let size = CGSize(width: (metrics.size.height - 10) * 0.561403509,
height: metrics.size.height - 10)
self.cropSize = size
return self.image
.resizable()
.frame(width: size.width, height: size.height, alignment: .top)
.clipped()
}
Since I actually ONLY needed the crop size, and didn't necessarily need an image returned, I did the following which also helped to avoid throwing an error which can occur in the accepted answer:
[SwiftUI] Modifying state during view update, this will cause undefined behavior.
I added an .onAppear function call which helps to avoid the above error, and then properly passes the crop dimension to the parent:
GeometryReader { metrics in
ZStack{
self.image
.resizable()
.frame(width: (metrics.size.height - 10) * 0.561403509 , height: metrics.size.height - 10, alignment: .top)
.clipped()
}.onAppear{
self.saveCropSize(for: metrics)
}....
private func saveCropSize(for metrics: GeometryProxy){
let size = CGSize(width: (metrics.size.height - 10) * 0.561403509,
height: metrics.size.height - 10)
self.cropSize = size
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With