Swift CoreData:无法使用带有自定义功能的 sectionNameKeyPath 对 tableView 进行分区

Posted

技术标签:

【中文标题】Swift CoreData:无法使用带有自定义功能的 sectionNameKeyPath 对 tableView 进行分区【英文标题】:Swift CoreData: Unable to section tableView using sectionNameKeyPath with custom function 【发布时间】:2018-03-24 07:45:49 【问题描述】:

我是 CoreData 的一个极端新手,我正在尝试使用自定义函数使用 fetchedResultsController 对我的 tableView 进行分区。我当前的实现没有设法对 tableView 进行分区,我仍然只得到 1 个部分。

我在实现中参考了以下帖子:here 和 here。我还采用了瞬态属性。

我首先使用 Xcode 创建 NSManagedObject 子类(编辑器 -> 创建 NSMangedObject 子类)并添加 var sectionIdentifier 以返回自定义字符串。不幸的是,我的 frc 只返回 1 个部分。

// from the Conversation+CoreDataProperties.swift file automatically generated by Xcode
import Foundation
import CoreData


extension Conversation 

    @nonobjc public class func fetchRequest() -> NSFetchRequest<Conversation> 
        return NSFetchRequest<Conversation>(entityName: "Conversation")
    

    @NSManaged public var conversationStartTime: Double
    @NSManaged public var profileImage: NSData?
    @NSManaged public var recipientID: String?
    @NSManaged public var recipientUsername: String?
    @NSManaged public var shoutoutID: String?
    @NSManaged public var unreadMessagesCount: Int32

    var sectionIdentifier: String? 
        let presentTimestamp = NSDate().timeIntervalSince1970

        if conversationStartTime < presentTimestamp - Double(Constants.PermissibleDurationInMinutes * 60) 
            return "Expired Conversations"
         else 
            return "Active Conversations"
        
    


//at VC
lazy var fetchedResultsController: NSFetchedResultsController<Conversation> = 
    let context = CoreDataManager.shared.persistentContainer.viewContext

    let request: NSFetchRequest<Conversation> = Conversation.fetchRequest()
    request.sortDescriptors = [NSSortDescriptor(key: "conversationStartTime", ascending: true)]

    let frc = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: "sectionIdentifier", cacheName: nil)
    frc.delegate = self

    do 
        try frc.performFetch()
     catch let err 
        print(err)
    

    return frc
()

在控制台打印对话实体返回此

<Conversation: 0x604000e93100> (entity: Conversation; id: 0xd000000003e00000 <x-coredata://91BC90B2-9A0C-45A7-9B82-844BE88BAFE0/Conversation/p248> ; data: 
    conversationStartTime = "1521359598.88445";
    profileImage = <ffd8ffe0 00104a46 49460001 02000001 00010000 ffed009c 50686f74 6f73686f 7020332e 30003842 494d0404 00000000 0080>;
    recipientID = "-L7rvH71i-KUXvLQVDOh";
    recipientUsername = Angemon;
    sectionIdentifier = nil;
    shoutoutID = "-L7rvH71i-KUXvLQVDOh";
    unreadMessagesCount = 0; )

不知何故,sectionIdentifier 总是为零。任何建议为什么会发生这种情况?归根结底,我想将我的对话列表分为两部分,第一部分“活动对话”和第二部分“过期对话”,具体取决于该对话的时间。

更新代码:

// At Conversation+CoreDataProperties.swift
import Foundation
import CoreData


extension Conversation 

    @nonobjc public class func fetchRequest() -> NSFetchRequest<Conversation> 
        return NSFetchRequest<Conversation>(entityName: "Conversation")
    

    @NSManaged public var conversationStartTime: Double
    @NSManaged public var profileImage: NSData?
    @NSManaged public var recipientID: String?
    @NSManaged public var recipientUsername: String?
    @NSManaged public var shoutoutID: String?
    @NSManaged public var unreadMessagesCount: Int32

    @objc var sectionIdentifier: String 
        willAccessValue(forKey: "sectionIdentifier")

        let presentTimestamp = NSDate().timeIntervalSince1970
        var text = ""
        if conversationStartTime < presentTimestamp - Double(Constants.PermissibleDurationInMinutes * 60) 
            text = "Expired Conversations"
         else 
            text = "Active Conversations"
        

        didAccessValue(forKey: "sectionIdentifier")
        return text
    


