Ошибка утверждения в - [UITableView _endCellAnimationsWithContext:]

89

Надеюсь, это быстро исправит. Я пытаюсь выяснить, в чем заключается ошибка, которую я получаю. Ошибка указана ниже, а приложение - ниже.

Любая помощь приветствуется.

Благодарность

2012-04-12 21: 11: 52.669 Chanda [75100: f803] --- Ошибка утверждения в -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-1914.84/UITableView.m:1037 2012-04-12 21: 11: 52.671 Chanda [75100: f803] --- Завершение работы приложения из-за неперехваченного исключения ' NSInternalInconsistencyException' , причина: 'Недействительное обновление: недопустимое количество строк в разделе 0. Количество строк, содержащихся в существующем разделе после обновления (2), должно быть равно количеству строк, содержащихся в этом разделе перед обновлением (2), плюс или минус количество строк, вставленных или удаленных из этого раздела (1 вставлено, 0 удалено) и плюс или минус количество строк, перемещенных в этот раздел или из него (0 перемещено, 0 перемещено) ».

#import "AppDelegate.h"

@implementation AppDelegate

@synthesize window = _window;
@synthesize databaseName,databasePath; 

- (BOOL)application: (UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions {
    self.databaseName = @"Customers.db";

    NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentDir = [documentPaths objectAtIndex:0];
    self.databasePath = [documentDir stringByAppendingPathComponent:self.databaseName];
    [self createAndCheckDatabase];

    return YES;
}

- (void)createAndCheckDatabase {
    BOOL success;

    NSFileManager *fileManager = [NSFileManager defaultManager];
    success = [fileManager fileExistsAtPath:databasePath];

    if (success) return; 

    NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:self.databaseName];

    [fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
}

@end
Дурак с аквалангом
источник
3
Вам следует переместить этот код в классы и, возможно, выполнить его в фоновом потоке. В любом случае, эта ошибка обычно возникает, когда вы пытаетесь удалить строки без фактического уменьшения количества строк, предоставленных источником данных табличного представления. Вы действительно удаляете данные при удалении строк? Если вы не удаляете какие-либо строки, можете ли вы предоставить реализацию источника данных табличного представления?
Константино Царухас

Ответы:

43

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

Возможно, вы ошиблись в одном из этих методов источника данных. В настоящее время невозможно сказать, что именно не так, но я предполагаю, что это может быть что-то вроде: вы указываете представление таблицы в том, что numberOfRowsInSectionвы хотите, чтобы n строк были зарезервированы и настроены, а в этом случае cellForRowAtIndexPathвы обрабатываете только n - 1 строку, например.

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

АТС
источник
26

Как сказал Сунь-Цзы : лучше побеждать без борьбы . В моем случае всякий раз, когда я вижу такое сообщение об ошибке (например, несоответствие между добавленными строками, удаленными и т. Д.). Я даже ничего не отлаживаю. Я просто избегаю этого дополнительного вызова, когда я перезагружаю строки и т. Д., Это 99% случаи, когда возникает эта ошибка.

Это распространенный сценарий, при котором возникает эта ошибка: у меня есть UINavigationControllerи у меня есть UITableView, когда я нажимаю на строку, она выталкивает новую UITableViewи так далее. Эта ошибка всегда случается со мной, когда я открываю последнюю UITableviewи возвращаюсь к предыдущей UITableView, на этом этапе я делаю ненужный вызов loadItфункции, которая в основном вставляет строки и повторно загружает UITableView.

Причина этого в том, что я ошибочно помещаю свою функцию loadIt viewDidAppear:animatedвместо viewDidLoad. viewDidAppear:animatedвызывается каждый раз, когда UITableViewотображается, viewDidLoadвызывается только один раз.

Abbood
источник
3
Спасибо, хорошо сказано. Ваш первый абзац направил меня на верный путь к решению подобной проблемы.
scrrr
2
с удовольствием @scrrr :)
abbood
Это изменило мой взгляд на это - решило проблему, сделав ее неактуальной. Ты жжешь!
Чарли
Престижность viewDidAppear, а не viewDidLoad. Это была моя проблема, и я занимался этим неделями.
GrandSteph
1
Я распечатал вашу цитату Сунь Цзы и приклеил ее к верхней части экрана. Спасибо за цитату и за помощь в поиске тонны повторяющегося кода :)
Адриан,
24

При удалении строк помните, что он также проверяет разделы при обновлении, в:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)theTableView

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

Olof_t
источник
1
Это чистое золото! Гвоздь попал в голову! Собственно моя проблема.
phatmann
6

Не забудьте обновить свой массив, который определяет numberOfRowsInSection. Его необходимо обновить перед анимацией и удалением

