0

Hier zu wiederholen ist mein Dilemma, Refactoring ich meinen Code, NSFetchedResultsController zu verwenden, möchte ich hart Code a Satz von 5 Abschnitten, die ich immer anzeigen möchte, unabhängig davon, ob Zeilen darin enthalten sind oder nicht. Ich bin ein bisschen erfolgreich darin, sie zum Vorschein zu bringen, aber leider kann ich nicht die richtigen Zeilen finden, um unter den richtigen Abschnitten zu erscheinen. (Anmerkung: Ich bin mit einem benutzerdefinierten Header mit einer Schaltfläche, die Reihe der hinzufügt, wenn der Benutzer eine Zelle auf einen bestimmten Abschnitt hinzufügen möge.) Die Modellbeziehung in Coredata ist, dass ein Hund Modell viel Aufgabe den haben kann, sondern eine Aufgabe kann nur haben ein Hund. Leider scheint ich alle Aufgaben für jeden einzigartigen "Hund", den ich erschaffe, zu holen, so dass sie alle dieselben Informationen haben.Mit NSFetchedResultsController, wie kann ich programmgesteuert 5 Abschnitte zu zeigen sowie filtern, um die entsprechenden Zeilen

Hier ist mein Task-Modell und die Enumeration, die ich verwende, um die 5 Abschnitte in der NSFetchedResultsController zu erstellen.

Hier ist mein FetchedResultsController, wo ich den Enum-Typ als sectionNameKeyPath übergeben.

class TaskController { 
    static let sharedController = TaskController() 
    private let kTask = "Task" 
    var fetchedResultsController: NSFetchedResultsController 

    var dog: Dog? 

    init() { 
     let request = NSFetchRequest(entityName: kTask) 
     let sortDescriptor1 = NSSortDescriptor(key: "type", ascending: true) 
     request.sortDescriptors = [sortDescriptor1] 

     fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: Stack.sharedStack.managedObjectContext, sectionNameKeyPath: String(Type), cacheName: nil) 
     _ = try? fetchedResultsController.performFetch() 
    } 
} 

Im numberOfRowsInSection versuche ich den sections.count übereinstimmen Abschnitt zu der der Funktion Eigenschaft gleich zu sein, wie ich die fetchedResultsController Abschnitt gehe davon tatsächlich nicht existiert, bis eine Zeile erstellt wird? Um ehrlich zu sein, bin ich an diesem Punkt verloren, da ich nicht sicher bin, wie ich die richtigen Zeilen holen soll, um den passenden Abschnitten zu entsprechen. Der kommentierte Code war, wie ich anfänglich die richtigen Zeilen für die richtigen Abschnitte abgerufen habe, bevor ich meinen Code umstrukturierte und ihn auf den NSFetchedResultsController-Weg aktualisierte.

extension DogDetailViewController: UITableViewDataSource { 
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
     guard let sections = TaskController.sharedController.fetchedResultsController.sections else { return 0 } 

     if sections.count > 0 && section < sections.count { 
      return sections[section].numberOfObjects 
     } 
     return 0 

      //   } 

      //  if let dog = self.dog { 
      //   switch section { 
      //   case 0: 
      //    return dog.tasks.filter({$0.type == String(Type.Meals)}).count 
      //   case 1: 
      //    return dog.tasks.filter({$0.type == String(Type.Exercise)}).count 
      //   case 2: 
      //    return dog.tasks.filter({$0.type == String(Type.Health)}).count 
      //   case 3: 
      //    return dog.tasks.filter({$0.type == String(Type.Training)}).count 
      //   case 4: 
      //    return dog.tasks.filter({$0.type == String(Type.Misc)}).count 
      //   default: 
      //    return 0 
      //   } 
      //  } else { 
      //   return 0 
      //  } 
     } 

Jede Hilfe wird geschätzt, Danke!

Antwort

1

Wenn Sie möchten, dass alle 5 Abschnitte angezeigt werden, unabhängig davon, wie viele Aufgaben dieses Typs für einen bestimmten Hund vorhanden sind, können Sie die Anzahl der Abschnitte und ihre Reihenfolge fest codieren und eine NSFetchedResultsController (und damit eine andere NSFetchRequest) haben) für jeden Abschnitt.

Ihre vorhandene Abrufanforderung beschränkt Ihre Anforderung nicht auf einen bestimmten Hund (Sie würden dies tun, indem Sie eine NSPredicate mit der entsprechenden Einschränkung erstellen und diese für Ihre Abrufanforderung festlegen). Dies ist wahrscheinlich wichtig, damit Sie nicht alle Task in den Speicher ziehen und dann den Filter durchführen. Dies würde erfordern, dass Ihre Task Unternehmen eine Beziehung zu der Dog Einheit in Ihrem Datenmodell hat, die auf der Basis des Code nicht klar ist, dass Sie geschrieben haben. Ich werde annehmen, dass du diese Beziehung hast. Sie können auch kein Singleton-Modell für Ihre TaskController (TaskDataSource) in der Weise verwenden, wie Sie es jetzt sind, da Sie jedes Mal ein neues Modell erstellen müssen, wenn Sie die Dog ändern, für die Sie eine Anfrage stellen möchten.

