在tableView中使用日期的sectionName
在tableView中使用日期的sectionName
//日期
func sectionName() -> String{
var shortDate: String
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MMM yyyy"
return dateFormatter.stringFromDate(NSDate())
}
//表格视图
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchedRequest, managedObjectContext: coreDataStack.managedObjectContext!, sectionNameKeyPath: "sectionName", cacheName: nil)
//错误
不明原因导致应用程序在"sectionName"处崩溃,报错为"is not key value coding-compliant for the key "sectionName"."
在使用sectionName()
函数时,可能将其定义为一个自由函数,而不是被获取的托管对象子类的属性或方法。此外,将“当前日期”格式化为字符串并没有太多意义,因为您可能想根据实体的某个日期属性对对象进行分组。
Apple的示例项目Custom Section Titles with NSFetchedResultsController演示了如何根据“timeStamp”属性的月份和年份将表视图分组为部分。该项目是用Objective-C编写的。
下面是如何在Swift中实现相同效果的简短步骤。
首先,在“Event”实体中添加一个类型为“String”的“sectionIdentifier”瞬态属性。
接下来,定义Event
类的sectionIdentifier
属性。可以直接在class Event { ... }
定义中完成,也可以作为扩展:
extension Event {
var sectionIdentifier : String? {
// 根据需要创建和缓存部分标识符
self.willAccessValueForKey("sectionIdentifier")
var tmp = self.primitiveValueForKey("sectionIdentifier") as? String
self.didAccessValueForKey("sectionIdentifier")
if tmp == nil {
if let timeStamp = self.valueForKey("timeStamp") as? NSDate {
/*
部分按月份和年份组织。创建表示数字(年份 * 1000)+ 月份的字符串作为部分标识符;
这样无论月份的实际名称如何,它们都将按照时间顺序正确排序。
*/
let calendar = NSCalendar.currentCalendar()
let components = calendar.components(.CalendarUnitYear | .CalendarUnitMonth, fromDate: timeStamp)
tmp = String(format: "%ld", components.year * 1000 + components.month)
self.setPrimitiveValue(tmp, forKey: "sectionIdentifier")
}
}
return tmp
}
}
在表视图控制器中,您必须重写titleForHeaderInSection
方法,以根据部分标识符计算正确的标题:
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
/*
* 创建一个日期格式化程序是“昂贵”的。使用静态属性,这样只需要执行一次。
*/
struct Formatter {
static let formatter : NSDateFormatter = {
let fmt = NSDateFormatter()
let dateFormat = NSDateFormatter.dateFormatFromTemplate("MMMM yyyy", options: 0,
locale: NSLocale.currentLocale())
fmt.dateFormat = dateFormat
return fmt
}()
}
/*
部分信息来自事件的部分标识符,其是一个表示数字(年份 * 1000)+ 月份的字符串。
为了显示部分标题,将年份和月份组件转换为字符串表示。
*/
if let theSection = fetchedResultsController.sections?[section] as? NSFetchedResultsSectionInfo,
let numericSection = theSection.name?.toInt() {
let components = NSDateComponents()
components.year = numericSection / 1000
components.month = numericSection % 1000
if let date = NSCalendar.currentCalendar().dateFromComponents(components) {
let titleString = Formatter.formatter.stringFromDate(date)
return titleString
}
}
return nil
}
最后,使用“timeStamp”作为第一个排序描述符和“sectionIdentifier”作为sectionNameKeyPath
来创建获取结果控制器:
let fetchRequest = NSFetchRequest(entityName: "Event")
let timeStampSort = NSSortDescriptor(key: "timeStamp", ascending: false)
fetchRequest.sortDescriptors = [timeStampSort]
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest,
managedObjectContext: context,
sectionNameKeyPath: "sectionIdentifier",
cacheName: nil)
如果对象的时间戳可以修改,则情况会稍微复杂一些。相应的“sectionIdentifier”必须无效化,以便按需重新计算。
在Objective-C中,通过仅重写“timeStamp”属性的getter方法很简单(参见DateSectionTitles/APLEvent.m)。在Swift中,似乎需要将“timeStamp”定义为一个普通的计算属性(没有NSManagedObject
),如https://devforums.apple.com/thread/262118?tstart=0中讨论的那样:
class Event: NSManagedObject {
// var timeStamp : NSDate
var timeStamp: NSDate? {
get {
self.willAccessValueForKey("timeStamp")
let tmp = self.primitiveValueForKey("timeStamp") as? NSDate
self.didAccessValueForKey("timeStamp")
return tmp
}
set {
self.willChangeValueForKey("timeStamp")
self.setPrimitiveValue(newValue, forKey: "timeStamp")
self.didChangeValueForKey("timeStamp")
// 如果时间戳发生变化,部分标识符将变为无效。
self.setPrimitiveValue(nil, forKey: "sectionIdentifier")
}
}
override class func keyPathsForValuesAffectingValueForKey(key: String) -> Set<NSObject> {
var keyPaths = super.keyPathsForValuesAffectingValueForKey(key)
if key == "sectionIdentifier" {
keyPaths.insert("timeStamp")
}
return keyPaths
}
}
更新:从Swift 4开始,您必须通过添加@objc
属性,明确将自定义的Core Data属性显示给Objective-C运行时,例如:
var sectionIdentifier : String? { ... }
var timeStamp: NSDate? { ... }
有关此更改的更多信息,请参见:
- Swift 4 "This class is not key value coding compliant"
- How can I deal with inference deprecation with #selector() in Swift 4?
嘿,-r,我按照您的代码在iOS 10上运行正常。但在iOS 11上,该属性无法识别,应用程序崩溃:stackoverflow.com/questions/45610890/…。您有任何想法吗?
:我认为您只需在变量的定义之前添加@objc
,就像我在您的其他问题中评论的那样(这将使其成为一个重复问题)。如果有帮助,请告诉我,我将在此处更新代码。谢谢!
添加objc
到变量的定义之前解决了该问题。感谢您的评论!