SwiftUI 列表在删除后继续显示核心数据项
Posted
技术标签:
【中文标题】SwiftUI 列表在删除后继续显示核心数据项【英文标题】:SwiftUI List Keeps Showing Core Data Items After They Were Deleted 【发布时间】:2021-06-19 10:37:49 【问题描述】:我正在探索 SwiftUI,但遇到了一个问题。我有一个使用 Core Data 保存的歌手列表
let singer1 = Singer(context: viewContext)
singer1.firstName = "Taylor"
singer1.lastName = "Swift"
let singer2 = Singer(context: viewContext)
singer2.firstName = "Ed"
singer2.lastName = "Sheeran"
let singer3 = Singer(context: viewContext)
singer3.firstName = "Adele"
singer3.lastName = "Adkins"
try? viewContext.save()
我创建了另一个视图来填充歌手列表。
struct ListOfSingers<T: NSManagedObject, Content: View>: View
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest<T> var singers: FetchedResults<T>
let content: (T) -> Content
init(filterKey: String, filterValue: String, @ViewBuilder content: @escaping (T) -> Content)
_singers = FetchRequest<T>(entity: T.entity(), sortDescriptors: [], predicate: NSPredicate(format: "%K BEGINSWITH %@", filterKey, filterValue))
self.content = content
var body: some View
List
ForEach(singers, id: \.self)
content($0)
.onDelete(perform: deleteSinger)
func deleteSinger(at offsets: IndexSet)
withAnimation
offsets.map singers[$0] .forEach(viewContext.delete)
try? viewContext.save()
我使用谓词将它们分类为 2 个列表。我无法解决的问题是,当我使用谓词“S”并删除列表中的对象,然后返回谓词“A”并删除该列表中的歌手时,即使我检查了歌手,我仍然有歌手显示在那里' 数组,它说它是空的。我也可以与这个“幽灵”对象交互,但是如果我尝试删除这个对象,应用程序会崩溃,因为现在它知道数组是空的并且有这样一个对象要删除。
这是我的内容视图:
struct ContentView: View
@Environment(\.managedObjectContext) private var viewContext
@State var lastNameFilter = "A"
var body: some View
NavigationView
VStack
ListOfSingers(filterKey: "lastName", filterValue: lastNameFilter) (singer: Singer) in
Text("\(singer.wrappedName) \(singer.wrappedLastName)")
.onAppear print( "\(singer.wrappedName) \(singer.wrappedLastName)" )
Button("Add Singers")
let singer1 = Singer(context: viewContext)
singer1.firstName = "Taylor"
singer1.lastName = "Swift"
let singer2 = Singer(context: viewContext)
singer2.firstName = "Ed"
singer2.lastName = "Sheeran"
let singer3 = Singer(context: viewContext)
singer3.firstName = "Adele"
singer3.lastName = "Adkins"
try? viewContext.save()
.frame(width: 280, height: 50)
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
.padding()
HStack
Button("Sort by A")
withAnimation
lastNameFilter = "A"
.frame(width: 130, height: 50)
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
Spacer()
Button("Sort by S")
withAnimation
lastNameFilter = "S"
.frame(width: 130, height: 50)
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
.padding()
.navigationTitle("Singers")
【问题讨论】:
您只执行一次获取请求,因此 UI 不会更新。保存后再次手动执行它,或收听上下文的更新通知或采用新的@FetchRequest 属性包装器 我采用了新的@FetchRequest 属性包装器,但结果仍然相同。我尝试再次手动执行 fetch 请求,但它失败了,因为 SwiftUI 不允许使用变异函数,因为 @FetchRequest@State var lastNameFilter = "A"
在初始化之前无法使用。
另外,即使删除了泛型,我仍然遇到同样的问题
【参考方案1】:
问题是由于某种原因 ListOfSingers 结构被调用了两次。即使我从 Core Data 中删除了最后一位歌手,渲染时间也比重新索引 Core Data 更快。所以我最终得到了一个“幽灵视图”,它是在我的项目从核心数据中完全删除之前呈现的。当我尝试删除 Ghost 视图时,我遇到了一个不存在的数组。它总是会导致应用崩溃。
解决方案是添加一个函数,在删除触发后立即检查 Core Data 中是否有任何项目。
在主视图中创建@State var noMoreSingers = false
以检查应呈现哪个视图并将此条件添加到 VStack 以明确告知应呈现哪个视图:
VStack
if !noMoreSingers
ListOfSingers(filterKey: "lastName", filterValue: lastNameFilter) (singer: Singer) in
Text("\(singer.wrappedName) \(singer.wrappedLastName)")
else
Spacer()
将@Binding var noMoreSingers: Bool
添加到ListOfSingers
以便能够在Core Data 中没有更多项目时告诉主视图
添加函数到 Persistanse Controller 以获取所有项目:
func getAllSingers() -> [Singer]
let request: NSFetchRequest<Singer> = Singer.fetchRequest()
do
return try container.viewContext.fetch(request)
catch
fatalError("Error Fetching Users")
将函数添加到 ListOfSingers 以触发 getAllSingers
并检查检索到的数组是否为空:
func checkIfSingersAreEmpty()
let checkSingers = PersistenceController.shared.getAllSingers()
if checkSingers.isEmpty
noMoreSingers = true
现在一切正常!
【讨论】:
以上是关于SwiftUI 列表在删除后继续显示核心数据项的主要内容,如果未能解决你的问题,请参考以下文章
如何从 SwiftUI 的详细信息视图(编辑项目视图)中删除核心数据条目?
在 macOS 上的 SwiftUI 列表视图中选择和删除核心数据实体