I’m constructing my first app in SwiftUI. One of many required functionalities is for the consumer to have the ability to make annotations on a picture. The picture must be panned and zoomed. Nonetheless I additionally have to hold monitor of its absolute location on the display ánd hold monitor of relative areas of the annotations (relative to the picture as a complete).
I’m having nice hassle with getting absolutely the and relative positions of the picture on the display. By now I’ve tried a dozen completely different configurations for GeometryReader, nevertheless that solely returns info of ít’s bounds/body inside it is mum or dad, however not of the Views (in my case, the Picture) within the GeometryReader itself. I can not determine a solution to dynamically return these properties. I’ve now given up and can attempt to hold monitor of adjustments (DragGesture and MagnifyGesture) and apply these to the preliminary values of the picture, however in my view that is much less beneficial. I hope this neighborhood can assist!
I’ve tried (amongst different issues) the next related concepts:
struct MyView: View {
// Inputs
var imageName: String
// States
@State non-public var imageSize: CGSize = .zero
@State non-public var scale: CGFloat = 1.0
@State non-public var offset: CGSize = .zero
@State non-public var zoomAnchor: UnitPoint = .heart
@GestureState non-public var magnifyBy = 1.0
@GestureState non-public var translateBy: CGSize = .zero
// Physique
var physique: some View {
let picture = Picture(imageName)
ZStack {
GeometryReader { geometryProxy in
picture
.resizable()
.aspectRatio(contentMode: .match)
.scaleEffect(scale * magnifyBy, anchor: zoomAnchor) // Apply each the persistent and gesture scale
.offset(x: offset.width + translateBy.width, y: offset.top + translateBy.top) // Mix persistent and gesture offset }
.gesture(
SimultaneousGesture(
MagnifyGesture()
.updating($magnifyBy) { worth, gestureState, _ in
gestureState = worth.magnification
}
.onEnded { worth in
scale *= worth.magnification
},
DragGesture()
.updating($translateBy) { value2, gestureState2, _ in
gestureState2 = value2.translation
}
.onEnded { worth in
offset.width += worth.translation.width
offset.top += worth.translation.top
}
)
)
}
}
}
}
}
I additionally tried the background with a transparent Shade trick that I see floating round on the web, nevertheless this doesn’t replace dynamically when pinching or dragging (There could also be a curly bracket to many or few due to copy pasting):
struct MyView: View {
// Inputs
var imageName: String
// States
@State non-public var imageSize: CGSize = .zero
@State non-public var scale: CGFloat = 1.0
@State non-public var offset: CGSize = .zero
@State non-public var zoomAnchor: UnitPoint = .heart
@GestureState non-public var magnifyBy = 1.0
@GestureState non-public var translateBy: CGSize = .zero
// Physique
var physique: some View {
let picture = Picture(imageName)
ZStack {
picture
.resizable()
.aspectRatio(contentMode: .match)
.scaleEffect(scale * magnifyBy, anchor: zoomAnchor) // Apply each the persistent and gesture scale
.offset(x: offset.width + translateBy.width, y: offset.top + translateBy.top) // Mix persistent and gesture offset }
.background(
GeometryReader { geometry in
Shade.clear
.onAppear {
imageSize = geometry.measurement
}
}
)
.gesture(
SimultaneousGesture(
MagnifyGesture()
.updating($magnifyBy) { worth, gestureState, _ in
gestureState = worth.magnification
}
.onEnded { worth in
scale *= worth.magnification
},
DragGesture()
.updating($translateBy) { value2, gestureState2, _ in
gestureState2 = value2.translation
}
.onEnded { worth in
offset.width += worth.translation.width
offset.top += worth.translation.top
}
)
)
}
}
}
}
}
One other approach I attempted this trick was utilizing a View Extension and modifier:
struct SizeCalculator: ViewModifier {
@Binding var bounds: CGRect
func physique(content material: Content material) -> some View {
content material
.background(
GeometryReader { proxy in
Shade.clear // we simply need the reader to get triggered, so let's use an empty shade
.onAppear {
bounds = proxy.body(in: .world)
}
.onChange(of: proxy.measurement) {
bounds = proxy.body(in: .world) // Replace bounds when measurement adjustments
}
}
)
}
}
extension View {
func saveSize(in bounds: Binding) -> some View {
modifier(SizeCalculator(bounds: bounds))
}
}
Nonetheless this additionally refused to replace the bounds/body dynamically, each when used as a modifier to the picture and when referred to as as a technique in for instance an .onChanged(of:scale)
construction. I additionally tried concepts which I do not absolutely perceive however discovered on-line like:
non-public func updateImageBounds(proxy: GeometryProxy) {
// Replace bounds primarily based on the proxy's body
DispatchQueue.major.async {
self.imageBounds = proxy.body(in: .world)
print("Bounds up to date -> minX: (imageBounds.minX), maxX: (imageBounds.maxX), minY: (imageBounds.minY), maxY: (imageBounds.maxY), width: (imageBounds.width), top: (imageBounds.top)")
}
}
Lastly I additionally tried making the Picture a UIImage, which appears to offer higher entry to it is dynamic properties, however I received all types of issues right here with compatibility if I bear in mind appropriately. I’ll have tried 20 different variations of the concepts talked about above which I have not all saved or remembered, however you get the thought. Is there some solution to get these properties in a dynamic style? I’m very curious what the mixed data on right here can give you!