4

Я загружаю информацию из Интернета и используя данные для создания объектов в Core Data. Я пытаюсь сортировать объекты (сущности - это ТВ-шоу, данные из Trakt) атрибутом airDate объекта TVEpisode, который имеет отношение к объекту TVShow. Объект TVShow имеет это отношение только к показу, если у показанных данных есть эпизод, который будет транслироваться в будущую дату с текущего времени.Вставка строк с несколькими NSFetchedResultsController

Так как я хочу, чтобы отсортировать данные в: Top: Показывает, имеющие отношение upcomingEpisode, отсортированный по атрибуту upcomingEpisodeairDate, упорядоченный по возрастанию. Середина: Показывает, что у них нет отношений upcomingEpisode, но будет возвращаться. Дно: показывает, что у них нет отношений upcomingEpisode, и которые завершены/отменены

Вот проблемы, с которыми я столкнулся, чтобы заставить это работать.

Выпуск 1: Использование 1 NSFetchedResultsController

let fetchRequest = NSFetchRequest(entityName: "TVShow") 
    let airDateSort = NSSortDescriptor(key: "upcomingEpisode.airDate", ascending: true) 
    let titleSort = NSSortDescriptor(key: "title", ascending: true) 
    fetchRequest.sortDescriptors = [airDateSort, titleSort]; 

    upcomingShowsResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: coreDataStack.context, sectionNameKeyPath: nil, cacheName: "upcomingShows") 
    upcomingShowsResultsController.delegate = self; 

    var error: NSError? = nil 
    if (!upcomingShowsResultsController.performFetch(&error)) { 
     println("Error: \(error?.localizedDescription)") 
    } 

Используя этот NSFetchedResultsController приложу все TVShow сущности, не upcomingEpisode отношений на вершине, отсортированные все по названию, мне нужны мертвые шоу, отсортированные по названию на самом дне и возвращающие шоу сортируются по названию в середине.

Выпуск 2: Использование нескольких NSFetchedResultsController «ы

func setupUpcomingShowsFetchedResultsController() { 
    let fetchRequest = NSFetchRequest(entityName: "TVShow") 
    let airDateSort = NSSortDescriptor(key: "upcomingEpisode.airDate", ascending: true) 
    let titleSort = NSSortDescriptor(key: "title", ascending: true) 
    fetchRequest.sortDescriptors = [airDateSort, titleSort]; 

    let predicate = NSPredicate(format: "upcomingEpisode != nil") 
    fetchRequest.predicate = predicate 

    upcomingShowsResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: coreDataStack.context, sectionNameKeyPath: nil, cacheName: "upcomingShows") 
    upcomingShowsResultsController.delegate = self; 

    var error: NSError? = nil 
    if (!upcomingShowsResultsController.performFetch(&error)) { 
     println("Error: \(error?.localizedDescription)") 
    } 
} 

func setupReturningShowsFetchedResultsController() { 
    let fetchRequest = NSFetchRequest(entityName: "TVShow") 
    let titleSort = NSSortDescriptor(key: "title", ascending: true) 
    fetchRequest.sortDescriptors = [titleSort]; 

    let predicate = NSPredicate(format: "status == 'returning series'") 
    let predicate2 = NSPredicate(format: "upcomingEpisode == nil") 
    fetchRequest.predicate = NSCompoundPredicate.andPredicateWithSubpredicates([predicate!, predicate2!]) 

    returningShowsResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: coreDataStack.context, sectionNameKeyPath: nil, cacheName: nil) 
    returningShowsResultsController.delegate = self; 

    var error: NSError? = nil 
    if (!returningShowsResultsController.performFetch(&error)) { 
     println("Error: \(error?.localizedDescription)") 
    } 
} 

func setupDeadShowsFetchedResultsController() { 
    let fetchRequest = NSFetchRequest(entityName: "TVShow") 
    let titleSort = NSSortDescriptor(key: "title", ascending: true) 
    fetchRequest.sortDescriptors = [titleSort] 

    let endedShowsPredicate = NSPredicate(format: "status == 'ended'") 
    fetchRequest.predicate = endedShowsPredicate 

    deadShowsResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: coreDataStack.context, sectionNameKeyPath: nil, cacheName: nil) 
    deadShowsResultsController.delegate = self; 

    var deadShowsError: NSError? = nil 
    if (!deadShowsResultsController.performFetch(&deadShowsError)) { 
     println("Error: \(deadShowsError?.localizedDescription)") 
    } 
} 

Они работают для того, что я хочу, но только тогда, когда данные уже загружены и в Core Data. Когда приложение сначала запускает и загружает данные, они сбрасываются каждый раз, потому что количество строк в разделе не совпадает с тем, что ожидает таблица. Я манипулировал указательными путями, которые дает NSFetchedResultsControllerDelegate в функции didChangeObject, и я распечатал индекс, который вставляется. Счет, который я делал в любом разделе, был равен тому, сколько табличного представления говорит, что он ожидал, но каждый раз он выдает ошибку. Это, как я обработка метод многократного NSFetchedResultsController's

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { 
     let section = sectionOfFetchedResultsController(controller) 
     let indexPathsComputed = [NSIndexPath(forRow: indexPath?.row ?? 0, inSection: section)] 
     let newIndexPathsComputed = [NSIndexPath(forRow: newIndexPath?.row ?? 0, inSection: section)] 

     dispatch_async(dispatch_get_main_queue(), {() -> Void in 
      switch type { 
      case NSFetchedResultsChangeType.Insert: 
       self.tableView.insertRowsAtIndexPaths(newIndexPathsComputed, withRowAnimation: .Automatic) 
      case NSFetchedResultsChangeType.Delete: 
       self.tableView.deleteRowsAtIndexPaths(indexPathsComputed, withRowAnimation: .Automatic) 
      case NSFetchedResultsChangeType.Move: 
       self.tableView.deleteRowsAtIndexPaths(indexPathsComputed, withRowAnimation: .Automatic) 
       self.tableView.insertRowsAtIndexPaths(newIndexPathsComputed, withRowAnimation: .Automatic) 
      case NSFetchedResultsChangeType.Update: 
       if let index = indexPathsComputed[0] { 
        if let cell = self.tableView.cellForRowAtIndexPath(index) as? ShowTableViewCell { 
         self.configureCell(cell, indexPath: index) 
        } 
       } 
       else { 
        println("No cell at index path") 
       } 
      } 
     }) 
    } 

