【问题标题】:iOS: NSFetchResultsController sort on transient property (Swift)iOS:NSFetchResultsController 对瞬态属性排序 (Swift)
【发布时间】:2015-02-05 11:17:11
【问题描述】:

例如,我有一个名为 TasksEntity 和一个名为 dateAttribute 和一个填充此 TasksUITableView Entity.

这是我目前看到的:

7-Dec-14 09:30
7-Dec-14 11:00
7-Dec-14 13:30
7-Dec-14 16:00

现在让我们假设当前时间实际上是 14 年 12 月 7 日 12:00,所以前 2 行已经过去,但后 2 行是未来时间。我想将这些分成UITableView 中的组。

我发现我可以在实体上创建一个瞬态属性,如下所示:

var dateGroup: NSNumber {
    get {
        if date.compare(NSDate()) == NSComparisonResult.OrderedAscending {
            return 0
        } else {
            return 1
        }
    }
}

当我设置 sectionNameKeyPath: "dateGroup":

Group 0
7-Dec-14 09:30
7-Dec-14 11:00
Group 1
7-Dec-14 13:30
7-Dec-14 16:00

我的麻烦是如何让UITableView 实际显示这样的结果(所以未来的任务高于过期任务):

Group 1
7-Dec-14 13:30
7-Dec-14 16:00
Group 0
7-Dec-14 09:30
7-Dec-14 11:00

我尝试在瞬态属性 (dateGroup) 上创建 NSSortDescriptor,但这不起作用。

我意识到这是因为我的NSSortDescriptor 在“日期”属性上。但我不知道如何搜索 date > currentDate 的行,然后是 date

我想基本上我想要做的是交换如果有超过 1 个部分。

希望这是有道理的。您将如何解决这个排序问题?

更新

好的,我已将其完全重写为使用 2 x FetchRequestControllers self.upcomingFRC(第 0 节)和 self.elapsedFRC(第 1 节),以下内容现在可以正常工作,没有任何错误或 nils。

FetchResultsControllers 之间的所有插入、更新、移动和删除都流畅且具有动画效果。

非常感谢 pbasdf。

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
    switch type {
        case .Insert:
            println("insert")
            if var indexPathNew = newIndexPath {
                if controller == self.elapsedFRC {  indexPathNew = NSIndexPath(forRow: newIndexPath!.row, inSection: 1)  }
                println("N indexPath : \(indexPathNew)")
                self.tableViewEvent.insertRowsAtIndexPaths([indexPathNew], withRowAnimation: .Fade)
            }
        case .Delete:
            println("delete")
            if var indexPathOld = indexPath {
                if controller == self.elapsedFRC {  indexPathOld = NSIndexPath(forRow: indexPath!.row, inSection: 1)  }
                println("O indexPath : \(indexPathOld)")
                self.tableViewEvent.deleteRowsAtIndexPaths([indexPathOld], withRowAnimation: .Fade)
            }
        case .Update:
            println("update")
            if self.showLarge {
                if var indexPathOld = indexPath {
                    if controller == self.elapsedFRC {  indexPathOld = NSIndexPath(forRow: indexPath!.row, inSection: 1)  }
                    println("O indexPath : \(indexPathOld)")
                    if let largeCell = self.tableViewEvent.cellForRowAtIndexPath(indexPathOld) as? LargeTableViewCell {
                        if      indexPathOld.section == 0 {  self.configureLargeCell(largeCell, frc:self.upcomingFRC, row:indexPathOld.row)  }
                        else if indexPathOld.section == 1 {  self.configureLargeCell(largeCell, frc:self.elapsedFRC, row:indexPathOld.row)  }
                    } else {
                        println("******************************************")
                        println("found nil largeCell - configure Large Cell")
                        println("******************************************")
                    }
                } else {
                    println("******************************************")
                    println("found nil indexPath - configure Large Cell")
                    println("******************************************")
                }
            } else {
                if let indexPathOld = indexPath {
                    if let smallCell = self.tableViewEvent.cellForRowAtIndexPath(indexPathOld) as? SmallTableViewCell {
                        if      indexPathOld.section == 0 {  self.configureSmallCell(smallCell, frc:self.upcomingFRC, row:indexPathOld.row)  }
                        else if indexPathOld.section == 1 {  self.configureSmallCell(smallCell, frc:self.elapsedFRC, row:indexPathOld.row)  }
                    } else {
                        println("******************************************")
                        println("found nil smallCell - configure Small Cell")
                        println("******************************************")
                    }
                } else {
                    println("******************************************")
                    println("found nil indexPath - configure Small Cell")
                    println("******************************************")
                }
            }
        case .Move:
            println("move")
            if var indexPathOld = indexPath {
                if controller == self.elapsedFRC {  indexPathOld = NSIndexPath(forRow: indexPath!.row, inSection: 1)  }
                println("O indexPath : \(indexPathOld)")
                self.tableViewEvent.deleteRowsAtIndexPaths([indexPathOld], withRowAnimation: .Fade)
            }
            if var indexPathNew = newIndexPath {
                if controller == self.elapsedFRC {  indexPathNew = NSIndexPath(forRow: newIndexPath!.row, inSection: 1)  }
                println("N indexPath : \(indexPathNew)")
                self.tableViewEvent.insertRowsAtIndexPaths([indexPathNew], withRowAnimation: .Fade)
            }
        default:
            return
    }
}

