当我检测到onAppear上的列表包含一个过时的记录时,我正在以编程方式从SwiftUI列表数据源中删除一条记录。当用户离开列表屏幕时,该记录已更新。
例如,用户位于项目编辑屏幕上。该屏幕在环境对象中标记了一个有关记录更新的属性,以便“列表”屏幕可以使用该属性在“列表”再次出现时从“列表”数据源中删除陈旧记录。希望这种刷新列表的方式还可以吗?
List{
Section{
Toggle(isOn: self.$showArchivedItems.onChange(showOptionsChanged)) {
Text("Show Archived Records).font(.headline)
}
}
Section {
ForEach(userData.items) { dataItem in
NavigationLink(destination: ItemDetailView(item: dataItem)){
ListRowItemView(item: dataItem)
}
}.onDelete(perform: self.onDeletingItems)
.onAppear() {
if let updatedRecord = self.userData.lastUpdatedRecord {
if let updatedRecordId = self.userData.items.firstIndex(where: {return $0.id == updatedRecord.id}) {
//OPTION #1 - Doesn’t work
//self.userData.items.remove(at: updatedRecordId)
//self.userData.items.append(updatedRecord)
//OPTION #2 - Doesn’t work
//self.userData.items.replaceSubrange(updatedRecordId... updatedRecordId, with: [updatedRecord])
//OPTION #3 - works
self.userData.items = self.storage.load()
}
}
}
}
}.listStyle(GroupedListStyle())
private func onDeletingItems(indexset: IndexSet) {
var itemsToDeactivate: [ItemModel] = []
for index in indexset {
userData.items[index].isArchived = true
itemsToDeactivate.append(userData.items[index])
}
for item in itemsToDeactivate {
storage.persist(dataItem: item)
}
userData.items.remove(atOffsets: indexset)
}
附加代码:
UserData.swift
import Foundation
final class UserData: ObservableObject {
@Published var items: [ItemModel]
var lastUpdatedRecord: ItemModel? = nil
init(data: [ItemModel] = []) {
self.items = data
}
func clearLastUpdatedRecord() {
self.lastUpdatedRecord = nil
}
}
列表视图
struct ListView: View {
@EnvironmentObject var userData: UserData
var body: some View {
NavigationView {
VStack{
List{
//ForEach on userData.items
}
}
}
}
SceneDelegate.swift
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
// Create the SwiftUI view that provides the window contents.
let storage = ItemLocalStorage()
let userData = UserData(data: storage.load())
let userSettings = SettingsModel()
let contentView = ContentView().environmentObject(storage).environmentObject(userData).environmentObject(userSettings)
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
问题是,我尝试了两种选择,专门是通过在List数据源中查找过时记录的索引(在OPTION#1中)并追加更新的实例,或者在OPTION#2中追加过时的实例,尝试将过时的记录替换为update实例。
在两个选项中,更新的记录的确添加到列表中,但过时的记录不会从列表中删除,尽管陈旧的记录具有更新的数据,但我看到的是更新记录的双重实例。
当我从存储中完全刷新数据源(OPTION#3)时,列表将显示正确的项目,即仅更新记录的一个实例。
任何人都可以建议如何以编程方式从List数据源中删除记录,以便List也刷新其视图。我的意思是我不必从存储中再次加载所有记录来更新列表,因为单个记录已更新。 (在我的应用程序中,更新是用户触发的,根据当前设计,一次只能更新一条记录)
我还想知道,List是否支持从List编辑项目,就像我在List上实现onDelete一样。用户可以在此处将List置于editMode并删除单个项目。我确定List不会调用我的存储来提取所有数据。那么如何管理列表以删除单个项目并刷新其显示?
答案 0 :(得分:0)
我通过捕获List的数据源作为显式状态变量设法找到了解决该问题的方法。如果我现在对此状态变量进行更改,即删除并添加行,则列表显示将反映更新的记录。
假设是,当基础值发生变化时,SwiftUI会比@State更喜欢@State来自动呈现UI。因此,我没有处理在EnvironmentObject中保存的Array副本,而是将工作传递给专用的@State变量。在此之后,我得到了想要的行为。
我的关键更改是
ListView.swift
@State private var items: [ItemModel] = []
init(items: [ItemModel]) {
_items = State(initialValue: items)
}
List{
Section{
Toggle(isOn: self.$showArchivedItems.onChange(showOptionsChanged)) {
Text("Show Archived”).font(.headline)
}
}
Section {
ForEach(self.items) { item in
NavigationLink(destination: ItemDetailView(item: item)){
ListRowItemView(item: item)
}
}.onDelete(perform: self.onDeletingItems)
.onAppear() {
if let updatedRow = self.userData.lastUpdatedRecord {
self.updateListDataSource(updatedRow)
}
}
}
}.listStyle(GroupedListStyle())
fileprivate func updateListDataSource(_ updatedRow: ItemModel) {
if let updatedRecordId = self.items.firstIndex(where: {return $0.id == updatedRow.id}) {
self.items.remove(at: updatedRecordId)
self.items.append(updatedRow)
self.userData.clearLastUpdatedRecord()
}
}
private func onDeletingItems(indexset: IndexSet) {
var itemsToDeactivate: [ItemModel] = []
for index in indexset {
userData.items[index].isArchived = true
userData.items[index].lastModified = Date()
itemsToDeactivate.append(userData.items[index])
}
for item in itemsToDeactivate {
storage.persist(item: item)
self.userData.items.removeAll { $0.id == item.id}
}
self.items.remove(atOffsets: indexset)
}
UserData.swift
import Foundation
final class UserData: ObservableObject {
@Published var items: [ItemModel]
var lastUpdatedRecord: ItemModel? = nil {
didSet {
if let updatedInstance = lastUpdatedRecord {
if let updatedInstanceIndex = self.items.firstIndex(where: {$0.id == updatedInstance.id}) {
self.items.remove(at: updatedInstanceIndex)
self.items.append(updatedInstance)
}
}
}
}
init(data: [ItemModel] = []) {
self.items = data
}
func clearLastUpdatedRecord() {
self.lastUpdatedRecord = nil
}
}