Home > Software engineering >  SwiftUI: Make Circle "fit" the Text element
SwiftUI: Make Circle "fit" the Text element

Time:03-25

I have the following code:


import SwiftUI

struct EntryHeaderIconView: View {
    private let backgroundSize: Double = 88
    private let color: Color
    private let initials: String

    init(color: Color,
         initials: String = "") {
        self.color = color
        self.initials = initials
    }

    var body: some View {
        ZStack(alignment: .center) {
            Circle()
//                .frame(width: backgroundSize,
//                       height: backgroundSize)
            // Uncommenting this fixes the issue, but the text now clips
                .foregroundColor(color)
            icon
        }
    }


    @ViewBuilder
    private var icon: some View {
        Text(verbatim: initials)
            .font(.system(size: 48, weight: .bold, design: .rounded))
            .foregroundColor(.white)
            .accessibilityIdentifier("entry_header_initials")
    }
}

struct ContentView: View {
    var body: some View {
        VStack {
            EntryHeaderIconView(color: .red,
                                initials: "RT")

            EntryHeaderIconView(color: .green,
                                initials: "LV")

            EntryHeaderIconView(color: .red,
                                initials: "中国")
            Spacer()
        }
    }
}

My goal is to have the Circle element to fit perfectly around the Text element. However, it either continues growing in the VStack, so that it occupies as much space as possible and looks like this (sizing lines commented out):

commented out

Or, if I set a fixed size to the Circle, the content gets clipped:

    var body: some View {
        ZStack(alignment: .center) {
            Circle()
                .frame(width: backgroundSize,
                       height: backgroundSize)
            // Uncommenting this fixes the issue, but the text now clips
                .foregroundColor(color)
            icon
        }
    }

clipped

My goal is to make the Circle size dependent on the Text size, so that it grows (or shrinks) together with it.

Pretty easy to make with AutoLayout, how to achieve the same with SwiftUI?

CodePudding user response:

A ZStack takes up as much space as its child views need. You aren't providing an explicit frame to the Circle, so how could SwiftUI know that the Circle's size should match that of the icon?

Instead, you should add the Circle as a background to your icon, after applying some padding to the icon.

struct EntryHeaderIconView: View {
    private let color: Color
    private let initials: String

    init(color: Color,
         initials: String = "") {
        self.color = color
        self.initials = initials
    }

    var body: some View {
        icon
            .padding(.all, 30)
            .background(background)
    }

    private var background: some View {
        Circle()
            .foregroundColor(color)
    }

    private var icon: some View {
        Text(verbatim: initials)
            .font(.system(size: 48, weight: .bold, design: .rounded))
            .foregroundColor(.white)
            .accessibilityIdentifier("entry_header_initials")
    }
}

struct EntryHeaderIconView_Preview: PreviewProvider {
    static var previews: some View {
        VStack {
            EntryHeaderIconView(color: .red,
                                initials: "RT")

            EntryHeaderIconView(color: .green,
                                initials: "LV")

            EntryHeaderIconView(color: .red,
                                initials: "中国")
        }
    }
}

CodePudding user response:

You need to apply a padding to the text and then show a background in the shape of a circle.

Here's how you can achieve that:

struct Example: View {
    
    var body: some View {
            VStack {
                Text("1")
                    .font(.footnote)
                    .foregroundColor(.white)
                    .padding(5)
                    .background(.red)
                    .clipShape(Circle())
                Text("2")
                    .font(.headline)
                    .foregroundColor(.white)
                    .padding(5)
                    .background(.red)
                    .clipShape(Circle())
                Text("3")
                    .font(.largeTitle)
                    .foregroundColor(.white)
                    .padding(5)
                    .background(.red)
                    .clipShape(Circle())
            }
        
    }
}

enter image description here

  • Related