sectionName in tableView with date

前端 未结 1 866
北荒
北荒 2020-12-06 08:48
//date 
func sectionName() -> String{
    var shortDate: String
        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = \"MMM yyyy\"


 r         


        
相关标签:
1条回答
  • 2020-12-06 09:09

    You have probably defined sectionName() as a free function, and not as a property or method of your managed object subclass that is being fetched. Also formatting the "current date" as a string does not make much sense, as you probably want to group the objects according to some date property of your entity.


    Apple's sample project Custom Section Titles with NSFetchedResultsController demonstrates how to group a table view into sections based on the month and year of a "timeStamp" property.

    That project is written in Objective-C. Here is a short recipe how the same can be achieved in Swift. In the following, I assume that the entity and the managed object subclass is called Event, and that the events should be grouped according to the month and year of the timeStamp property.

    First, add a transient property "sectionIdentifier" of type "String" to the "Event" entity.

    Next, define the sectionIdentifier property of the Event class. This can be done directly in the class Event { ... } definition or as an extension:

    extension Event {
        var sectionIdentifier : String? {
            // Create and cache the section identifier on demand.
    
            self.willAccessValueForKey("sectionIdentifier")
            var tmp = self.primitiveValueForKey("sectionIdentifier") as? String
            self.didAccessValueForKey("sectionIdentifier")
    
            if tmp == nil {
                if let timeStamp = self.valueForKey("timeStamp") as? NSDate {
                    /*
                    Sections are organized by month and year. Create the section
                    identifier as a string representing the number (year * 1000) + month;
                    this way they will be correctly ordered chronologically regardless
                    of the actual name of the month.
                    */
                    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
        }
    }
    

    In the table view controller, you have to override the titleForHeaderInSection method to compute a proper title from the section identifier:

    override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        /*
        * Creating a date formatter is "expensive". Use a static property so that
        * this is done only once.
        */
        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
                }()
        }
    
        /*
        Section information derives from an event's sectionIdentifier, which is a string
        representing the number (year * 1000) + month.
        To display the section title, convert the year and month components to a string representation.
        */
    
        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
    }
    

    Finally, create the fetched results controller with "timeStamp" as first sort descriptor and "sectionIdentifier" as 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)
    

    If the time stamp of an object can be modified then things become slightly more complicated. The corresponding "sectionIdentifier" must be invalidated so that it is computed again on demand. In Objective-C, that is fairly simple by overriding the getter method of the "timeStamp" property only (see DateSectionTitles/APLEvent.m). In Swift this seems to require that you define "timeStamp" as a normal computed property (without @NSManaged), as discussed in https://devforums.apple.com/thread/262118?tstart=0:

    class Event: NSManagedObject {
    
        //@NSManaged 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")
                // If the time stamp changes, the section identifier become invalid.
                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
        }
    }
    

    Update: As of Swift 4 you have to make the custom Core Data properties explicitly visible to the Objective-C runtime by adding the @objc attribute, e.g.

    @objc var sectionIdentifier : String? { ... }
    
    @objc var timeStamp: NSDate? { ... }
    

    For more information about this change, see

    • Swift 4 "This class is not key value coding compliant"
    • How can I deal with @objc inference deprecation with #selector() in Swift 4?
    0 讨论(0)
提交回复
热议问题