SwiftUI - 可调整大小的列表高度取决于元素数量
SwiftUI - Resizable List height that dependent on an element count
我在根据元素数量动态更改列表高度时遇到了一些麻烦。
我试过这个解决方案,但没有用。
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;DR
这不是 SwiftUI 的设计者希望您使用列表的方式。要么你必须想出一个将来可能会失败的 hacky 解决方案(见下文),要么使用列表以外的东西。
背景
SwiftUI 往往有两种类型的视图
- 这些设计易于修改和组合,为独特的外观和感觉提供无限的可定制性。
- 那些旨在为某种类型的交互提供标准、一致的感觉,无论它们在什么应用程序中使用。
类型 1 的示例是文本。可以更改字体大小、粗细、字体、颜色、背景、填充等,专为您修改而设计。
类型 2 的一个例子是列表。你不能直接控制行高,你不能改变视图周围的填充,你不能告诉它只显示这么多行等等。他们不希望它是非常可定制的,因为那样每个应用程序的列表的行为会有所不同,从而违背了标准控件的目的。
List 旨在用尽可能多的行填充整个父视图,即使它们是空的或仅部分显示在屏幕上(如果太多而无法一次显示,则滚动)。
你的问题
您遇到的问题是因为列表的大小不会以任何方式影响其行的大小。 SwiftUI 不关心是否有太多或太少的行以适合您的首选大小;它会根据内容愉快地调整其行的大小,即使这意味着它们不会全部显示或显示空行。
如果您确实需要根据父行的大小调整行的大小,则应使用 VStack。如果需要滚动,则需要将 VStack 包装在 ScrollView 中。
Hacky 解决方案
如果您仍然坚持使用列表,则必须执行以下操作:
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:
其他解决方案是根据rowCount*rowHeight
或其他GeometryReader -> geometry.size.height
在List上设置.frame(height: )
SwiftUi 进化了。这是 SwiftUI 3 的简单明了的答案:
我在根据元素数量动态更改列表高度时遇到了一些麻烦。
我试过这个解决方案,但没有用。
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;DR
这不是 SwiftUI 的设计者希望您使用列表的方式。要么你必须想出一个将来可能会失败的 hacky 解决方案(见下文),要么使用列表以外的东西。
背景
SwiftUI 往往有两种类型的视图
- 这些设计易于修改和组合,为独特的外观和感觉提供无限的可定制性。
- 那些旨在为某种类型的交互提供标准、一致的感觉,无论它们在什么应用程序中使用。
类型 1 的示例是文本。可以更改字体大小、粗细、字体、颜色、背景、填充等,专为您修改而设计。
类型 2 的一个例子是列表。你不能直接控制行高,你不能改变视图周围的填充,你不能告诉它只显示这么多行等等。他们不希望它是非常可定制的,因为那样每个应用程序的列表的行为会有所不同,从而违背了标准控件的目的。
List 旨在用尽可能多的行填充整个父视图,即使它们是空的或仅部分显示在屏幕上(如果太多而无法一次显示,则滚动)。
你的问题
您遇到的问题是因为列表的大小不会以任何方式影响其行的大小。 SwiftUI 不关心是否有太多或太少的行以适合您的首选大小;它会根据内容愉快地调整其行的大小,即使这意味着它们不会全部显示或显示空行。
如果您确实需要根据父行的大小调整行的大小,则应使用 VStack。如果需要滚动,则需要将 VStack 包装在 ScrollView 中。
Hacky 解决方案
如果您仍然坚持使用列表,则必须执行以下操作:
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:
其他解决方案是根据rowCount*rowHeight
或其他GeometryReader -> geometry.size.height
.frame(height: )
SwiftUi 进化了。这是 SwiftUI 3 的简单明了的答案: