LinusG.
LinusG.

Reputation: 28892

Views compressed by other views in SwiftUI VStack and List

In my SwiftUI application, I'm trying to implement a UI similar to this:

I've added the two rows for category 1 and category 2. The result looks like this:

NavigationView {
    VStack(alignment: .leading) {
        CategoryRow(...)
        CategoryRow(...)
        Spacer()
    }
    .navigationBarTitle(Text("Featured"))
}

Now, when added the view for the third category – an VStack with images – the following happens:

This happened, after I replaced Spacer(), with said VStack:

VStack(alignment: .leading) {
    Text("Rivers")
        .font(.headline)
    ForEach(self.categories["Rivers"]!.identified(by: \.self)) { landmark in
        landmark.image(forSize: 200)
    }
}

My CategoryRow is implemented as follows:

VStack(alignment: .leading) {
    Text(title)
        .font(.headline)
    ScrollView {
        HStack {
            ForEach(landmarks) { landmark in
                CategoryItem(landmark: landmark, isRounded: self.isRounded)
            }
        }
    }
}

Question

It seems that the views are compressed. I was not able to find any compression resistance or content hugging priority modifiers to fix this.
I also tried to use .fixedSize() and .frame(width:height:) on CategoryRow.

How can I prevent the compression of these views?


Update

I've tried embedding the whole outer stack view in a scroll view:

NavigationView {
    ScrollView { // also tried List
        VStack(alignment: .leading) {
            CategoryRow(...)
            CategoryRow(...)
            ForEach(...) { landmark in
                landmark.image(forSize: 200)
            }
        }
        .navigationBarTitle(Text("Featured"))
    }
}

...and the result is worse:

Upvotes: 37

Views: 27353

Answers (5)

Nilay
Nilay

Reputation: 335

struct ContentView1: View {
    var body: some View {
        NavigationView {
            ScrollView {
                VStack {
                    CategoryListView {
                        CategoryView()
                    }

                    CategoryListView {
                        SquareCategoryView()
                    }
                    
                    CategoryListView {
                        RectangleCategoryView()
                    }
                }
                .padding()
            }
            .navigationTitle("Featured")
        }
    }
}

struct CategoryListView<Content>: View where Content: View {
    private let viewSize: CGFloat = 150
    var content: () -> Content
    
    init(@ViewBuilder content: @escaping () -> Content) {
        self.content = content
    }
    
    var body: some View {
        VStack {
            HStack {
                Text("Category name")
                Spacer()
            }
            ScrollView(.horizontal, showsIndicators: false){
                HStack {
                    ForEach(0..<10) { _ in
                        content()
                    }
                }
            }
        }
    }
}

struct ContentView1_Previews: PreviewProvider {
    static var previews: some View {
        ContentView1()
    }
}

struct CategoryView: View {
    private let viewSize: CGFloat = 150
    var body: some View {
        Circle()
            .fill()
            .foregroundColor(.blue)
            .frame(width: viewSize, height: viewSize)
    }
}

struct RectangleCategoryView: View {
    private let viewSize: CGFloat = 350
    var body: some View {
        Rectangle()
            .fill()
            .foregroundColor(.blue)
            .frame(width: viewSize, height: viewSize * 9 / 16)
    }
}

struct SquareCategoryView: View {
    private let viewSize: CGFloat = 150
    var body: some View {
        Rectangle()
            .fill()
            .foregroundColor(.blue)
            .frame(width: viewSize, height: viewSize)
    }
}

Upvotes: 0

Denis
Denis

Reputation: 1214

You might prevent the views in VStack from being compressed by using

  .fixedSize(horizontal: false, vertical: true)

For example: I have the following VStack:

VStack(alignment: .leading){
        ForEach(group.items) {
            FeedCell(item: $0)
        }
    }

Which render compressed Text()

VStack with compressed elements

When I add .fixedSize(horizontal: false, vertical: true) it doesn't compress anymore

VStack(alignment: .leading){
        ForEach(group.items) {
            FeedCell(item: $0)
                .fixedSize(horizontal: false, vertical: true)
        }
    }

VStack doesn't compresss content

Upvotes: 116

turingtested
turingtested

Reputation: 7154

You could try to add a layoutPriority()operator to your first VStack. This is what the documentation says about the method:

In a group of sibling views, raising a view’s layout priority encourages that view to shrink later when the group is shrunk and stretch sooner when the group is stretched.

So it's a bit like the content compression resistance priority in Autolayout. But the default value here is 0, so you just have to set it to 1 to get the desired effect, like this:

VStack(alignment: .leading) {
    CategoryRow(...)
    CategoryRow(...)
    Spacer()
}.layoutPriority(1)
VStack(alignment: .leading) {
    ...
}

Hope it works!

Upvotes: 28

Argas
Argas

Reputation: 1527

It looks like is not enough space for all your views in VStack, and it compresses some of them. You can embed it into the ScrollView

NavigationView {
 ScrollView {
   VStack(alignment: .leading) {
      CategoryRow(...)
      CategoryRow(...)
      /// you images and so on
   }
  }
}

Upvotes: 1

Marius Waldal
Marius Waldal

Reputation: 9932

I think your topmost view (in the NavigationView) needs to be a List, so that it is scrollable:

NavigationView {
            List {
                ...

Or use a ScrollView.

A stack automatically fits within a screen. If you want your content to exceed this, you would have used a ScrollView or a TableView etc i UIKit

EDIT:

Actually, a little Googling brought this result, which seems to be exactly what you are making: https://developer.apple.com/tutorials/swiftui/composing-complex-interfaces

Upvotes: -2

Related Questions