在SwiftUI的VStack和List中,视图被其他视图压缩

34

在我的SwiftUI应用程序中,我正在尝试实现类似于此图的UI:

我已经添加了类别1和类别2的两行。结果看起来像这样:

NavigationView {
    VStack(alignment: .leading) {
        CategoryRow(...)
        CategoryRow(...)
        Spacer()
    }
    .navigationBarTitle(Text("Featured"))
}
现在,当添加第三个类别的视图 - 一个带有图像的VStack时,会发生以下情况: 这发生在我用该VStack替换了Spacer()后。
VStack(alignment: .leading) {
    Text("Rivers")
        .font(.headline)
    ForEach(self.categories["Rivers"]!.identified(by: \.self)) { landmark in
        landmark.image(forSize: 200)
    }
}

我的CategoryRow实现如下:

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

问题

看起来视图被压缩了。我找不到任何压缩阻力或内容抱紧优先级修饰符来解决这个问题。 我还尝试在CategoryRow上使用.fixedSize().frame(width:height:)

如何防止这些视图被压缩?


更新

我尝试将整个外部堆栈视图嵌入滚动视图中:

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

...结果更糟糕:


你尝试过使用.scaledToFit()吗? - naudecruywagen
5个回答

105

您可以通过使用以下方法防止 VStack 中的视图被压缩:

  .fixedSize(horizontal: false, vertical: true)

例如: 我有下面的VStack:

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

哪个函数能渲染压缩的文本()

包含压缩元素的VStack

当我添加 .fixedSize(horizontal: false, vertical: true) 后,它不再压缩

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

VStack无法压缩内容


1
谢谢。我有多行的 Text() 和一个可调整大小的 Image() 在竖直空间上争夺位置,但是通过这个方法,我能够强制文本优先并抵抗压缩。 - Cloud

26
你可以尝试在第一个 VStack 中添加一个 layoutPriority() 运算符。这是文档对该方法的解释:

在一组兄弟视图中,提高视图的布局优先级会鼓励该视图在组被缩小时稍后收缩,在组被拉伸时更早地拉伸。

所以这有点像 Autolayout 中的内容压缩阻力优先级。但这里的默认值是0,所以你只需要将其设置为1即可获得所需效果,就像这样:
VStack(alignment: .leading) {
    CategoryRow(...)
    CategoryRow(...)
    Spacer()
}.layoutPriority(1)
VStack(alignment: .leading) {
    ...
}

希望它能正常工作!


1
似乎VStack中没有足够的空间来容纳所有的视图,因此它会压缩其中一些。您可以将其嵌入到ScrollView中。
NavigationView {
 ScrollView {
   VStack(alignment: .leading) {
      CategoryRow(...)
      CategoryRow(...)
      /// you images and so on
   }
  }
}

1
我确实这样做了,但是仍然出现同样的问题:使用ScrollView和List都会使视图被压缩。 - LinusGeffarth

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


-2

我认为你的最顶层视图(在NavigationView中)需要是一个列表,这样它才能滚动:

NavigationView {
            List {
                ...

或者使用ScrollView。

堆栈自动适应屏幕大小。如果您希望内容超出此范围,则可以在UIKit中使用ScrollView或TableView等。

编辑:

实际上,通过一些搜索,我找到了这个结果,看起来正是您要做的事情: https://developer.apple.com/tutorials/swiftui/composing-complex-interfaces


我确实这样做了,但是仍然出现同样的问题:使用ScrollView和List都会使视图被压缩。是的,我有来自那个教程的截图,但即使他们在那里展示了,他们也没有完全实现那个UI,而是有3个类别行。由于每个类别行都不是很高,它们不会到达视图底部,因此首先不会被压缩。 - LinusGeffarth
请看我编辑过的问题。我还试图完全删除最顶部的堆栈视图,以便CategoryRow直接位于List/ScrollView中 - 但没有成功。 - LinusGeffarth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接