Clifton Labrum
Clifton Labrum

Reputation: 14168

SwiftUI: Center Content in ScrollView

I'm trying to center a bunch of views in a VStack within a ScrollView in SwiftUI. To simplify things, I'm just trying to get it to work with a single Text view. Here's what I've come up with so far:

var body: some View {
  ScrollView(alwaysBounceVertical: true){
    HStack(alignment: .center) {
      Spacer()
      Text("This Is a Test")
      Spacer()
    } //HStack
    .background(Color.green)
  } //ScrollView
  .background(Color.gray)
}

This results in this:

enter image description here

I want the text to be in the middle like this:

enter image description here

So the HStack should be full-width and the Text should be centered within it. It seems like this should be easy, but I don't get what I'm doing wrong. :)

Upvotes: 14

Views: 20579

Answers (4)

Josh Brown
Josh Brown

Reputation: 53153

Depending on what you're doing, ViewThatFits might be a good solution for this. For my use case, I needed a horizontal ScrollView if the content was too wide, but an HStack was sufficient otherwise.

This works for me:

var body: some View {
  ViewThatFits {
    HStack(alignment: .center) {
      Spacer()
      Text("This Is a Test")
      Spacer()
    } //HStack
    .background(Color.green)

    ScrollView(alwaysBounceVertical: true){
      HStack(alignment: .center) {
        Spacer()
        Text("This Is a Test")
        Spacer()
      } //HStack
      .background(Color.green)
    } //ScrollView
    .background(Color.gray)
  }
}

Upvotes: 1

Danilo
Danilo

Reputation: 59

You should use the modifier frame and set its maxWidth: .infinity. So it tells its parent: "the wider, the better" :)

var body: some View {
  ScrollView(.vertical, showsIndicators: true){
    HStack(alignment: .center) {
      Spacer()
      Text("This Is a Test")
        .frame(maxWidth: .infinity) // <- this
      Spacer()
    } //HStack
    .background(Color.green)
  } //ScrollView
  .background(Color.gray)
}

And this works regardless its parent. Scrollview or whatever View it's set in.

Paul is doing a great job clarifying it to all of us here:

https://www.hackingwithswift.com/quick-start/swiftui/how-to-give-a-view-a-custom-frame

Answer compatible with Xcode 12.1 (12A7403) I hope this helps 👍 dsa

Upvotes: 4

M Reza
M Reza

Reputation: 19758

This seems to be a bug in Xcode 11.0 beta, ScrollView content wouldn't fill the scroll view. If you replace the ScrollView with a List it will work as expected. But if you have to use a scroll view, one workaround is to fix the scroll view's content width.

So your code will look something like this:

ScrollView(alwaysBounceVertical: true) {
  HStack(alignment: .center) {
    Spacer()
    Text("This Is a Test")
    Spacer()
  } // HStack
  .frame(width: UIScreen.main.bounds.width) // set a fixed width
  .background(Color.green)
} // ScrollView
.background(Color.gray)

Result:

enter image description here

Upvotes: 4

Benjamin Kindle
Benjamin Kindle

Reputation: 1844

Using GeometryReader, you can get information about the size of the containing view and use that to size your view.

    var body: some View {
        GeometryReader { geometry in                <--- Added
            ScrollView(alwaysBounceVertical: true){
                HStack(alignment: .center) {
                    Spacer()
                    Text("This Is a Test")
                    Spacer()
                } //HStack
                .frame(width: geometry.size.width)  <--- Added
                .background(Color.green)
            } //ScrollView
        .background(Color.gray)
    }
}

edit: after looking into this more, it seems that part of the problem is that you are using a ScrollView. If you remove that parent, the spacers in the HStack will automatically cause stretching to fill the view. I'm guessing the automatic stretching doesn't happen in ScrollViews because there's no finite limit to how big it can be, how much would it stretch? (because a ScrollView can scroll in any direction)

Upvotes: 21

Related Questions