Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the position of a view in SwiftUI

Tags:

swift

swiftui

Below I have three Text views, the location of View 3 will be dynamic every time the app loads, and what I would like to be able to do is animate View 2 to the exact position of View 3 regardless of where View 3 is on the screen.

I found this thread but I'm not sure how to use ReometryReader to accomplish what I need.

How can I get the current position of View 3?

How can I assign the x and y position of the View 3 to xPosition and yPosition respectively inside the withAnimation method?

struct PositioningViews: View {
    @State private var xPosition:CGFloat = 0
    @State private var yPosition:CGFloat = 0
    
    var body: some View {
        HStack {
            Text("View 1")
                .background(.purple)
           
            Text("View 2")
                .background(.orange)
                .offset(x: xPosition, y: yPosition)
                .onAppear {
                    withAnimation(Animation.easeOut(duration: 0.3).delay(0.5)) {
                        self.xPosition = 50
                        self.yPosition = 0
                    }
                }
            
            Text("View 3")
                .background(.blue)
        }
    }
}
like image 227
fs_tigre Avatar asked Oct 21 '25 14:10

fs_tigre


2 Answers

Complete Working Code:

struct PositioningViews: View {
    @State private var xPosition2:CGFloat = 0
    @State private var yPosition2:CGFloat = 0
    @State private var xPosition3:CGFloat = 0
    @State private var yPosition3:CGFloat = 0
    @State private var offsetValueX = 0.0
    @State private var offsetValueY = 0.0
    var body: some View {
        HStack {
            Text("View 1")
                .background(.purple)
           
            
            Text("View 2")
                .background(.orange)
                .overlay(
                    GeometryReader{ geo -> AnyView in
                        
                        AnyView(Color.clear
                            .onAppear(){
                              
                                    xPosition2 = geo.frame(in: .global).midX
                                    yPosition2 = geo.frame(in: .global).midY
                                
                                
                            })
                    }
                )

                .background(.blue)
                .offset(x: offsetValueX, y: offsetValueY)
                .onAppear(){
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                        withAnimation {
                            offsetValueX = xPosition3 - xPosition2
                            offsetValueY = yPosition3 - yPosition2
                        }
                    }
                }
                .zIndex(2)
            
            
            Text("View 3")
                .background(.blue)
                .overlay(
                    GeometryReader{ geo -> AnyView in
                        
                        AnyView(Color.clear
                            .onAppear(){
                               
                                xPosition3 = geo.frame(in: .global).midX
                                yPosition3 = geo.frame(in: .global).midY
                                    
                                
                            })
                    }
                )
                .zIndex(1)
        }
    }
}

EDIT, Without AnyView:

struct PositioningViews: View {
    @State private var xPosition2:CGFloat = 0
    @State private var yPosition2:CGFloat = 0
    @State private var xPosition3:CGFloat = 0
    @State private var yPosition3:CGFloat = 0
    @State private var offsetValueX = 0.0
    @State private var offsetValueY = 0.0
    var body: some View {
        HStack {
            Text("View 1")
                .background(.purple)
           
            
            Text("View 2")
                .background(.orange)
                .overlay(
                    GeometryReader{ geo  in
                        Color.clear
                            .onAppear(){
                                xPosition2 = geo.frame(in: .global).midX
                                yPosition2 = geo.frame(in: .global).midY
                            }
                    }
                )
                .background(.blue)
                .offset(x: offsetValueX, y: offsetValueY)
                .onAppear(){
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                        withAnimation {
                            offsetValueX = xPosition3 - xPosition2
                            offsetValueY = yPosition3 - yPosition2
                        }
                    }
                }
                .zIndex(2)
            
            Text("View 3")
                .background(.blue)
                .overlay(
                    GeometryReader{ geo in
                       Color.clear
                            .onAppear(){
                                xPosition3 = geo.frame(in: .global).midX
                                yPosition3 = geo.frame(in: .global).midY
                                    
                            }
                    }
                )
                .zIndex(1)
        }
    }
}
like image 188
Tanvirgeek Avatar answered Oct 23 '25 03:10

Tanvirgeek


import SwiftUI

@available(macOS 13.0, *)
public extension View {
    func getFrameInfo(_ space: CoordinateSpace ,_ pos: Binding<CGRect>) -> some View {
        self.modifier(GetFrameInfo(space: space, pos: pos))
    }
}

@available(macOS 13.0, *)
private struct GetFrameInfo: ViewModifier {
    let space: CoordinateSpace
    @Binding var pos: CGRect
    
    func body(content: Content) -> some View {
        content
            .onGeometryChange (for: CGRect.self) {
                $0.frame(in: space)
            } action: { newValue in
                pos = newValue
            }
    }
}


usage:

struct Test: View {
    @State private var frame: CGRect = .zero
    
    var body: some View {
        Text("hello")
            .getFrameInfo(.global, $frame)
    }
}
like image 20
Andrew Avatar answered Oct 23 '25 04:10

Andrew