可以使用下面的代码一致地重现该问题。 Xcode 13.3 + iOS 15.4(都是最新的)。
enum ListID: String, CaseIterable, Hashable, Identifiable {
case list1 = "List1"
case list2 = "List2"
var id: Self {
self
}
}
struct ContentView: View {
@State var listID: ListID = .list1
var body: some View {
VStack {
// 1) Picker
Picker(selection: $listID) {
ForEach(ListID.allCases) { id in
Text(id.rawValue)
}
} label: {
Text("Select a list")
}
.pickerStyle(.segmented)
// 2) List
switch listID {
case .list1:
createList(Array(1...2), id: .list1)
case .list2:
createList(Array(101...102), id: .list2)
}
}
}
@ViewBuilder func createList(_ itemValues: [Int], id: ListID) -> some View {
List {
ForEach(itemValues, id:\.self) { value in
Text("\(value)")
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
Button("Edit") {
// do nothing
}
.tint(.blue)
}
}
}
.id(id)
}
}
重现问题的步骤:
- 启动应用程序。见图1。
- 滑动列表 1 中的项目 1,保持“编辑”按钮不变(不要点击它)。见图2
- 然后在选择器中选择列表 2。您应该看到列表中的项目之前有一个额外的空格。此外,所有列表项都不再可滑动。见图3。
- 然后在选择器中选择列表 1。它有同样的问题。见图4。
该问题并非特定于选择器。例如,如果我们用一组按钮替换选择器,就可以重现它。我相信只有当旧列表在 SwiftUI 视图层次结构中被销毁时才会出现此问题。根据我对 SwiftUI 中结构化身份的理解,列表 1 和列表 2 被视为 SwiftUI 视图层次结构中的单独视图。因此尚不清楚它们如何相互影响。我能猜测的唯一原因是,虽然列表 1 和列表 2 被视为单独的虚拟视图,但 SwiftUI 实际上为它们使用相同的物理视图(例如,出于性能目的等)。所以对我来说这似乎是一个 SwiftUI 错误。
沿着这个思路思考,我可以通过不破坏列表来解决这个问题:
ZStack {
createList(Array(1...2), id: .list1)
.opacity(listID == .list1 ? 1 : 0)
createList(Array(101...102), id: .list2)
.opacity(listID == .list2 ? 1 : 0)
}
这在这个特定示例中完美运行,但不幸的是它不可扩展。例如,在我的日历应用程序中,当用户单击日历中的日期时,我想显示该日期的事件列表(我想对不同的日期使用不同的列表。我通过调用id()
为每个列表设置不同的 id)。在这种情况下,没有明显/优雅的方法来应用上述解决方案。
所以我想知道是否有人知道如何以更通用的方式解决这个问题?谢谢。