Home > other >  How to properly place GeometryReader to a specific point to read coordinates
How to properly place GeometryReader to a specific point to read coordinates

Time:07-31

To preface, I am only a few weeks into SwiftUI. This project is also based on a hypocycloid. Having said that, I am trying to place a GeometryReader to track a very specific point (spoke point, or: the red dot) — which is offset, animated and nested inside of several ZStacks and an overlay — relative to the rest of the screen.

My issue is that, I don't know how to properly call the function to track the coordinates from within the GeometryReader and output it to a variable. I get the error that: Type '()' cannot conform to 'View' On top of that, my coordinates do not seem accurate, as I never get a negative value.

Without GeometryReader:

import SwiftUI

struct CreateHypotrochoid: View {
    @State var isAnimating: Bool    = true
    @State var offsetX:     CGFloat = .zero
    @State var offsetY:     CGFloat = .zero
    
    var body: some View {
        ZStack {
            // MARK: - OUTER CIRCLE -
            Circle()
                .stroke(style: .init(
                    lineWidth: 2.0,
                    lineCap: .round,
                    lineJoin: .round))
                .foregroundColor(Color.white)
                .frame(width: 200,
                       height: 200)
            
            ZStack {
                // MARK: - INNER CIRCLE -
                Circle()
                    .stroke(style: .init(
                        lineWidth: 2.0,
                        lineCap: .round,
                        lineJoin: .round))
                    .foregroundColor(Color.red)
                    .frame(width: 100,
                           height: 100)
                    .overlay(
                        // MARK: - INNER CIRCLE SPOKE -
                        Rectangle()
                            .stroke(style: .init(
                                lineWidth: 1.0,
                                lineCap: .round,
                                lineJoin: .round))
                            .foregroundColor(Color.white)
                            .frame(width: 1.0,
                                   height: 100)
                            .offset(x: 0,
                                    y: -50)
                            .rotationEffect(
                                Angle(degrees: isAnimating ? -360 : 0),
                                anchor: .center)
                            .animation(
                                Animation.linear(duration: 3)
                                    .repeatForever(autoreverses: false), value: isAnimating)
                    )
                
                    .overlay(
                        // MARK: - DETERMINE SPOKE POINT -
// I need to track this circle's x & y coordinates.
//                         |
//                         V
                        Circle()
                            .foregroundColor(Color.red)
                            .frame(width: 6,
                                   height: 6)
                            .offset(x: 0,
                                    y: -100)
                            .rotationEffect(
                                Angle(degrees: isAnimating ? -360.0 : 0),
                                anchor: .center)
                            .animation(
                                Animation.linear(duration: 3)
                                    .repeatForever(autoreverses: false), value: isAnimating)
                    )
                
                // MARK: - INNER CIRCLE (CONTINUED) -
                    .offset(x: 0,
                            y: -50)
                    .rotationEffect(
                        Angle(degrees: isAnimating ? 360.0 : 0),
                        anchor: .center)
                    .animation(
                        Animation.linear(duration: 2)
                            .repeatForever(autoreverses: false), value: isAnimating)
            }
        }
        
        .rotationEffect(Angle(degrees: 90))
        .padding(20)
        
        Button {
            isAnimating.toggle()
        } label: {
            Text("Start/Stop")
        
        }
    }
}

And with GeometryReader:

import SwiftUI

struct CreateHypotrochoid: View {
    @State var isAnimating: Bool    = true
    @State var offsetX:     CGFloat = .zero
    @State var offsetY:     CGFloat = .zero
    
    var body: some View {
        ZStack {
            // MARK: - OUTER CIRCLE -
            Circle()
                .stroke(style: .init(
                    lineWidth: 2.0,
                    lineCap: .round,
                    lineJoin: .round))
                .foregroundColor(Color.white)
                .frame(width: 200,
                       height: 200)
            
            ZStack {
                // MARK: - INNER CIRCLE -
                Circle()
                    .stroke(style: .init(
                        lineWidth: 2.0,
                        lineCap: .round,
                        lineJoin: .round))
                    .foregroundColor(Color.red)
                    .frame(width: 100,
                           height: 100)
                    .overlay(
                        // MARK: - INNER CIRCLE SPOKE -
                        Rectangle()
                            .stroke(style: .init(
                                lineWidth: 1.0,
                                lineCap: .round,
                                lineJoin: .round))
                            .foregroundColor(Color.white)
                            .frame(width: 1.0,
                                   height: 100)
                            .offset(x: 0,
                                    y: -50)
                            .rotationEffect(
                                Angle(degrees: isAnimating ? -360 : 0),
                                anchor: .center)
                            .animation(
                                Animation.linear(duration: 3)
                                    .repeatForever(autoreverses: false), value: isAnimating)
                    )
                
                    .overlay(
                        // MARK: - DETERMINE SPOKE POINT -
                        GeometryReader { geometry in
                            Circle()
                                .foregroundColor(Color.red)
                                .frame(width: 6,
                                       height: 6)
                                .offset(x: 0,
                                        y: 0)
                                .rotationEffect(
                                    Angle(degrees: isAnimating ? -360.0 : 0),
                                    anchor: .center)
                                .animation(
                                    Animation.linear(duration: 3)
                                        .repeatForever(autoreverses: false), value: isAnimating)
                            
                            Text("coordinates: \(geometry.frame(in: .global).origin.dictionaryRepresentation)")
                                .offset(x: 50,
                                        y: 50)
                                .frame(width: 200,
                                       height: 200,
                                       alignment: .center)
                        }
                        
                            .background(Color.green)
                            .frame(width: 6,
                                   height: 6)
                            .offset(x: 0,
                                    y: -100)
                            .rotationEffect(
                                Angle(degrees: isAnimating ? -360.0 : 0),
                                anchor: .center)
                            .animation(
                                Animation.linear(duration: 3)
                                    .repeatForever(autoreverses: false), value: isAnimating)
                    )
                
                // MARK: - INNER CIRCLE (CONTINUED) -
                    .offset(x: 0,
                            y: -50)
                    .rotationEffect(
                        Angle(degrees: isAnimating ? 360.0 : 0),
                        anchor: .center)
                    .animation(
                        Animation.linear(duration: 2)
                            .repeatForever(autoreverses: false), value: isAnimating)
            }
        }
        
        //.rotationEffect(Angle(degrees: 90))
        .padding(20)
        
        Button {
            isAnimating.toggle()
        } label: {
            Text("Start/Stop")
            
        }
    }
}

CodePudding user response:

If you want to assign the changes of your GeometryReader´s frame to a var use the .onChange(of: modifier.

E.g:

.onChange(of: geometry.frame(in: .global)) { newValue in
    test = newValue
}

with:

@State private var test: CGRect = .zero
  • Related