Если сбой может быть исправлен, это будет лучшим способом добиться того, что я хочу сделать.

Выпуск 3: Использование множественного массива

func reloadShowsArray() { 
    let fetchRequest = NSFetchRequest(entityName: "TVShow") 
    let airDateSort = NSSortDescriptor(key: "upcomingEpisode.airDate", ascending: true) 
    let titleSort = NSSortDescriptor(key: "title", ascending: true) 
    fetchRequest.sortDescriptors = [airDateSort, titleSort]; 

    let predicate = NSPredicate(format: "upcomingEpisode != nil") 
    fetchRequest.predicate = predicate 

    var error: NSError? 
    showsArray = coreDataStack.context.executeFetchRequest(fetchRequest, error: &error) as [TVShow] 
    if let error = error { 
     println(error) 
    } 
} 

func reloadDeadShows() { 
     let fetchRequest = NSFetchRequest(entityName: "TVShow") 
     let titleSort = NSSortDescriptor(key: "title", ascending: true) 
     fetchRequest.sortDescriptors = [titleSort] 

     let endedShowsPredicate = NSPredicate(format: "status == 'ended'") 
     fetchRequest.predicate = endedShowsPredicate 

     var error: NSError? 
     deadShows = coreDataStack.context.executeFetchRequest(fetchRequest, error: &error) as [TVShow] 
     if let error = error { 
      println(error) 
     } 
    } 

Это решает Сбои и работает после того, как данные загружены, и в то время как данные загружаются. Но при использовании этого я должен позвонить self.tableView.reloadData(), когда данные будут загружены, а сущности просто появятся в представлении таблицы без анимации, и я действительно хочу анимацию от insertRowsAtIndexPaths, потому что она выглядит лучше и лучше. Я попробовал позвонить reloadShowsArray(), а затем использовать функцию find() с сущностью, чтобы получить индекс, чтобы я мог использовать insertRowsAtIndexPaths, но каждый раз для индекса он возвращает nil, даже если объект был сохранен с контекстом до этого. Кроме того, ячейки не будут автоматически перезагружаться или перемещаться, как с помощью NSFetchedResultsController

Итак, каков наилучший способ справиться с этим, и как я могу получить нужную сортировку с помощью анимаций?

+0

Для варианта 1, что является «статусом» шоу в верхней части? Не могли бы вы сначала отсортировать «статус», чтобы получить верхний/средний/нижний в правильном порядке? Для варианта 2 у вас есть tableView.beginUpdates() и tableView.endUpdates() в других методах делегата FRC? Если нет, это может объяснить ошибки. И даже в этом случае вам может потребоваться реализовать счетчик, который увеличивается с каждым вызовом 'controller: willChangeContent' и уменьшается с каждым вызовом' controller: didChangeContent'. beginUpdates следует вызывать только в том случае, если счетчик равен нулю, и endUpdates только тогда, когда он возвращается к нулю. – pbasdf

+1

Также в варианте 2 ошибки могут быть связаны с dispatch_async, так как это может привести к обновлению таблицы вне цикла beginUpdates/endUpdates. – pbasdf

+0

Статус «возвращение серии» для лучших шоу, «в производстве» для средних шоу и «закончился»/«отменен» для нижних показов. Я не знаю, почему, но сортировка по тому, что раньше не работала так, как я хотел. Я могу попробовать еще раз, но у него была странная сортировка. Я также попробую счетчик, который может работать. Я использую dispatch_async там, потому что я думал, что это будет проще, чем сохранить контекст в каждом месте основного потока, потому что в сетевых запросах они не находятся на главном нить, поэтому просто убедитесь, что они вставлены правильно. – Maximilian

ответ

3

В соответствии с комментариями, я подозреваю, что метод с тремя FRC вызывает проблемы, поскольку один FRC вызывает controller:didChangeContent (который вызывает tableView.endUpdates), в то время как другой FRC все еще обрабатывает обновления. Чтобы преодолеть это, выполните счетчик, который увеличивается в controller:willChangeContent и уменьшается в controller:didChangeContent. Столбец beginUpdates должен вызываться только в том случае, если счетчик равен нулю, а endUpdates - только когда счетчик возвращается к нулю. Таким образом, endUpdates будет вызываться только тогда, когда все три FRC завершили обработку своих обновлений.

Если возможно, я бы также избегал dispatch_async, так как это может привести к обновлению таблицы, выходящей за цикл beginUpdates/endUpdates.

 Смежные вопросы

  • Нет связанных вопросов^_^