【问题讨论】:

  • 我的错误 - 您应该使用 indexPath 而不是 newIndexPath 将单元格出列(我会修改我的答案)。使用替代的 dequeue(不指定 indexPath)应该可以正常工作,但是您需要仔细检查您没有得到 nil 单元格(而 dequeue... forIndexPath: 如果找不到原型单元格或注册的类/笔尖,则会抛出异常) .

标签: ios sorting core-data swift transient


【解决方案1】:

我会修改您的 tableView 委托和数据源函数,以便它们“交换”这些部分。例如:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if self.fetchedResultsController.sections!.count = 2 {
        section = 1 - section
    }
    let sectionInfo = self.fetchedResultsController.sections![section] as NSFetchedResultsSectionInfo
    return sectionInfo.numberOfObjects
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    var newIndexPath : NSIndexPath
    if self.fetchedResultsController.sections!.count = 2 {
        newIndexPath = NSIndexPath(forRow: indexPath.row, inSection: (1 - indexPath.section))
    } else {
        newIndexPath = indexPath
    }
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
    self.configureCell(cell, atIndexPath: newIndexPath)
    return cell
}

遗憾的是,您必须为每个功能执行此操作。如果您使用 fetched results controller 来实现插入/删除等,还要小心,因为您必须在那里进行相同的调整。

【讨论】:

  • 您好,感谢您抽出宝贵时间。我现在不在我的电脑附近尝试这个,但它确实有意义,我相信它会起作用(注意你对所有其他领域的评论也需要改变)。我唯一的另一个想法是创建 2 x NSFetchedResultsController 一个用于已过期,一个用于即将到来。这是个坏主意吗?
  • 拥有两个 FRC 肯定是可行的。我在一个 tableView 中显示了两个单独的实体,而您可能只有互斥谓词(这将避免对瞬态属性的需要)。您只需要注意同时触发(或交错)的 FRC 委托方法。
  • 谢谢。所以你是说因为我正在查询同一个实体,所以使用瞬态属性(就像我的第一篇文章中一样)会更简单(甚至更稳定),然后只是切换部分?最后,我是否认为只需添加一个瞬态属性就不需要在下次升级时将我的核心数据迁移到新数据库?
  • 我不确定哪个更简单(恐怕我不知道添加瞬态属性是否需要迁移)。我喜欢 2 FRC 方法;我唯一的犹豫是 FRC 委托函数将如何处理对 Task 的编辑,从而有效地将其从 Expired 移动到 Upcoming,反之亦然 - 两个 FRC 或多或少会同时触发委托函数。这一切都可能像梦一样工作!
  • 感谢您的帮助。将在几个小时内尝试这两种情况,并让您知道我的进展情况。干杯
猜你喜欢
  • 1970-01-01
  • 2014-11-15
  • 2011-02-10
  • 2011-08-19
  • 1970-01-01
  • 2023-03-29
  • 1970-01-01
  • 2011-11-23
  • 1970-01-01
相关资源
最近更新 更多