考虑以下代码示例(您可以下载 here ):
struct Item: Identifiable {
var id = UUID()
var name: String
}
struct Row: View {
var item: Item
static var counter = 0
init(item: Item) {
self.item = item
Row.counter += 1
print(Row.counter)
}
var body: some View {
Text(item.name)
}
}
struct ContentView: View {
@State var items = (1...1000).map { Item(name: "Item \($0)") }
var body: some View {
List {
ForEach(items) {
Row(item: $0)
.swipeActions(edge: .leading) {
Button("Action", action: {})
}
}
}
}
}
运行此代码会打印出从 1
到 21
的数字,大约是屏幕上可见的行数。
现在,如果我将 ForEach
语句包装在 Section
中,则打印出数字 1
到 1000
.因此,没有单元格重用,所有行都被一次加载。
Section {
ForEach(items) {
Row(item: $0)
.swipeActions(edge: .leading) {
Button("Action", action: {})
}
}
}
如果我删除滑动操作,则会打印出数字 1
到 18
。
Section {
ForEach(items) {
Row(item: $0)
}
}
这是已知问题还是我做错了什么?
最佳答案
即使没有滑动操作,我也遇到了这个问题。我使用 .insetGroup
样式为列表创建了一个新的 SectionView,直到 Apple 修复它。缺点是部分页眉和页脚与原始 View 不同。
它已经过测试并适用于 iOS 15 和 16。
struct LazySection<Element, Row: View>: View where Element: Equatable, Element: Identifiable {
// source: https://medium.com/devtechie/round-specific-corners-in-swiftui-d23ceee08188
struct RoundedCorner: Shape {
var radius: CGFloat = .infinity
var corners: UIRectCorner = .allCorners
func path(in rect: CGRect) -> Path {
let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
return Path(path.cgPath)
}
}
let elements: [Element]
var header: String?
var footer: String?
@ViewBuilder let row: (_ element: Element) -> Row
var body: some View {
if let header = header {
HeaderFooter(header, isHeader: true)
.textCase(.uppercase)
}
if let first = elements.first,
let last = elements.last {
if first == last {
row(first)
.listRowBackground(
Rectangle()
.foregroundColor(Color(uiColor: .secondarySystemGroupedBackground))
.clipShape(RoundedCorner(radius: 10))
)
} else {
row(first)
.listRowBackground(
Rectangle()
.foregroundColor(Color(uiColor: .secondarySystemGroupedBackground))
.clipShape(RoundedCorner(radius: 10, corners: [.topLeft, .topRight]))
)
ForEach(elements.dropFirst().dropLast()) { element in
row(element)
}
row(last)
.listRowBackground(
Rectangle()
.foregroundColor(Color(uiColor: .secondarySystemGroupedBackground))
.clipShape(RoundedCorner(radius: 10, corners: [.bottomLeft, .bottomRight]))
)
}
}
if let footer = footer {
HeaderFooter(footer, isHeader: false)
}
}
func HeaderFooter(_ title: String, isHeader: Bool) -> some View {
VStack(alignment: .leading) {
if isHeader == true {
Spacer()
}
Text(title)
.font(.footnote)
if isHeader == false {
Spacer()
}
}
.foregroundColor(.init(uiColor: .secondaryLabel))
.listRowBackground(Color(uiColor: .systemGroupedBackground))
.listRowSeparator(.hidden)
}
}
在给定的代码示例中使用它 Item 必须符合 Equatable。
struct Item: Identifiable, Equatable {
var id = UUID()
var name: String
}
struct Row: View {
var item: Item
static var counter = 0
init(item: Item) {
self.item = item
Row.counter += 1
print(Row.counter)
}
var body: some View {
Text(item.name)
}
}
struct ContentView: View {
@State var items = (1...1000).map { Item(name: "Item \($0)") }
var body: some View {
List {
LazySection(elements: items) { element in
Row(item: element)
}
}
}
}
https://stackoverflow.com/questions/71806868/