Мы проверяем, равно ли количество строк в разделе 1, потому что нам придется удалить весь раздел.

Поправьте меня, если кто-нибудь сможет прояснить этот ответ.

[self.tableView beginUpdates];
if ([tableView numberOfRowsInSection:indexPath.section] == 1) {

   [tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
} else {

   [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
[self.tableView endUpdates];
love2script12
источник
4

Я помещаю элементы каждого раздела в отдельные массивы. Затем поместите их в другой массив (arrayWithArray). Мое решение этой проблемы:

[quarantineMessages removeObject : message];
[_tableView beginUpdates];
if([[arrayWithArray objectAtIndex: indPath.section] count]  > 1)
{
    [_tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indPath] withRowAnimation:UITableViewRowAnimationBottom];
}
else
{
    [_tableView deleteSections:[NSIndexSet indexSetWithIndex:indPath.section]
              withRowAnimation:UITableViewRowAnimationFade];
}
[_tableView endUpdates];
Боб
источник
1
для меня это были начальные и конечные обновления, которые решили эту проблему, спасибо!
Mark W
2
Я не знал, что раздел становится пустым и что вместо этого нужно его анимировать ... Черт! Благодарность!
двкч
1
Получил мой код для работы с большой помощью этого ответа. Публикуем ниже.
love2script12
2

У меня была такая же ошибка.

Я использовал следующие строки

UINib *myCustomCellNib = [UINib nibWithNibName:@"CustomNib" bundle:nil];
[tableView registerNib:myCustomCellNib forCellReuseIdentifier:@"CustomNib"];

чтобы зарегистрировать перо в методе viewDidLoad, поскольку у меня был другой наконечник, который также был связан с тем же классом. Следовательно, строка

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"GBFBLoadingCell"];

возвращал nil, если я не зарегистрировал перо в viewDidLoad.

Моя проблема заключалась в том, что я забыл установить идентификатор в инспекторе атрибутов для моего файла «CustomNib.xib» и «CustomNib ~ iphone.xib». (Или, точнее, я забыл нажать Enter после ввода идентификатора в инспекторе атрибутов в XCode, так что новое имя не удалось сохранить.)

Надеюсь это поможет.

user1612868
источник
2
Я НЕ думаю, что ваш ответ имеет отношение к проблеме.
DawnSong
2

Если вы используете NSFetchedResultsControllerподобное мне и обновляете данные в фоновом потоке, не забудьте начинать и заканчивать обновления в делегате:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView beginUpdates];
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView endUpdates];
}
Микехо
источник
2

У меня была такая же ошибка, которая при попытке [tableView reloadData]сработала нормально. Ошибка действительно была в строке

[TabView insertRowsAtIndexPaths:indexPathsArray withRowAnimation:UITableViewRowAnimationRight];

Когда я попытался проверить значения indexPath, они оказались неправильными.

Я исправил это, изменив значения в indexPathsArray.

Надеюсь это поможет .

user5553647
источник
1

Это может быть один из UITableViewDataSourceметодов протокола

За

- tableView:numberOfRowsInSection:

он должен возвращать целое число, равное сумме или результату

-insertRowsAtIndexPaths:withRowAnimation:и / или -deleteRowsAtIndexPaths:withRowAnimation:

За

- numberOfSectionsInTableView:

он должен возвращать целое число, равное сумме или результату

-insertRowsAtIndexPaths:withRowAnimation: и / или -deleteSections:withRowAnimation:

Тед
источник
0

У меня была такая же проблема с базой данных Core Data. Если вы используете много FRC, вам просто нужно перезагрузить tableview внутри каждого условия в numberOfSectionsInTableView.

Privatus Privatus
источник
2
У меня тоже есть проблема с Core Data. Не могли бы вы подробнее рассказать о своем рецепте.
Drux
0

Это случилось со мной, когда я использовал Swift и хранилище данных с поддержкой FRC для управления информацией. Добавление простой проверки к операции удаления для оценки текущего indexPath.section позволило мне избежать посторонних вызовов. Думаю, я понимаю, почему возникает эта проблема ... Обычно я загружаю сообщение в верхнюю строку всякий раз, когда мой набор данных пуст. Это создает проблему с ошибкой на единицу, поскольку есть ложная строка.

Мое решение

... delete my entity, save the datastore and call reloadData on tableView
//next I add this simple check to avoid calling deleteRows when the system (wrongly) determines that there is no section.

       if indexPath.section > 0 {

        tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .None)

            }
Томми К.
источник
0

Просто убедитесь, что вы вызываете [yourTableView reloadData]; после модификации массива значений.

Евгений Клебан
источник