我在动态更改取决于元素计数的列表高度时遇到一些麻烦。
我尝试了这个解决方案,但没有成功。
List {
ForEach(searchService.searchResult, id: \.self) { item in
Text(item)
.font(.custom("Avenir Next Regular", size: 12))
}
}.frame(height: CGFloat(searchService.searchResult.count * 20))
TL;博士
这不是 SwiftUI 的设计者希望你使用列表的方式。要么你必须想出一个将来可能会崩溃的黑客解决方案(见下文),要么使用列表以外的东西。
背景
SwiftUI 往往有两种类型的视图
类型 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)
}
}
}
滑块用于显示列表行高如何随子视图的高度而变化。您必须手动选择
listRowPadding
和 listRowMinHeight
才能获得最符合您期望的外观。如果 Apple 更改了列表的外观(更改填充、最小行高等),您必须记住返回并手动调整这些值。
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)
}
您可以根据您的需要更改所有尺寸和间距
请注意您可以使用
iOS 14中的
LazyVStack
使其延迟加载并提高其性能。
从 iOS 14 开始,您可以使用
LazyVStack
代替 List
。
列表似乎跨越整个父视图高度,与行高或计数无关。
LazyVStack {
ForEach(searchService.searchResult, id: \.self) { item in
Text(item)
.font(.custom("Avenir Next Regular", size: 12))
}
}.frame(height:
其他解决方案是基于
.frame(height: )
或其他 rowCount*rowHeight
在列表上设置
GeometryReader -> geometry.size.height
可以使用 SwiftUIViewInspector 开源框架,您可以将其添加为 Swift 包,然后添加到构建阶段、目标和链接阶段。那么它就是这样的:
import Inspect
...
@State var listHeight: CGFloat = 100 // just any default height
...
List {
...
}
.frame(height: listHeight)
.inspect { list in
switch list {
case .collectionView(let collectionView):
self.listHeight = collectionView.contentSize.height
case .tableView((let tableView)):
self.listHeight = tableView.contentSize.height
}
}
首先,列表以默认高度显示,它在内部计算可滚动大小,其中包括所有标题和行,并将其存储在其
contentSize
属性中。因此,我们只需将 listHeight
状态设置为此值,然后调整列表大小以显示所有内容。
这带有明显的警告,仅将其用于小表,因为本质上它会禁用行重用。或者如果它正在进行一些离屏优化,则可能不会。
SwiftUi 已经发展。这是 SwiftUI 3 的简单明了的答案:https://stackoverflow.com/a/65769005/4514671