SwiftUI-可调整大小的列表高度,取决于元素数量

Abj*_*jox 0 swiftui swiftui-list

根据元素数量动态更改列表高度时遇到一些麻烦。

我尝试了此解决方案,但没有成功。

List {
    ForEach(searchService.searchResult, id: \.self) { item in
        Text(item)
        .font(.custom("Avenir Next Regular", size: 12))
    }
}.frame(height: CGFloat(searchService.searchResult.count * 20))
Run Code Online (Sandbox Code Playgroud)

Moj*_*ini 11

自身尺寸List

如果你想一次List显示所有内容,这意味着你不需要回收功能(列表的关键功能),所以你只需要不使用List! 相反,您可以ForEach直接使用,然后它会根据其内容自行调整大小:

ForEach(searchService.searchResult, id: \.self) { item in
    VStack(alignment: .leading) {
        Text(item).font(.custom("Avenir Next Regular", size: 12))
        Divider()
    }.padding(.horizontal, 8)
}
Run Code Online (Sandbox Code Playgroud)

您可以根据需要更改所有尺寸和间距

请注意,您可以LazyVStackiOS 14使用它来使其延迟加载并提高其性能。

  • 我的天啊。谢谢你!列表给我带来了无尽的麻烦 - 如果您在子视图中使用“列表”,那么预计您的高度会缩小到零。阅读大量尝试使用 GeometryReader 获取子内容大小的 hack,但无法使其正常工作。从 List 切换到 ForEach 立即为我解决了这个问题。谢谢。 (3认同)
  • 请注意,这个答案丢失了 List 的一个重要部分,即它不会立即渲染所有内容。如果您想在包含数百个项目的列表上执行此操作,则会遇到滚动性能问题。 (2认同)
  • 我认为OP想要一些缩小到阈值以下并滚动到阈值以上的东西。我正在寻找类似的东西,这样,您只需使用 .frame(maxHeight:xxx) ,其中 xxx 由(屏幕或父视图高度的 % )或( elements.height * ThresholdElementCount )中的较小者计算得出。 (2认同)

Mic*_*bro 8

从 iOS 14 开始,您可以LazyVStack使用List. 列表似乎跨越整个父视图高度,与行高或计数无关。

LazyVStack {
    ForEach(searchService.searchResult, id: \.self) { item in
        Text(item)
        .font(.custom("Avenir Next Regular", size: 12))
    }
}.frame(height: 
Run Code Online (Sandbox Code Playgroud)

其他解决方案是.frame(height: )根据rowCount*rowHeight或其他设置列表GeometryReader -> geometry.size.height


Joh*_* M. 5

TL; DR

这不是SwiftUI的设计者希望您使用列表的方式。您将不得不提出一种可能会在将来中断的hacky解决方案(请参阅下文),或者使用列表以外的方法。

背景

SwiftUI倾向于具有两种视图

  1. 旨在易于修改和组合的设计,提供无限的可定制性,以提供独特的外观。
  2. 这些设计旨在为某种类型的交互提供标准,一致的感觉,而不管它们使用的是什么应用。

类型1的示例是文本。您可以更改字体大小,粗细,字体,颜色,背景,填充等。它是供您修改的。

类型2的一个示例是列表。您不能直接控制行高,不能更改视图周围的填充,不能告诉它仅显示这么多行,等等。他们不希望它具有很高的可定制性,因为那时每个应用程序的列表的行为会有所不同,从而违反了标准控件的目的。

列表旨在用尽可能多的行填充整个父视图,即使它们为空或仅部分显示在屏幕上(如果一次显示太多,也可以滚动)。

你的问题

之所以出现问题,是因为List的大小不会以任何方式影响其行的大小。SwiftUI不在乎是否有太多或太少的行适合您的首选大小;它会根据内容愉快地调整行的大小,即使这意味着它们没有全部显示或显示的是空行。

如果您确实需要根据行的大小调整行的大小,则应使用VStack。如果需要滚动,则需要将VStack包装在ScrollView中。

哈克解决方案

如果您仍然坚持使用列表,则必须执行以下操作:

struct ContentView: View {

    @State private var textHeight: Double = 20
    let listRowPadding: Double = 5 // This is a guess
    let listRowMinHeight: Double = 45 // This is a guess
    var listRowHeight: Double {
        max(listRowMinHeight, textHeight + 2 * listRowPadding)
    }

    var strings: [String] = ["One", "Two", "Three"]

    var body: some View {
        VStack {
            HStack {
                Text(String(format: "%2.0f", textHeight as Double))
                Slider(value: $textHeight, in: 20...60)
            }
                VStack(spacing: 0) {
                    Color.red
                    List {
                        ForEach(strings, id: \.self) { item in
                            Text(item)
                                .font(.custom("Avenir Next Regular", size: 12))
                                .frame(height: CGFloat(self.textHeight))
                                .background(Color(white: 0.5))
                        }
                    }
                    // Comment out the following line to see how List is expected to work
                    .frame(height: CGFloat(strings.count) * CGFloat(self.listRowHeight))
                    Color.red
            }.layoutPriority(1)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

滑块在那里显示列表行的高度如何随其子视图的高度而变化。您将必须手动选择listRowPaddinglistRowMinHeight获得最符合您期望的外观。如果Apple曾经更改过List的外观(更改了填充,最小行高等),则必须记住要手动重新调整这些值。