UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

I'm Stuck - SwiftUI Geometry Reader & .coordinateSpace

Forums > SwiftUI

Hi Everyone,

I read the article in Hacking With SwiftUI about geometry reader and the various types of coordinate spaces. I'm stuck implementing this though as I can't seem to get it to work.

I'm trying to place circles over locations on an image (these will eventually be buttons but I'm starting small). The image is actually a hand drawn map and I need those circles to stay over the same spot regardless of device size. I'm forcing 'landscape only' since the map is wider than it is tall; that makes things a bit easier but I'm still having problems. My circles are fine on the Y-axis but not on the X-axis. I've tried a number of iterations for the position but none seem to work. I even thought I'd try a different approach and try calculating an offset from center but that didn't work either.

Below I've copied the struct that defines a 'place.' Master width and height are the size of the original file, and the pdfXCoordinate and pdfYCoordinate are the 'true' locations on the original image. The calculated variables were my attempts to use geometry reader to correctly place those circles on the image.

The MapView struct has an arry of four places to display. I gave the image itself a named coordinate space thinking that's how I'd get those circles to appear in the right place but no matter what variations I try it doesn't seem to work. The background colors are just there to help me visualize the views.

Any help is greatly appreciated.

Thanks, Jim

struct SimplePlace: Codable, Hashable, Identifiable {

    let id = UUID()
     let masterWidth = CGFloat(6082)
     let masterHeight = CGFloat(4502)

     var name: String

     var pdfXCoordinate: CGFloat
     var pdfYCoordinate: CGFloat
     var xOffSet: CGFloat {
         return (masterWidth/2)-pdfXCoordinate
     }
     var yOffSet: CGFloat {
         return (masterHeight/2) - pdfYCoordinate
     }
     var pdfXSpot: CGFloat {
         return pdfXCoordinate/masterWidth
     }
     var pdfYSpot: CGFloat {
         return pdfYCoordinate/masterHeight
     }

     static var example: SimplePlace {
         return SimplePlace(name: "Spot 59", pdfXCoordinate: 3671, pdfYCoordinate: 826)
     }

}

struct MapView: View {

let places: [SimplePlace] = [
SimplePlace(name: "Spot 59", pdfXCoordinate: 3671, pdfYCoordinate: 826),
SimplePlace(name: "Spot 81", pdfXCoordinate: 4929, pdfYCoordinate: 442),
SimplePlace(name: "Spot 53", pdfXCoordinate: 2989, pdfYCoordinate: 2137),
SimplePlace(name: "Spot 13", pdfXCoordinate: 4682, pdfYCoordinate:  3188)
]

let masterWidth = CGFloat(6082)
let masterHeight = CGFloat(4502)

var body: some View {
    GeometryReader {geometry in
        ZStack{
            Image("Map1")
            .resizable()
                .coordinateSpace(name: "Map")
                .aspectRatio(contentMode: .fit)
                //.frame(height:geometry.size.height)
                .overlay(
                    RoundedRectangle(cornerRadius: 1)
                        .stroke(Color.orange, lineWidth: 4)
                )

            ForEach(0..<self.places.count) {place in
                Circle()
                .frame(width:10, height: 10)
                .foregroundColor(Color.black)
                    .position(x: geometry.frame(in: .named("Map")).width*self.places[place].pdfXSpot, y: geometry.frame(in: .named("Map")).height*self.places[place].pdfYSpot)
            }
        }

    }.background(Color.green)
}

}

3      

I briefly thought I had a solution using UIScreen.main.bounds & using that to determine the ratio of the device's height to the master height, then using that multiplied by the master width to set a frame on the GeometryReader. Unfortunately I have a tab bar at the bottom to navigate between various views and that throws it off. There must be a way to do this with .coordinateSpace(in: .named("Map")) I feel like I'm so close...

3      

I solved the problem in a roundabout way. I realized that I could calculate the 'true' width of my image since I knew the original width/height and the height of the view. Geometry reader gave me the entire width, so I was able to subtract the image's width from that and divide by two, which essentially gave me the 'dead space' on the side of my image. I then added that to the existing x-coordinate data and things seem to be lining up nicely. It feels like a real hack though, and with order of operations & parantheses it's a bit of a mess.

3      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.