作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试使用 SwiftUI 重新创建基本的 Collection View 行为:
我有许多水平相邻显示的 View (例如照片)。当没有足够的空间在同一行上显示所有照片时,剩余的照片应换行到下一行。
这是一个例子:
看起来可以将一个 VStack
与多个 HStack
元素一起使用,每个元素包含一行照片。
我尝试使用 GeometryReader
并迭代照片 View 来动态创建这样的布局,但它无法编译(包含声明的闭包不能与函数构建器“ViewBuilder”一起使用)。是否可以动态创建 View 并返回它们?
澄清:
盒子/照片可以具有不同的宽度(与经典的“网格”不同)。棘手的部分是,我必须知道当前框的宽度,以便决定它是否适合当前行,或者是否必须开始新行。
最佳答案
以下是我使用 PreferenceKeys
解决此问题的方法。
public struct MultilineHStack: View {
struct SizePreferenceKey: PreferenceKey {
typealias Value = [CGSize]
static var defaultValue: Value = []
static func reduce(value: inout Value, nextValue: () -> Value) {
value.append(contentsOf: nextValue())
}
}
private let items: [AnyView]
@State private var sizes: [CGSize] = []
public init<Data: RandomAccessCollection, Content: View>(_ data: Data, @ViewBuilder content: (Data.Element) -> Content) {
self.items = data.map { AnyView(content($0)) }
}
public var body: some View {
GeometryReader {geometry in
ZStack(alignment: .topLeading) {
ForEach(0..<self.items.count) { index in
self.items[index].background(self.backgroundView()).offset(self.getOffset(at: index, geometry: geometry))
}
}
}.onPreferenceChange(SizePreferenceKey.self) {
self.sizes = $0
}
}
private func getOffset(at index: Int, geometry: GeometryProxy) -> CGSize {
guard index < sizes.endIndex else {return .zero}
let frame = sizes[index]
var (x,y,maxHeight) = sizes[..<index].reduce((CGFloat.zero,CGFloat.zero,CGFloat.zero)) {
var (x,y,maxHeight) = $0
x += $1.width
if x > geometry.size.width {
x = $1.width
y += maxHeight
maxHeight = 0
}
maxHeight = max(maxHeight, $1.height)
return (x,y,maxHeight)
}
if x + frame.width > geometry.size.width {
x = 0
y += maxHeight
}
return .init(width: x, height: y)
}
private func backgroundView() -> some View {
GeometryReader { geometry in
Rectangle()
.fill(Color.clear)
.preference(
key: SizePreferenceKey.self,
value: [geometry.frame(in: CoordinateSpace.global).size]
)
}
}
}
你可以像这样使用它:
struct ContentView: View {
let texts = ["a","lot","of","texts"]
var body: some View {
MultilineHStack(self.texts) {
Text($0)
}
}
}
它不仅适用于文本
,还适用于任何 View 。
关于SwiftUI:如何让 HStack 将子项沿多行包装(如 Collection View )?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57510093/
我是一名优秀的程序员,十分优秀!