Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Center View horizontally in SwiftUI

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:

enter image description here

But I want to achieve this:

enter image description here

like image 782
SwiftiSwift Avatar asked Jun 05 '19 14:06

SwiftiSwift


People also ask

How do I align a view in Swiftui?

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.

How do I center a form in Swiftui?

Use frame to expand the text's frame to the full width and multilineTextAlignment to cause text (including wrapped text) to be centered.

What is HStack Swiftui?

A view that arranges its subviews in a horizontal line.

What is a Vstack?

A view that arranges its subviews in a vertical line.


2 Answers

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()         }     } } 

Example code shown on simulator

like image 59
RPatel99 Avatar answered Oct 01 '22 17:10

RPatel99


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:

enter image description here

like image 30
Artem Novichkov Avatar answered Oct 01 '22 18:10

Artem Novichkov