所以我一直在尝试使用swiftUI
制作组件,该组件允许您在各节之间的List
中移动项目。
我准备了一个包含两个部分的示例:“第一列表”和“第二列表”。每当您点击一个项目时,它都会交换部分。这是屏幕截图:
当我点击“第一列表:1”时,它会正确移至第二部分:
但是,由于我在各节中为元素命名的方式(请参见下面的代码),其名称现在应更改为“第二列表:1”。所以这很奇怪。但它变得陌生:
当我现在在第二部分中点击“第一列表:1”时,会发生这种情况:
它不能正确换回。它只是被复制了,但是这次复制的名称实际上是正确的。
考虑下面的代码,我不知道这是怎么可能的。似乎swiftUI
以某种方式重用了该项目,即使它重新渲染了视图?似乎也重用了.onTapGesture
闭包,因为实际上从未调用过将项目放回第一部分的方法。
知道这里发生了什么吗?以下是该问题的完整示例:
import SwiftUI
import Combine
struct TestView: View {
@ObservedObject var viewModel: ViewModel
class ViewModel: ObservableObject {
let objectWillChange = PassthroughSubject<ViewModel,Never>()
public enum List {
case first
case second
}
public var first: [Int] = []
public var second: [Int] = []
public func swap(elementWithIdentifier identifier: Int, from list: List) {
switch list {
case .first:
self.first.removeAll(where: {$0 == identifier})
self.second.append(identifier)
case .second:
print("Called")
self.second.removeAll(where: {$0 == identifier})
self.first.append(identifier)
}
self.objectWillChange.send(self)
}
init(first: [Int]) {
self.first = first
}
}
var body: some View {
NavigationView {
List {
Section(header: Text("First List")) {
ForEach(self.viewModel.first, id: \.self) { id in
Text("First List: \(id)")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.onTapGesture {
self.viewModel.swap(elementWithIdentifier: id, from: .first)
}
}
}
Section(header: Text("First List")) {
ForEach(self.viewModel.second, id: \.self) { id in
Text("Second List: \(id)")
.onTapGesture {
self.viewModel.swap(elementWithIdentifier: id, from: .second)
}
}
}
}
.listStyle(GroupedListStyle())
.navigationBarTitle(Text("Testing"))
}.environment(\.editMode, .constant(EditMode.active))
}
}
struct TestView_Preview: PreviewProvider {
static var previews: some View {
TestView(viewModel: TestView.ViewModel(first: [1, 2, 3, 4, 5]))
}
}
答案 0 :(得分:0)
我解决此问题的唯一方法是通过向列表添加随机ID来防止列表差异。不过,这会删除动画,因此正在寻找更好的解决方案
id="notes"
删除这些部分也可以解决此问题,但这也不是有效的解决方案
答案 1 :(得分:0)
我发现自己处于类似情况,并且找到了解决该问题的更优雅的解决方法。我相信问题在于iOS13。在iOS14中,该问题不再存在。下面详细介绍了一个可在iOS13和iOS14上运行的简单解决方案。
尝试一下:
extension Int {
var id:UUID {
return UUID()
}
}
,然后在您的ForEach
参考\.id
或\.self.id
中而不是\.self
,即在您的两个部分中都这样:
ForEach(self.viewModel.first, id: \.id) { id in
Text("First List: \(id)")
.onTapGesture {
self.viewModel.swap(elementWithIdentifier: id, from: .first)
}
}
这会使事情正常。但是,摆弄时我确实发现了以下问题:
.listStyle(GroupedListStyle())
动画看起来很奇怪。删除它,动画效果会好很多。再一次,这是一种解决方法,但我认为Apple仍在解决SwiftUI中的问题。
更新
PS,如果您在iOS14中使用任何onDelete
或onMove
修饰符,则会将动画添加到列表中,从而导致异常行为。我发现使用\.self
适用于iOS14,\.self.id
适用于iOS13。代码不是很漂亮,因为您很可能会在代码中插入#available(iOS 14.0, *)
个代码。但这有效。
答案 2 :(得分:0)
我不知道为什么,但似乎您的 swap 方法在您添加的第一个对象上做了一些奇怪的事情,因为如果第二个有效,那么您可能丢失了一些实例。
顺便说一句,每次在每个列表中添加一个新对象时,是否都需要 removeAll?
opacity
也许你的问题出在这个函数上,一切看起来都很棒。