Beachten Sie auch, dass ich einfach ging und TaskController in TaskDataSource gemacht und das UICollectionViewDataSource Protokoll direkt übernommen. Dies ist nicht erforderlich, macht es jedoch potenziell wiederverwendbar, wenn Ihre App diesen Inhalt auf mehreren Bildschirmen haben möchte.

class TaskDataSource: NSObject, UICollectionViewDataSource { 
    func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { 
     return TaskDataSource.Sections.count 
    } 

    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 
     let count: Int 
     switch TaskDataSource.Sections[section] { 
     case .Meals: 
      count = mealsResultsController?.sections?.first?.numberOfObjects ?? 0 
     case .Exercise: 
      count = exerciseResultsController?.sections?.first?.numberOfObjects ?? 0 
     case .Health: 
      count = healthResultsController?.sections?.first?.numberOfObjects ?? 0 
     case .Training: 
      count = trainingResultsController?.sections?.first?.numberOfObjects ?? 0 
     case .Misc: 
      count = miscResultsController?.sections?.first?.numberOfObjects ?? 0 
     } 

     return count 
    } 

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { 
     let task: Task 
     // Each fetched results controller has a singel section, so we have to make an appropriate index path 
     let adjustedIndexPath = NSIndexPath(forItem: indexPath.item, inSection: 0) 
     switch TaskDataSource.Sections[indexPath.section] { 
     case .Meals: 
      task = mealsResultsController?.objectAtIndexPath(adjustedIndexPath) as! Task 
     case .Exercise: 
      task = exerciseResultsController?.objectAtIndexPath(adjustedIndexPath) as! Task 
     case .Health: 
      task = healthResultsController?.objectAtIndexPath(adjustedIndexPath) as! Task 
     case .Training: 
      task = trainingResultsController?.objectAtIndexPath(adjustedIndexPath) as! Task 
     case .Misc: 
      task = miscResultsController?.objectAtIndexPath(adjustedIndexPath) as! Task 
     } 

     // This part will vary, depending on your cell/storyboard, but this is the idea. Note we don't use the adjusted index path here 
     let cell = collectionView.dequeueReusableCellWithReuseIdentifier("TaskCell", forIndexPath: indexPath) as! TaskCell 
     cell.titleLabel.text = task.title 

     return cell 
    } 

    init(dog: Dog) { 
     // Create a sort descriptor to sort by whatever you like, I assume you'd want things sorted by title 
     let sortDescriptor = NSSortDescriptor(key: "title", ascending: true) 

     // A closure to create an NSFetchedResultsController, this avoids copy/pasting 
     let createFetchRequestForType = { (type: Type) -> NSFetchedResultsController? in 
      let fetchRequest = NSFetchRequest(entityName: Task.kClassName) 
      // Note, you'll want to create a multi-key index on the Task entity to make sure this is reasonably fast 
      fetchRequest.predicate = NSPredicate(format: "dog == %@ && type == %@", dog, type.rawValue) 
      fetchRequest.sortDescriptors = [sortDescriptor] 

      let context = Stack.sharedStack.managedObjectContext 
      let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil) 
      do { 
       try fetchedResultsController.performFetch() 
      } 
      catch { 
       return nil 
      } 

      return fetchedResultsController 
     } 

     mealsResultsController = createFetchRequestForType(.Meals) 
     exerciseResultsController = createFetchRequestForType(.Exercise) 
     healthResultsController = createFetchRequestForType(.Health) 
     trainingResultsController = createFetchRequestForType(.Training) 
     miscResultsController = createFetchRequestForType(.Misc) 
    } 

    static let Sections: Array<Type> = [.Meals, .Exercise, .Health, .Training, .Misc] 

    var mealsResultsController: NSFetchedResultsController? 
    var exerciseResultsController: NSFetchedResultsController? 
    var healthResultsController: NSFetchedResultsController? 
    var trainingResultsController: NSFetchedResultsController? 
    var miscResultsController: NSFetchedResultsController? 
} 
+0

Dank @Charles A., wird heute Abend eine Aufnahme später geben. Es ist interessant, dass Sie jedem Element im Array Sections eine eigene Variablenzuordnung von NSFetchedResultsController gaben. Ich nahm einfach an, dass ich nur einen Haupt-NSFetchedResultsController brauchte, den ich einfach für die Information filtern würde, die ich wollte? –

+0

Wenn Sie den Abschnitt komplett weglassen wollten, wenn eine Aufgabe für einen bestimmten Typ nicht existierte, würde ich eine 'NSFetchedResultsController' verwenden, die Einschränkung für' type' im 'NSPredate' weglassen und sie stattdessen in Abschnitte mit dem' sectionNameKeyPath aufteilen '. Angesichts der Tatsache, dass Sie immer die Abschnitte wollen, halte ich das für einen einfacheren Ansatz. Im Idealfall möchten Sie die Filterung in der DB-Engine mit einem Prädikat durchführen, wenn dies sinnvoll ist und nicht im Speicher. –

Verwandte Themen