SwiftUI ScrollView 不计算自定义 StaggeredGrid 高度
SwiftUI ScrollView doesn't compute custom StaggeredGrid height
我正在尝试在 SwiftUI 中创建一个新的网格结构来显示高度可变的卡片。就此而言,我在 HStack
中使用了多个 LazyVStack
,并且有条件以正确的顺序显示我的数据项。此视图单独工作,调整列数以适应屏幕大小,但在 ScrollView
中使用它时,其大小未正确计算,并且以下视图最终位于网格下方而不是网格下方。这是我用于网格的代码:
struct StaggeredGrid<Element, Content>: View where Content: View {
var preferredItemWidth: CGFloat
var data: [Element] = []
var content: (Element) -> Content
init(preferredItemWidth: CGFloat, data: [Element], @ViewBuilder content: @escaping (Element) -> Content) {
self.preferredItemWidth = preferredItemWidth
self.data = data
self.content = content
}
var body: some View {
GeometryReader { geometry in
HStack(alignment: .top, spacing: 20) {
ForEach(1...Int(geometry.size.width / preferredItemWidth), id: \.self) { number in
LazyVStack(spacing: 20) {
ForEach(0..<data.count, id: \.self) { index in
if (index+1) % Int(geometry.size.width / preferredItemWidth) == number % Int(geometry.size.width / preferredItemWidth) {
content(data[index])
}
}
}
}
}
.padding([.horizontal])
}
}
}
以及显示行为的预览:
struct StaggeredGrid_Previews: PreviewProvider {
static var previews: some View {
ScrollView {
VStack {
StaggeredGrid(preferredItemWidth: 160, data: (1...10).map{"Item \([=11=])"}) { item in
Text(item)
.foregroundColor(.blue)
}
Text("I should be below the grid")
}
}
}
}
预览图如下:
Wrong appearance in a ScrollView
以及ScrollView
被注释掉时的一张图:
Expected behavior, ScrollView removed
提前感谢您对我不理解的这种行为的任何帮助或线索。
我引用 @Asperi :
“ScrollView 没有自己的大小,GeometryReader 也没有自己的大小,所以你陷入了先有鸡还是先有蛋的问题,即没有人知道要渲染的大小,所以崩溃了。你必须有明确的项目框架大小在 ScrollView 里面。"
此处您必须设置 StaggeredGrid
或其包含的 GeometryReader
的高度。
在您的情况下,它的高度当然取决于其内容的高度(即 HStack)。您可以使用 Reader 读取此高度(例如其背景/覆盖层的高度)。并用它来确定你的 GeometryReader
的框架大小
例如:
struct StaggeredGrid<Element, Content>: View where Content: View {
var preferredItemWidth: CGFloat
var data: [Element] = []
var content: (Element) -> Content
init(preferredItemWidth: CGFloat, data: [Element], @ViewBuilder content: @escaping (Element) -> Content) {
self.preferredItemWidth = preferredItemWidth
self.data = data
self.content = content
}
@State private var gridHeight: CGFloat = 100
var body: some View {
GeometryReader { geometry in
HStack(alignment: .top, spacing: 20) {
ForEach(1...Int(geometry.size.width / preferredItemWidth), id: \.self) { number in
LazyVStack(spacing: 20) {
ForEach(0..<data.count, id: \.self) { index in
if (index+1) % Int(geometry.size.width / preferredItemWidth) == number % Int(geometry.size.width / preferredItemWidth) {
content(data[index])
}
}
}
}
}
.overlay(GeometryReader { proxy in
Color.clear.preference(
key: HeightPreferenceKey.self,
value: proxy.size.height
)
})
.onPreferenceChange(HeightPreferenceKey.self) {
gridHeight = [=10=]
}
.padding([.horizontal])
}
.frame(height: gridHeight)
}
}
private struct HeightPreferenceKey: PreferenceKey {
static let defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat,
nextValue: () -> CGFloat) {
value = nextValue()
}
}
我正在尝试在 SwiftUI 中创建一个新的网格结构来显示高度可变的卡片。就此而言,我在 HStack
中使用了多个 LazyVStack
,并且有条件以正确的顺序显示我的数据项。此视图单独工作,调整列数以适应屏幕大小,但在 ScrollView
中使用它时,其大小未正确计算,并且以下视图最终位于网格下方而不是网格下方。这是我用于网格的代码:
struct StaggeredGrid<Element, Content>: View where Content: View {
var preferredItemWidth: CGFloat
var data: [Element] = []
var content: (Element) -> Content
init(preferredItemWidth: CGFloat, data: [Element], @ViewBuilder content: @escaping (Element) -> Content) {
self.preferredItemWidth = preferredItemWidth
self.data = data
self.content = content
}
var body: some View {
GeometryReader { geometry in
HStack(alignment: .top, spacing: 20) {
ForEach(1...Int(geometry.size.width / preferredItemWidth), id: \.self) { number in
LazyVStack(spacing: 20) {
ForEach(0..<data.count, id: \.self) { index in
if (index+1) % Int(geometry.size.width / preferredItemWidth) == number % Int(geometry.size.width / preferredItemWidth) {
content(data[index])
}
}
}
}
}
.padding([.horizontal])
}
}
}
以及显示行为的预览:
struct StaggeredGrid_Previews: PreviewProvider {
static var previews: some View {
ScrollView {
VStack {
StaggeredGrid(preferredItemWidth: 160, data: (1...10).map{"Item \([=11=])"}) { item in
Text(item)
.foregroundColor(.blue)
}
Text("I should be below the grid")
}
}
}
}
预览图如下: Wrong appearance in a ScrollView
以及ScrollView
被注释掉时的一张图:
Expected behavior, ScrollView removed
提前感谢您对我不理解的这种行为的任何帮助或线索。
我引用 @Asperi : “ScrollView 没有自己的大小,GeometryReader 也没有自己的大小,所以你陷入了先有鸡还是先有蛋的问题,即没有人知道要渲染的大小,所以崩溃了。你必须有明确的项目框架大小在 ScrollView 里面。"
此处您必须设置 StaggeredGrid
或其包含的 GeometryReader
的高度。
在您的情况下,它的高度当然取决于其内容的高度(即 HStack)。您可以使用 Reader 读取此高度(例如其背景/覆盖层的高度)。并用它来确定你的 GeometryReader
例如:
struct StaggeredGrid<Element, Content>: View where Content: View {
var preferredItemWidth: CGFloat
var data: [Element] = []
var content: (Element) -> Content
init(preferredItemWidth: CGFloat, data: [Element], @ViewBuilder content: @escaping (Element) -> Content) {
self.preferredItemWidth = preferredItemWidth
self.data = data
self.content = content
}
@State private var gridHeight: CGFloat = 100
var body: some View {
GeometryReader { geometry in
HStack(alignment: .top, spacing: 20) {
ForEach(1...Int(geometry.size.width / preferredItemWidth), id: \.self) { number in
LazyVStack(spacing: 20) {
ForEach(0..<data.count, id: \.self) { index in
if (index+1) % Int(geometry.size.width / preferredItemWidth) == number % Int(geometry.size.width / preferredItemWidth) {
content(data[index])
}
}
}
}
}
.overlay(GeometryReader { proxy in
Color.clear.preference(
key: HeightPreferenceKey.self,
value: proxy.size.height
)
})
.onPreferenceChange(HeightPreferenceKey.self) {
gridHeight = [=10=]
}
.padding([.horizontal])
}
.frame(height: gridHeight)
}
}
private struct HeightPreferenceKey: PreferenceKey {
static let defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat,
nextValue: () -> CGFloat) {
value = nextValue()
}
}