18.4 C
New York
Monday, March 10, 2025

ios – Dynamically replace View measurement info in SwiftUI


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!

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles