Swift CoreData:无法使用带有自定义功能的sectionNameKeyPath来对tableView进行分区 [英] Swift CoreData: Unable to section tableView using sectionNameKeyPath with custom function

查看:57
本文介绍了Swift CoreData:无法使用带有自定义功能的sectionNameKeyPath来对tableView进行分区的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是使用CoreData的极端新手,并且尝试通过自定义函数使用fetchedResultsController来对tableView进行分区。我当前的实现未设法对tableView进行分区,而我仍然仅得到1个部分。



我在实现中引用了以下文章:

解决方案

这是@Sandeep的答案的继续,因为我想看看是否有可能在不使用排序描述符的情况下对瞬态属性进行分组,因为o



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




  • 在用于计算瞬时属性的持久性属性上需要一个排序描述符(并且它必须是第一个)

  • 排序描述符的排序顺序需要与瞬时属性匹配。



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



第一个而不是第二个,因为您的排序描述符具有ascending = true,但由于 Active在 Expired之前,因此您对sectionIdentifier具有隐式的降序排序。



这是我用一些现有代码制作的愚蠢示例,因为我有一些测试数据可用对此无能为力。如您所见,我将一个月的日期分为三部分,以便按相反的时间顺序显示。

  @ objc var silly:字符串{
willAccessValue(forKey:#keyPath(silly))
var结果:String =介于两者之间

让component = Calendar.current.component( Calendar.Component.day,来自:日期)如果组件< = 13 {
结果=最后
},则
否则,如果组件> 20 {
result = At the top
}
didAccessValue(forKey:#keyPath(silly))
返回结果
}

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

  ... 
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。


I am an extreme rookie with CoreData and I am attempting to section my tableView using fetchedResultsController using a custom function. My current implementation did not manage to section the tableView and I am still given just 1 section.

I refer to the following posts in my implementation: here and here. I also adopted the transient property.

I first create the NSManagedObject subclass using Xcode (Editor -> Create NSMangedObject Subclass) and added the var sectionIdentifier to return a custom string. Unfortunately, my frc returns only 1 section.

// 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
}()

Printing out the conversation entity in console returns this

<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; })

Somehow sectionIdentifier is always nil. Any advice why is this happening? At the end of the day, I want to divide my list of conversations into two sections, first section "Active Conversations" and second section "Expired Conversations" depending how long ago that conversation is.

UPDATED CODE:

// 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
}()

At my CoreData model screen:

解决方案

This is a continuation on the answer from @Sandeep since I wanted to see if it was possible to group by a transient property without having a sort descriptor with it since one of your links implied it should be possible to do so.

After some experimentation I managed to conclude that it is possible if you fulfil two conditions:

  • You need a sort descriptor (and it needs to be the first one) on the persistent attribute you use for calculating your transient attribute
  • The sort order of the sort descriptor needs to match the transient attribute.

(I assume this applies for when you access multiple persistent attributes as well for your transient attribute, sort descriptors for all of them and sort order needs to match but I haven't tested)

As I see it you fulfil the first one but not the second one since your sort descriptor has ascending = true but since "Active" comes before "Expired" you have an implicit descending sort order for sectionIdentifier.

Here is a silly example I made with some existing code since I had some test data available for it. As you can see I divide the days of the month into three sections so that the will show up in reverse chronological order.

@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
}

And when setting up my fetched result controller I do

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

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

The will/didAccessValue(...) was necessary to handle insert and updates of new objects after the first execute of the request, otherwise the attribute is set to nil.

这篇关于Swift CoreData:无法使用带有自定义功能的sectionNameKeyPath来对tableView进行分区的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