我正在使用
UIHostingConfiguration
将 SwiftUI 视图嵌入到 UICollectionView
中,如下所示:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
cell.contentConfiguration = UIHostingConfiguration {
CellView()
}
@State var
或其子视图中的任何CellView
声明都将被初始化为基本上随机的值。这似乎完全打破了 SwiftUI 中 @State
的工作方式。我已经能够在这个非常简单的示例项目中重现它。 这里是说明问题的视频。每个单元格的计数应初始化为 0,但增加任何单元格中的计数会导致其他单元格在分配时显示非零计数。
我认为发生这种情况是因为当集合视图使可重用单元出列时,它正在内存复制另一个单元的视图,并且
@State var
保存前一个单元的值。这就是集合视图单元格在 UIKit 中的工作方式,但是 UICollectionViewCell
有一个 prepareForReuse
函数,该函数应该清理并重新初始化单元格状态,但我无法在 SwiftUI 中完善类似的机制。
每当集合视图使单元格出队时,如何在我的视图中重新初始化
@State var
,而不是在其他时候不必要地重新初始化(例如调用更频繁的View.init
)?
UICollectionViewCell
是无状态的。 @State
不起作用是很自然的。
回想一下通常如何在 UIKit 中设置集合视图单元格 - 您可以根据
indexPath
参数使单元格出队,并将该单元格的某些属性设置为模型中的某些内容(通常是事物的集合)。
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
// as an example
cell.backgroundColor = someColors[indexPath.item]
return cell
}
您在这里缺少的是
someColors
。请注意,这不是集合视图单元格的一部分,而是其他地方,例如作为视图控制器类中的属性。换句话说,您可以在 cellForItemAt
中将所有状态分配给单元格,以供其显示。单元本身不存储该状态。
您需要在这里做类似的事情。假设您的单元格显示以下实例:
@Observable
class Item {
var number = 0
}
你的单元格不能有状态 - 它的初始化程序应该采用
Item
作为参数:
struct CellView: View {
let item: Item
var body: some View {
VStack {
Text("Count \(item.number)")
Button {
item.number += 1
} label: {
Text("Increase count")
}
}
}
}
在代码中的某个位置,您需要存储实际的项目,例如
var items = (0..<100).map { _ in Item() }
这需要传递到
cellForItemAt
可以轻松访问的地方。那么你需要做的就是:
cell.contentConfiguration = UIHostingConfiguration {
CellView(item: items[indexPath.item])
}
我选择使用
class
作为示例,因为它很容易传递。如果 Item
是 struct
的话会更难。我想用 struct
仍然是可能的,但那是另一个故事了。