How to center a buttons text when it also has a image?

Viewed 81

In the following code I'm trying to produce a layout that centers the button text, while having an image on the right hand side. Currently the text is offset by the width of the image rather than being centered (I've added a . in a label so that you can see its not centred. I'd expect the . to be above the B in the buttons text).

struct ContentView: View {
    var body: some View {
        VStack {
            Text(".") // Center point
            Button(action: {}, label: {
                HStack {
                    Spacer()
                    Text("ABC")
                    Spacer()
                    Image(systemName: "person.circle.fill")
                }
            })
        }
    }
}

enter image description here

4 Answers

A possible approach is to use overlay - text is at center by default, but image can be place where needed.

Note: of course if your text is dynamic and can be long then it is needed some more complicated solution, but expectation probably would be different

So, for your snapshot

demo

Button(action: {}, label: {
      Text("ABC")
        .frame(maxWidth: .infinity)
        .overlay(Image(systemName: "person.circle.fill").padding(),
            alignment: .trailing)
})

You can use ZStack containing an HStack { Spacer().

struct ContentView: View {
    var body: some View {
        VStack {
            Text(".") // Center point
            
            Button(action: {}, label: {
                ZStack {
                    Text("ABC")
                    
                    HStack {
                        Spacer()
                        Image(systemName: "person.circle.fill")
                    }
                }
            })
        }
    }
}

Result:

ABC is centered, with Profile button on right

A solution that involves a technique mentioned before but accounts for long strings and variably-sized images:

struct ContentView: View {
    
    @State private var imageWidth: CGFloat = 0
    
    var body: some View {
        VStack {
            Text(".")
            Button(action: {}, label: {
                ZStack {
                    HStack {
                        Spacer()
                        Image(systemName: "person.circle.fill")
                            .background(GeometryReader {
                                Color.clear.preference(key: ImageWidthPrefKey.self,
                                                       value: $0.frame(in: .local).size.width)
                            })
                            .onPreferenceChange(ImageWidthPrefKey.self) {
                                imageWidth = $0
                            }
                    }
                    Text("A long string that will probably wrap when it reaches the end and be a couple of lines long")
                       .multilineTextAlignment(.center)
                       .padding(.horizontal,imageWidth)
                }
            })
        }
    }
    
    struct ImageWidthPrefKey : PreferenceKey {
        static var defaultValue: CGFloat { 0 }
        static func reduce(value: inout Value, nextValue: () -> Value) {
            value = value + nextValue()
        }
    }
}

Sad but simple:

struct ContentView: View {
    var body: some View {
        VStack {
            Text(".") // Center point
            Button(action: {}, label: {
                HStack {
                    Image(systemName: "person.circle.fill")
                       .opacity(0)
                       .accessibility(hidden: true)
                    Spacer()
                    Text("ABC")
                    Spacer()
                    Image(systemName: "person.circle.fill")
                }
            })
        }
    }
}
Related