How can I center horizontally a View
(Image
) in an HStack
? I want a button to be left aligned and the image to be centered horizontally the view.
Currently I have this structure:
VStack { HStack { Button(action: { print("Tapped") }, label: { Image("left-arrow") .resizable() .frame(width: 30, height: 30, alignment: .leading) }).padding(.leading, 20) Spacer() Image("twitter-logo") .resizable() .frame(width: 30, height: 30, alignment: .center) } Spacer() }
Which is giving me this:
But I want to achieve this:
To align a text view along the horizontal axis, you need to use . frame() modifier with maxWidth set to . infinity and alignment to the alignment you want.
Use frame to expand the text's frame to the full width and multilineTextAlignment to cause text (including wrapped text) to be centered.
A view that arranges its subviews in a horizontal line.
A view that arranges its subviews in a vertical line.
You can embed two HStack
's in a ZStack
and place spacers accordingly for the horizontal spacing. Embed all that in a VStack
with a Spacer()
to have everything pushed up to the top.
struct ContentView : View { var buttonSize: Length = 30 var body: some View { VStack { ZStack { HStack { Button(action: { }, label: { Image(systemName: "star") .resizable() .frame(width: CGFloat(30), height: CGFloat(30), alignment: .leading) }).padding(.leading, CGFloat(20)) Spacer() } HStack { Image(systemName: "star") .resizable() .frame(width: CGFloat(30), height: CGFloat(30), alignment: .center) } } Spacer() } } }
Note: In the second HStack
, the image should automatically be center aligned, but if it isn't, you can place a Spacer()
before and after the image.
Edit: Added the VStack
and Spacer()
to move everything to the top like the OP wanted.
Edit 2: Removed padding on image because it caused the image to be slightly offset from the center. Since it is in its own HStack
and center-aligned, it does not need padding.
Edit 3: Thanks to @Chris Prince in the comments, I decided to make a simple NavigationBar-esque custom view that you can provide left, center, and right arguments to create the effect that the OP desired (where each set of views are aligned independently of each other):
struct CustomNavBar<Left, Center, Right>: View where Left: View, Center: View, Right: View { let left: () -> Left let center: () -> Center let right: () -> Right init(@ViewBuilder left: @escaping () -> Left, @ViewBuilder center: @escaping () -> Center, @ViewBuilder right: @escaping () -> Right) { self.left = left self.center = center self.right = right } var body: some View { ZStack { HStack { left() Spacer() } center() HStack { Spacer() right() } } } }
Usage:
struct ContentView: View { let buttonSize: CGFloat = 30 var body: some View { VStack { CustomNavBar(left: { Button(action: { print("Tapped") }, label: { Image(systemName: "star") .resizable() .frame(width: self.buttonSize, height: self.buttonSize, alignment: .leading) }).padding() }, center: { Image(systemName: "star") .resizable() .frame(width: 30, height: 30, alignment: .center) }, right: { HStack { Text("Long text here") Image(systemName: "star") .resizable() .frame(width: 30, height: 30, alignment: .center) .padding(.trailing) }.foregroundColor(.red) }) Spacer() Text("Normal Content") Spacer() } } }
What's about saving button size to a property and add a negative padding to the image? And pay attention to an additional spacer after the image.
struct ContentView: View { var buttonSize: Length = 30 var body: some View { VStack { HStack { Button(action: { print("Tapped") }, label: { Image(systemName: "star") .resizable() .frame(width: buttonSize, height: buttonSize, alignment: .leading) }) Spacer() Image(systemName: "star") .resizable() .frame(width: 30, height: 30, alignment: .center) .padding(.leading, -buttonSize) Spacer() } Spacer() } } }
The result:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With