Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/sorting/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift NSFetchedResultsController每个节使用不同的排序描述符_Swift_Sorting_Core Data_Nsfetchedresultscontroller - Fatal编程技术网

Swift NSFetchedResultsController每个节使用不同的排序描述符

Swift NSFetchedResultsController每个节使用不同的排序描述符,swift,sorting,core-data,nsfetchedresultscontroller,Swift,Sorting,Core Data,Nsfetchedresultscontroller,我有NSDate属性的对象,我需要将它们分成两部分(第一部分-未来事件,第二部分-历史事件),然后对于第一部分,我需要按日期属性按升序排序,第二部分按降序排序。你知道怎么分类吗 首先在fetchRequest中设置排序描述符 func itemFetchRequest() -> NSFetchRequest{ let fetchRequest = NSFetchRequest(entityName: "Events") let primarySortDescription

我有NSDate属性的对象,我需要将它们分成两部分(第一部分-未来事件,第二部分-历史事件),然后对于第一部分,我需要按日期属性按升序排序,第二部分按降序排序。你知道怎么分类吗

首先在fetchRequest中设置排序描述符

func itemFetchRequest() -> NSFetchRequest{

    let fetchRequest = NSFetchRequest(entityName: "Events")
    let primarySortDescription = NSSortDescriptor(key: "futureEvents", ascending: true)
    let secondarySortDescription = NSSortDescriptor(key: "historicEvents", ascending: false)
    fetchRequest.sortDescriptors = [primarySortDescription, secondarySortDescription]
    return fetchRequest
}
然后设置节数

func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    let numberOfSections = frc.sections?.count
    return numberOfSections!
}
最后是你的部分标题

func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String?{
    let sectionHeader = Future Events
    let sectionHeader1 = Historic Events
        if (section== "0") { 
            return sectionHeader
        } else {
            return sectionHeader1
        }
    } else {
        return nil
    }
}

