In SwiftUI, how to determine a Circle()'s size to draw a overlay on scale with that circle?

Viewed 7387

Target's iOS and macOS as well.

I have this very general code to draw an Image and some text:

    let dynamicScale = 1.2 // here just static but will be !!calculated!!
    ... 
    VStack (alignment: .leading){
            Image(user.imageName)
                .resizable()
                .frame(width: 60, height: 60)
                .clipped()
                .clipShape(Circle())
                .overlay(Circle()
                    .scale(dynamicScale) // with some calculation
                    .stroke(Color.red, lineWidth: 2))


            Text(user.username).font(.headline)
            Text(user.message).font(.subheadline)
            }.padding(.init(top: 10, leading: 5, bottom: 10, trailing: 5))
    }

for now the size is static, but later, its size will be depend on-screen size and rotation. So will the stroke be depended of that, size and some more settings. How can I get dynamically the circle size ( frame ) in SwiftUI, so I can us it to draw a second Circle() stroke/border at a offset of the Image. I want .scale(dynamicScale) dynamically calculated depending of the size of the image

As far as I understand I can't insert any Swift code to read that size, like 'let size = Image().frame.width'. Logical, there are no values there yet.

Somebody has some suggestions?

1 Answers

Here is an interactive example using GeometryReader.

GeometryReader takes up all the space available to it, which seems to be the size of the view it is overlaying if used inside of the overlay modifier.

You can then read the properties of the GeometryProxyobject to get details regarding the size of the space it is taking up and use that to size your views inside of the GeometryReader.

In this case we have a stroke circle that is dynamically sized based on a slider, with another circle overlayed that is always half the size of the first circle.

import SwiftUI

struct RelativeSizeView: View {
    @State var dynamicSize: CGFloat = 100
    var body: some View {
        VStack(spacing: 20) {
            Slider(value: $dynamicSize, in: 100...1000)
            Circle() // dynamically sized circle
                .stroke()
                .frame(width: dynamicSize, height: dynamicSize)
                .overlay(GeometryReader{ geometry in
                    Circle() // sized based on first
                        .frame(width: geometry.size.width*0.5, height: geometry.size.height*0.5)
                })
        }.padding()
    }
}

struct RelativeSizeView_Previews: PreviewProvider {
    static var previews: some View {
        RelativeSizeView()
    }
}
Related