2016-01-28 2 views
9

Полноэкранный просмотр таблицы iPad-only app. Я включил удаление удаленных строк в своих строках. Анимация строк всегда заканчивается после удаления (commitEditingStyle завершает), но изредка просматривается весь вид таблицы. Не весь пользовательский интерфейс, заметьте, так что это не заблокированный основной поток. Я могу нажать заголовок столбца или нажать кнопку «Назад» на навигационном контроллере, но сама таблица блокируется и не может быть пропущена. Я могу разморозить его довольно просто, нажав одну из моих кнопок заголовка столбца.UITableView замерзает после прокрутки, чтобы удалить, но не весь пользовательский интерфейс

enter image description here

Я просто в полном недоумении для того, что может быть причиной замораживания. Я использую NSFetchedResultsController, и вот для этого мой код делегата. Это довольно плита котла (Update: не в качестве котельного листового металла в настоящее время, используя дозирующий подход.):

// MARK: NSFetchedResultsController delegate methods 

lazy var deletedSectionIndexes : NSMutableIndexSet = { 
    return NSMutableIndexSet() 
}() 

lazy var insertedSectionIndexes : NSMutableIndexSet = { 
    return NSMutableIndexSet() 
}() 

lazy var deletedRowIndexPaths : [NSIndexPath] = { 
    return [NSIndexPath]() 
}() 

lazy var insertedRowIndexPaths : [NSIndexPath] = { 
    return [NSIndexPath]() 
}() 

lazy var updatedRowIndexPaths : [NSIndexPath] = { 
    return [NSIndexPath]() 
}() 


func controllerWillChangeContent(controller: NSFetchedResultsController) { 

} 

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { 
    switch(type) { 

    case .Delete: 
     if let indexPath = indexPath { 
      self.deletedRowIndexPaths.appendDistinct(indexPath) 
     } 
    case .Update: 
     if let indexPath = indexPath { 
      self.updatedRowIndexPaths.appendDistinct(indexPath) 
     } 
    case .Insert: 
     if let newIndexPath = newIndexPath { 
      self.insertedRowIndexPaths.appendDistinct(newIndexPath) 
     } 
    case .Move: 
     if let indexPath = indexPath, newIndexPath = newIndexPath { 
      self.insertedRowIndexPaths.appendDistinct(newIndexPath) 
      self.deletedRowIndexPaths.appendDistinct(indexPath) 
     } 
    } 
} 

func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { 
    switch(type) { 

    case .Delete: 
     self.deletedSectionIndexes.addIndex(sectionIndex) 
    case .Insert: 
     self.insertedSectionIndexes.addIndex(sectionIndex) 
    default: 
     break 
    } 
} 

func controllerDidChangeContent(controller: NSFetchedResultsController) { 
    self.tableView.beginUpdates() 
    self.tableView.insertSections(self.insertedSectionIndexes, withRowAnimation: .None) 
    self.tableView.deleteSections(self.deletedSectionIndexes, withRowAnimation: .None) 

    self.tableView.insertRowsAtIndexPaths(self.insertedRowIndexPaths, withRowAnimation: .None) 
    self.tableView.deleteRowsAtIndexPaths(self.deletedRowIndexPaths, withRowAnimation: .None) 
    self.tableView.reloadRowsAtIndexPaths(self.updatedRowIndexPaths, withRowAnimation: .None) 
    self.tableView.endUpdates() 

    self.insertedSectionIndexes.removeAllIndexes() 
    self.deletedSectionIndexes.removeAllIndexes() 
    self.deletedRowIndexPaths.removeAll() 
    self.insertedRowIndexPaths.removeAll() 
    self.updatedRowIndexPaths.removeAll()   
} 

Функция удаления вызывается в методе делегата didChangeObject, однако, технически это не реальное удаление. Я просто устанавливаю свойство -1, а затем сохраняю этот элемент через NSMangagedObjectContext - в этот момент NSFRC, кажется, делает правильные вещи, которые удаляют его из списка извлеченных объектов, которые были извлечены с использованием этого предиката:

NSPredicate(format: "account = %@ and quantity != -1", account) 

, где account - действительный объект, управляемый учетной записью. Строка исчезает без проблемы 90% или более времени. Иногда случается, что после завершения анимации таблица замораживается в усадьбе, которую я описал. Он никогда не зависает, пока кнопка удаления все еще отображается, поэтому я знаю, что это происходит после вызова commitEditingStyle. Кнопка удаления не имеет настраиваемой реализации. Это удаленная реализация UITableView по умолчанию для удаления. Вот мой commitEditingStyle метод:

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { 
    if editingStyle == .Delete { 
     if let frameboardItem = self.fetchedResultsController.objectAtIndexPath(indexPath) as? IRMFrameBoardItemMO { 
      if frameboardItem.isNew { 
       // If it's never been pushed to the server, just delete locally. This will trigger a table reload 
       // via NSFetchedResultsController 
       DataManager.mainContext.deleteObject(frameboardItem) 
      } else { 
       // Otherwise mark it with a negative quantity which tells the server to delete it and tells the 
       // app to hide it. 
       frameboardItem.quantity = -1 
      } 

      do { 
       try DataManager.mainContext.save() 
      } catch let error as NSError { 
       dLog("Something went wrong: \(error.localizedDescription)") 
      } 

     } 

    } 

} 

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