假设您使用的是
NSFetchedResultsController
,则必须以这种或那种方式对底层提取进行排序。我可以想出两种不同的解决方案:

  • 使用两个独立的FRC和互补谓词,以便一个处理过去的事件,而另一个处理未来的事件。一个按升序排序,另一个按降序排序。问题是两个FRC都将为节0生成
    indexpath
    。因此,您需要重新映射第二个FRC的
    indexPaths
    ,以使用tableView的第1节。例如,在
    cellforrowatinexpath
    中,您需要如下内容:

    if (indexPath.section == 0) {
        objectToDisplay = self.futureFetchedResultsController.objectAtIndexPath(indexPath)
    } else { // use second FRC
        let frcIndexPath = NSIndexPath(forRow: indexPath.row, inSection: 0)
        objectToDisplay = self.pastFetchedResultsController.objectAtIndexPath(frcIndexPath)
    }
    
  • 或者,坚持使用单个FRC,按升序排序。然后为第二节重新映射
    indexPath
    ,以便节中的最后一个对象显示在第0行,以此类推:

    if (indexPath.section == 0) {
        objectToDisplay = self.fetchedResultsController.objectAtIndexPath(indexPath)
    } else { // use remap to reverse sort order FRC
        let sectionInfo = self.fetchedResultsController.sections[1] as! NSFetchedResultsSectionInfo
        let sectionCount = sectionInfo.numberOfObjects
        let frcIndexPath = NSIndexPath(forRow: (sectionCount - 1 - indexPath.row), inSection:indexPath.section)
        objectToDisplay = self.fetchedResultsController.objectAtIndexPath(frcIndexPath)
    }
    

  • 我个人认为第二种选择更可取。在每种情况下,所有tableView数据源/委托方法都需要相同的重新映射,而FRC委托方法将需要反向映射。

    要解决一般问题,需要对每个节进行不同的排序,您可以将多个
    NSFetchedResultsController
    对象包装到单个对象中,将节数组展平到单个数组中,并重新映射像
    func object这样的函数的结果(位于:IndexPath)
    以及
    NSFetchedResultsControllerDelegate
    通知中的索引路径

    这将允许您解决希望以不同方式显示任意数量的节的一般问题

    我尝试过创建这个包装器对象(a),它似乎工作得很好:

    /**
     A CompoundFetchedResultsController is a wrapper of a number of inner NSFetchedResultsControllers.
     The wrapper flattens the sections produced by the inner controllers, behaving as if all sections
     were fetched by a single controller. Additionally, change notifications are mapped before being
     passed to the optional NSFetchedResultsControllerDelegate property, so that the section indices
     in the notifications reflect the flattened section indicies.
    
     Example use case: a table where sections should be ordered in mutually opposing ways. E.g., if
     section 1 should be ordered by propery A ascending, but section 2 should be ordered by property A
     descending. In this case, two controllers can be created - one ordering ascending, the other de-
     scending - and wrapped in a CompoundFetchedResultsController. This will maintain the ease of use
     in a UITableViewController, and the functionality provided by a NSFetchedResultsControllerDelegate.
     */
    class CompoundFetchedResultsController<T: NSFetchRequestResult>: NSObject, NSFetchedResultsControllerDelegate {
    
        // The wrapperd controllers
        let controllers: [NSFetchedResultsController<T>]
    
        // A delegate to notify of changes. Each of the controllers' delegates are set to this class,
        // so that we can map the index paths in the notifications before forwarding to this delegate.
        var delegate: NSFetchedResultsControllerDelegate? {
            didSet { controllers.forEach{$0.delegate = self} }
        }
    
        init(controllers: [NSFetchedResultsController<T>]) { self.controllers = controllers }
    
        func performFetch() throws { controllers.forEach{try? $0.performFetch()} }
    
        var sections: [NSFetchedResultsSectionInfo]? {
            // To get the flattened sections array, we simply reduce-by-concatenation the inner controllers' sections arrays.
            get { return controllers.flatMap{$0.sections}.reduce([], +) }
        }
    
        private func sectionOffset(forController controller: NSFetchedResultsController<T>) -> Int {
            // Determine the index of the specified controller
            let controllerIndex = controllers.index(of: controller)!
    
            // Count the number of sections present in all controllers up to (but not including) the supplied controller
            return controllers.prefix(upTo: controllerIndex).map{$0.sections!.count}.reduce(0, +)
        }
    
        func object(at indexPath: IndexPath) -> T {
            // Sum the section counts of the controllers, in order, until we exceed the section of the supplied index path.
            // At that point, we have identifiers the controller which should be used to obtain the object, and just
            // adjust the supplied index path's section accordingly.
            var sectionCount = 0
            for controller in controllers {
                if sectionCount + controller.sections!.count <= indexPath.section {
                    sectionCount += controller.sections!.count
                }
                else {
                    return controller.object(at: IndexPath(row: indexPath.row, section: indexPath.section - sectionCount))
                }
            }
            fatalError("Could not find index path \(indexPath).")
        }
    
        func indexPath(forObject object: T) -> IndexPath? {
            // Given an object, to determine which controller it is in, we just query each controller in turn.
            for controller in controllers {
                if let indexPath = controller.indexPath(forObject: object) {
                    return IndexPath(row: indexPath.row, section: sectionOffset(forController: controller) + indexPath.section)
                }
            }
            return nil
        }
    
        func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
            // Forward on the willChange notification
            delegate?.controllerWillChangeContent?(controller)
        }
    
        func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
            // Forward on the didlChange notification
            delegate?.controllerDidChangeContent?(controller)
        }
    
        func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
    
            let sectionOffset = self.sectionOffset(forController: controller as! NSFetchedResultsController<T>)
    
            // Index Paths should be adjusted by adding to the section offset to the section index
            func adjustIndexPath(_ indexPath: IndexPath?) -> IndexPath? {
                guard let indexPath = indexPath else { return nil }
                return IndexPath(row: indexPath.row, section: indexPath.section + sectionOffset)
            }
    
            // Forward on the notification with the adjusted index paths
            delegate?.controller?(controller, didChange: anObject, at: adjustIndexPath(indexPath), for: type, newIndexPath: adjustIndexPath(newIndexPath))
        }
    
        func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
    
            let sectionOffset = self.sectionOffset(forController: controller as! NSFetchedResultsController<T>)
    
            // Forward on the notification with the adjusted section index
            delegate?.controller?(controller, didChange: sectionInfo, atSectionIndex: sectionIndex + sectionOffset, for: type)
        }
    }
    
    /**
    CompoundFetchedResultsController是许多内部NSFetchedResultsController的包装器。
    包装器将展平内部控制器生成的部分,其行为就像所有部分一样
    由单个控制器获取。此外,更改通知在被
    传递给可选的NSFetchedResultsControllerDelegate属性,以便节索引
    在通知中,反映扁平部分标记。
    示例用例:一个表格,其中各部分应以相互对立的方式排序。例如,如果
    第1节应按属性A升序排列,但第2节应按属性A升序排列
    降序。在这种情况下,可以创建两个控制器-一个按升序排序,另一个按de排序-
    香味-并包装在CompoundFetchedResultsController中。这将保持易用性
    在UITableViewController中,以及NSFetchedResultsControllerDelegate提供的功能。
    */
    类CompoundFetchedResultsController:NSObject,NSFetchedResultsController委托{
    //包装好的控制器
    let控制器:[NSFetchedResultsController]
    //通知更改的委托。每个控制器的委托都设置为此类,
    //因此,我们可以在转发到此委托之前映射通知中的索引路径。
    变量委托:NSFetchedResultsControllerDelegate{
    didSet{controllers.forEach{$0.delegate=self}
    }
    init(控制器:[NSFetchedResultsController]){self.controllers=controllers}
    func performFetch()抛出{controllers.forEach{try?$0.performFetch()}}
    变量节:[NSFetchedResultsSectionInfo]{
    //为了得到展平部分数组,我们只需通过连接内部控制器的部分数组来减少。
    获取{return controllers.flatMap{$0.sections}.reduce([],+)}
    }
    private func sectionOffset(用于控制器:NSFetchedResultsController)->Int{
    //确定指定控制器的索引
    让controllerIndex=controllers.index(of:controller)!
    //计算所提供控制器(但不包括)之前所有控制器中存在的节数
    返回controllers.prefix(最多:controllerIndex).map{$0.sections!.count}.reduce(0,+)
    }
    func对象(在indepath:indepath)->T{
    //按顺序对控制器的节计数求和,直到超过所提供索引路径的节。
    //在这一点上,我们有了用于获取对象的控制器标识符,并且
    //相应地调整提供的索引路径的部分。
    var sectionCount=0
    用于控制器中的控制器{
    如果sectionCount+控制器.sections!.count IndexPath{
    //给定一个对象,为了确定它在哪个控制器中,我们只需依次查询每个控制器。
    用于控制器中的控制器{
    如果让indexPath=controller.indexPath(forObject:object){
    返回IndexPath(行:IndexPath.row,节:sectionOffset(forController:controller)+IndexPath.section)
    }
    }
    归零
    }
    func controllerWillChangeContent(\ucontroller:NSFetchedResultsController){
    //在willChange通知上转发
    委托?.controllerWillChangeContent?(控制器)
    }
    func controllerDidChangeContent(\控制器:NSFetchedResultsController){
    //在didlChange通知上转发
    委托?.controllerDidChangeContent?(控制器)
    }
    func控制器(controller:NSFetchedResultsController,didChange anObject:Any,在indexPath:indexPath?处,对于类型:NSFetchedResultsChangeType,newIndexPath:indexPath?){
    设sectionOffset=自sectionOffset(forCont