SwiftUI 从另一个视图重新排序列表动态部分

问题描述 投票:0回答:2

我有一个简单的

List
,其部分存储在
ObservableObject
内。我想从另一个视图重新排序它们。

这是我的代码:

class ViewModel: ObservableObject {
    @Published var sections = ["S1", "S2", "S3", "S4"]
    
    func move(from source: IndexSet, to destination: Int) {
        sections.move(fromOffsets: source, toOffset: destination)
    }
}
struct ContentView: View {
    @ObservedObject var viewModel = ViewModel()
    @State var showOrderingView = false

    var body: some View {
        VStack {
            Button("Reorder sections") {
                self.showOrderingView = true
            }
            list
        }
        .sheet(isPresented: $showOrderingView) {
            OrderingView(viewModel: self.viewModel)
        }
    }

    var list: some View {
        List {
            ForEach(viewModel.sections, id: \.self) { section in
                Section(header: Text(section)) {
                    ForEach(0 ..< 3, id: \.self) { _ in
                        Text("Item")
                    }
                }
            }
        }
    }
}
struct OrderingView: View {
    @ObservedObject var viewModel: ViewModel

    var body: some View {
        NavigationView {
            List {
                ForEach(viewModel.sections, id: \.self) { section in
                    Text(section)
                }
                .onMove(perform: viewModel.move)
            }
            .navigationBarItems(trailing: EditButton())
        }
    }
}

但是在

OrderingView
中,当尝试移动部分时,我收到此错误:“尝试为单元格创建两个动画”。可能是因为各部分的顺序发生了变化。

如何更改各部分的顺序?

swift swiftui swiftui-list
2个回答
2
投票

此场景的问题已多次重现

ViewModel
,因此在工作表中所做的修改丢失了。 (奇怪的是,在带有 StateObject 的 SwiftUI 2.0 中,这些更改也丢失了,并且 EditButton 根本不起作用。)

无论如何。看起来这是一个找到的解决方法。这个想法是打破面试依赖性(绑定)并使用纯数据将它们显式传递到工作表中并从其中显式返回它们。

使用 Xcode 12 / iOS 14 进行测试和工作,但我试图避免使用 SwiftUI 2.0 功能。

class ViewModel: ObservableObject {
    @Published var sections = ["S1", "S2", "S3", "S4"]

    func move(from source: IndexSet, to destination: Int) {
        sections.move(fromOffsets: source, toOffset: destination)
    }
}

struct ContentView: View {
    @ObservedObject var viewModel = ViewModel()
    @State var showOrderingView = false

    var body: some View {
        VStack {
            Button("Reorder sections") {
                self.showOrderingView = true
            }

            list
        }
        .sheet(isPresented: $showOrderingView) {
            OrderingView(sections: viewModel.sections) {
                self.viewModel.sections = $0
            }
        }
    }

    var list: some View {
        List {
            ForEach(viewModel.sections, id: \.self) { section in
                Section(header: Text(section)) {
                    ForEach(0 ..< 3, id: \.self) { _ in
                        Text("Item")
                    }
                }
            }
        }
    }
}

struct OrderingView: View {
    @State private var sections: [String]
    let callback: ([String]) -> ()

    init(sections: [String], callback: @escaping ([String]) -> ())
    {
        self._sections = State(initialValue: sections)
        self.callback = callback
    }

    var body: some View {
        NavigationView {
            List {
                ForEach(sections, id: \.self) { section in
                    Text(section)
                }
                .onMove {
                    self.sections.move(fromOffsets: $0, toOffset: $1)
                }
            }
            .navigationBarItems(trailing: EditButton())
        }
        .onDisappear {
            self.callback(self.sections)
        }
    }
}

0
投票

SwiftUI 1.0 的可能解决方案

我找到了一种解决方法,通过添加

List
来禁用
.id(UUID())
的动画:

var list: some View {
    List {
        ...
    }
    .id(UUID())
}

然而,这会弄乱使用

NavigationLink(destination:tag:selection:)
创建的 NavigationLink 的过渡动画:在 SwiftUI 中呈现 NavigationLink 时,过渡动画消失了。

所有其他动画(如

onDelete

)也丢失了。

更hacky的解决方案是有条件地禁用列表动画:

class ViewModel: ObservableObject { ... @Published var isReorderingSections = false ... }
struct OrderingView: View {
    @ObservedObject var viewModel: ViewModel

    var body: some View {
        NavigationView {
            ...
        }
        .onAppear {
            self.viewModel.isReorderingSections = true
        }
        .onDisappear {
            self.viewModel.isReorderingSections = false
        }
    }
}
struct ContentView: View {
    ...
    var list: some View {
        List {
            ...
        }
        .id(viewModel.isReorderingSections ? UUID().hashValue : 1)
    }
}
    
© www.soinside.com 2019 - 2024. All rights reserved.