https://vimeo.com/153406113

Хотелось бы услышать какие-либо предложения.

Update

Я обновил методы делегата NSFRC использовать дозирующий подход для обеспечения обновления получить применяется сразу. Это не решило проблему. Стол периодически замерзает.

+0

Вы уверены, что источник данных обновляется правильно? Есть ли когда-либо ситуация, когда 'frameboardItem' является' nil' в следующем выражении: 'if let frameboardItem = self.fetchedResultsController.objectAtIndexPath (indexPath) как? IRMFrameBoardItemMO'? И если это «nil», обновляется ли таблица без обновления источника данных? – Alexander

+0

Я смотрел видео несколько раз, где вы удалялись, и это зависало. это похоже на то, когда вы удаляете верх и низ из одного раздела, а затем меняете разделы. и удалите верх и низ. Или, может быть, это всего лишь один или два отдельных раздела. Это заставляет меня задаться вопросом, есть ли проблемы с подсчетом списка или как они перемещаются. это удерживает пользовательский интерфейс таблицы. Было бы хорошо, если бы вы могли сузить его до точности его воспроизводимости. Просто вслух. –

+0

1) Является ли tableView в контроллере дочернего представления? 2) Если в целях тестирования вы меняете одну из кнопок (которая реагирует на касания), так что она вызывает прокрутку телевизора вверх (или снизу), действительно ли она прокручивается? Это может дать представление о том, является ли телевизор просто игнорированием прикосновений или замерзает при попытке прокрутки. – pbasdf

ответ

2

У меня также есть предположение об этой проблеме. Моя идея состоит в том, что controllerDidChangeContent можно вызывать дважды или более раз и быстрее, чем обновление таблицы, и эта причина нескольких вызовов tableView.beginUpdates(), которые могут повесить таблицу.

Так, чтобы исправить это я предлагаю обновление обруча в dispatch_async блоке, или просто простой логический флаг

func controllerDidChangeContent(controller: NSFetchedResultsController) { 
    dispatch_async(dispatch_get_main_queue(), {() -> Void in 
     self.tableView.beginUpdates() 
     // ..... rest of update code 
     self.updatedRowIndexPaths.removeAll() 
    }) 
} 
+0

Так что же это делает именно тогда? Затем обновления ставятся в очередь? –

+0

@MattLong да, обновления выполняются последовательно в очереди – sage444

+0

К сожалению, это не совсем серебряная пуля, однако она довольно близка. Я воспроизвел замораживание даже с использованием этой техники, но частота очень минимальная - почти никогда. Я принимаю ваш ответ правильно. Спасибо, что принял удар в темноте. Я ценю это. –

0

Использовать блоки.

Непонятно, с каким потоком находится MOC, хотя, поскольку вы используете fetchedResultsController, это, вероятно, является основным.

Таким образом, вы можете выполнить

  • deleteObject
  • save

performBlockAndWait в засаде. Это может помочь гарантировать целостность данных.Что-то вдоль линий:

DataManager.mainContext.performBlockAndWait {() -> Void in 
    DataManager.mainContext.deleteObject(frameboardItem) 
    if DataManager.mainContext.hasChanges { 
     do { 
      try DataManager.mainContext.save() 
     } catch let error as NSError { 
      dLog("Something went wrong: \(error.localizedDescription)") 
     } 
    } 
} 
0

Я не думаю, что TableView замерзает из-за проблемы с памятью или несбалансированными начинаются */endEditing вызовов (исключение будет брошено или сигнал будет отправлен).

Я думаю, что это может быть материал на нитке, кроме основной нити. В таком случае даже блоки не помогут. (Установите точку останова и проверьте, какая нить остановлена ​​..., также: тест на реальном устройстве)

Моя идея исправить это, попробуйте что-то другое, например, добавление данных для добавления или удаления во временный массив и обновление TableView в рамках одного запуска метода (вызов этого метода для явного запуска в основном потоке после того, как ваш контроллер результатов выборки завершил свои вызовы делегатов).

0

Вы пытались реализовать делегат NSFetchedResultsControllerDelegate в более общем смысле, я имею в виду начать обновление таблицы, когда fetchedResultController просит , делать обновления и затем завершать обновление?

func controllerWillChangeContent(controller: NSFetchedResultsController) { 
    self.tableView.beginUpdates() 
} 

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { 
    /* update table here */ 
} 

func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { 
    /* update table here */ 
} 

func controllerDidChangeContent(controller: NSFetchedResultsController) {  
    self.tableView.endUpdates() 
} 

UPADATE:
Можно ли, что когда вы отмечаете объект как удален ", это вызывает некоторые более сложную цепь изменения объекта, который в свою очередь, вызовет didChangeObject функцию можно назвать нескольких раз ? Вы проследили, сколько раз вызывалась функцияChangeObject во время одиночной маркировки удаления?

+0

Да. Так было до переключения на пакетное обновление. См. Вопрос для деталей. –

+0

@MattLong yeap, извините, я пропустил это. –