//At VC
lazy var fetchedResultsController: NSFetchedResultsController<Conversation> = 
    let context = CoreDataManager.shared.persistentContainer.viewContext

    let request: NSFetchRequest<Conversation> = Conversation.fetchRequest()
    request.sortDescriptors = [
                               NSSortDescriptor(key: "conversationStartTime", ascending: false)
                                    ]

    let frc = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: "sectionIdentifier", cacheName: nil)
    frc.delegate = self

    do 
        try frc.performFetch()
     catch let err 
        print(err)
    

    return frc
()

在我的 CoreData 模型屏幕上:

【问题讨论】:

将@objc 添加到sectionIdentifier 的定义中。 @pbasdf 你的意思是Conversation+CoreDataProperties 文件?试过了,没什么区别。 我以前没有看到这个,我不确定它是否重要,但是您正在创建我从未做过的提取控制器的代码块内执行提取。我会尝试在块之外执行此操作,例如在 viewDidLoad 方法中。还要在 viewDidLoad 中设置委托属性。 @JoakimDanielson 我刚刚尝试过这种方法,但它仍然给了我完全相同的结果。我是否在代码块中执行 fetch 没有区别。 【参考方案1】:

这是@Sandeep 答案的延续,因为我想看看是否可以在没有排序描述符的情况下按瞬态属性进行分组,因为您的一个链接暗示应该可以这样做。

经过一些实验,我设法得出结论,如果你满足两个条件,这是可能的:

在用于计算瞬态属性的持久属性上需要一个排序描述符(并且它必须是第一个) 排序描述符的排序顺序需要与瞬态属性相匹配。

(我假设这适用于当您访问多个持久属性以及瞬态属性时,所有这些属性的排序描述符和排序顺序需要匹配,但我尚未测试)

正如我所见,您完成了第一个但没有完成第二个,因为您的排序描述符具有升序 = true 但由于“活动”出现在“过期”之前,因此您对 sectionIdentifier 有一个隐式的降序排序。

这是我用一些现有代码制作的一个愚蠢的例子,因为我有一些可用的测试数据。如您所见,我将一个月中的日子分为三个部分,以便按时间倒序显示。

@objc var silly: String 
    willAccessValue(forKey: #keyPath(silly))
    var result: String = "In between"

    let component = Calendar.current.component(Calendar.Component.day, from: date)
    if component <= 13 
        result = "Last"
     else if component > 20 
        result = "At the Top"
    
    didAccessValue(forKey: #keyPath(silly))
    return result

在设置我的获取结果控制器时,我会这样做

...
fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(Location.date), ascending: false)]  

let fetchedResultController = NSFetchedResultsController(fetchRequest: fetchRequest,
                                                             managedObjectContext: managedObjectContext,
                                                             sectionNameKeyPath: #keyPath(Location.silly),
                                                             cacheName: "Locations")

will/didAccessValue(...) 是处理第一次执行请求后新对象的插入和更新所必需的,否则该属性设置为 nil。

【讨论】:

我尝试了你的解决方案,但不知何故它对我不起作用......我仍然没有得到 sectionIdentifier 一个问题可能是您将属性定义为可选的,因为您总是返回一个字符串 @Koh,您是否尝试过不将 sectionIdentifier 声明为可选? 是的,我尝试过声明不是可选的,但我仍然坚持相同的结果。它仍然没有将其分成两部分。 而且我还注意到我无法取消选中“可选”字段,否则我无法创建 coredata 对象。会报错

以上是关于Swift CoreData:无法使用带有自定义功能的 sectionNameKeyPath 对 tableView 进行分区的主要内容,如果未能解决你的问题,请参考以下文章

核心数据:是不是可以在 group by 中使用自定义功能

如何为 Android 软键盘添加自定义功能

Ktor 自定义功能交换网址

自定义功能用户的角色不起作用,嵌套查询,prisma graphql

如何在 Material-UI 菜单中使用自定义功能组件

WordPress 创建自定义功能