Перезагрузить данные TableView после нажатия кнопки - PullRequest
3 голосов
/ 22 марта 2012

Я создаю приложение чата и читаю данные сообщений из базы данных SQLite. Я хочу перезагрузить мои данные TableView с новыми сообщениями после нажатия на кнопку «отправить». Событие для моей кнопки:

    [_sendButton addTarget:self action:@selector(saveMessage:) forControlEvents:UIControlEventTouchUpInside];

Я думаю, мне нужно использовать -(void)viewDidAppear:(BOOL)animated, но это не сработало (или я делаю что-то не так).

[_tableView reloadData];

-(void)viewDidAppear:(BOOL)animated {

sqlite3 *database;

databaseName = @"Messenges.db";
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];

messenges = [[NSMutableArray alloc] init];

if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
    const char *sqlStatement = "select * from messenges";
    sqlite3_stmt *compiledStatement;
    if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
        while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
            NSString *dbMessageText = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];

            Message *messege = [[Message alloc] initWithName:dbMessageText];

            [messenges addObject:messege];

            [messege release];
        }
    }
    sqlite3_finalize(compiledStatement);

}
sqlite3_close(database);}

Редактирование Это мой метод добавления новой строки в TableView. Новая строка должна быть добавлена ​​сразу после нажатия кнопки «Отправить».

-(IBAction)insertRows:(id)sender {
    MDAppDelegate *appDelegate = (MDAppDelegate *)[[UIApplication sharedApplication] delegate];
    [messenges insertObject:[[NSString alloc] initWithFormat:@"%@", _textField.text] atIndex:appDelegate.messenges.count+1];
    NSArray *insertIndexPaths = [NSArray arrayWithObject: [NSIndexPath indexPathForRow:1 inSection:1]];
    UITableView *tV = (UITableView *)self.tableView;

    [tV beginUpdates];
    [tV insertRowsAtIndexPaths:insertIndexPaths withRowAnimation:UITableViewRowAnimationRight];
    [tV endUpdates];}

Но этот код выдает мне ошибку: 'Invalid table view update. The application has requested an update to the table view that is inconsistent with the state provided by the data source.' Как я могу это исправить?

Edit2

Ok. У меня есть только 1 раздел. numberOfSectionsInTableView: не требует коррекции.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    MDAppDelegate *appDelegate = (MDAppDelegate *)[[UIApplication sharedApplication] delegate];
    NSUInteger numberOfRowsInSection = appDelegate.messenges.count; //add new variable to what rows will be +1
    if (self.editing) {
        numberOfRowsInSection++;
    }
    return numberOfRowsInSection;}

Но у меня все та же ошибка.

Редактировать # 3

Давайте посмотрим. Я изменил свой insertRows на:

-(IBAction)insertRows:(id)sender {
    MDAppDelegate *appDelegate = (MDAppDelegate *)[[UIApplication sharedApplication] delegate];
    [self.tableView beginUpdates];
    self.tableView.editing = YES;
    [messenges insertObject:[[NSString alloc] initWithFormat:@"%@", _textField.text] atIndex:appDelegate.messenges.count+1];

    NSArray *insertIndexPaths = [NSArray arrayWithObject: [NSIndexPath indexPathForRow:appDelegate.messenges.count+1 inSection:1]];
    [self.tableView insertRowsAtIndexPaths:insertIndexPaths withRowAnimation:UITableViewRowAnimationRight];
    [self.tableView endUpdates];
}  

И numberOfRowsInSection:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    MDAppDelegate *appDelegate = (MDAppDelegate *)[[UIApplication sharedApplication] delegate];
    NSUInteger numberOfRowsInSection = appDelegate.messenges.count;
    if (self.tableView.editing) {
           numberOfRowsInSection++;
    }
    NSLog(@"%i",numberOfRowsInSection);
    return numberOfRowsInSection;
}

Но я получил ошибку *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 4 beyond bounds [0 .. 3]'. Затем я исправил if (self.tableView.editing):

if (self.tableView.editing) {
    MDAppDelegate *someNewDelegate = (MDAppDelegate *)[[UIApplication sharedApplication] delegate];
    numberOfRowsInSection = someNewDelegate.messenges.count+1; //or .count
}

Но получена та же ошибка с «NSRangeException».

1 Ответ

7 голосов
/ 27 марта 2012

Чтобы обновить UITableView с анимацией, вам необходимо:

  1. Вызвать beginUpdates на вашем UITableView
  2. Обновление модели данных
  3. Вставка / удаление строк и разделов
  4. Вызов endUpdates на вашем UITableView

Несколько вещей, о которых следует помнить:

  • Если вы удаляете раздел, вы не должны также удалять строки из этого раздела (ненужные и даже могут вызвать ошибки)
  • Если вы вставляете раздел, вы не должны также вставлять строки в этот раздел (UITableView вызовет numberOfRowsInSection:, за которым следует cellForRowAtIndexPath:, если ячейка видна).

Что вы имеете в виду, обновить мою модель данных?

Если вы манипулируете UITableViewдобавляя и удаляя строки и разделы, вы должны иметь возможность отслеживать, какие разделы / строки в данный момент находятся в UITableView в любой данный момент.

Например,Таким образом, у вас должен быть метод numberOfRowsInSection:, который похож на этот:

- (NSInteger)numberOfRowsInSection:(NSInteger)section
{
    // You should have a way to get section data with an index (section)
    // The below example assumes an NSArray named self.mySectionCollection
    // exists for this purpose
    NSArray *sectionList = self.mySectionCollection;
    // You should have data associated with a section.  Below its assumed
    // this data is wrapped in an NSDictionary.  Data that might be in here
    // could include a height, a name, number of rows, etc...
    NSDictionary *sectionData = [sectionList objectAtIndex:section];

    NSInteger rowCount = 0;
    // SectionData should NEVER be nil at this point.  You could raise an
    // exception here, have a debug assert, or just return 0.
    if (sectionData)
    {
        rowCount = [[sectionData objectForKey:@"rowCount"] intValue];
    }
    return rowCount;
}

Вы можете хранить информацию о разделах / строках несколькими различными способами, включая использование CoreData.Дело в том, что, изменяя эту коллекцию в промежутке между beginUpdates и endUpdates, вы сообщаете UITableView, как обновить себя (он запроситnumberOfRowsInSection:, numberOfSections и cellForRowAtIndexPath: по мере необходимости).

Редактировать

Я полагаю, еслиВы изменяете свой метод insertRows: для изменения источника данных (messenges) в то же время, когда вы уведомляете UITableView о том, что произошли обновления, и все начнет работать правильно.

Попробуйте использовать этот код:

-(IBAction)insertRows:(id)sender 
{
   MDAppDelegate *appDelegate = (MDAppDelegate *)[[UIApplication sharedApplication] delegate];
   UITableView *tV = (UITableView *)self.tableView;

   [tV beginUpdates];

   // UPDATE DATA MODEL IN BETWEEN beginUpdates and endUpdates
   int rowIndex = appDelegate.messenges.count + 1;
   [messenges insertObject:[[NSString alloc] initWithFormat:@"%@", _textField.text] 
                   atIndex:rowIndex];

   // Notify UITableView that updates have occurred
   NSArray *insertIndexPaths = [NSArray arrayWithObject: 
                                [NSIndexPath indexPathForRow:rowIndex inSection:1]];
   [tV insertRowsAtIndexPaths:insertIndexPaths 
             withRowAnimation:UITableViewRowAnimationRight];

   [tV endUpdates];
}

Edit # 2

Если у вас все еще есть проблемы, я бы посмотрел, где вы устанавливаете флаг self.editing.

- (NSInteger)tableView:(UITableView *)tableView 
 numberOfRowsInSection:(NSInteger)section 
{
    MDAppDelegate *appDelegate = (MDAppDelegate *)[[UIApplication sharedApplication] delegate];
    //add new variable to what rows will be +1
    NSUInteger numberOfRowsInSection = appDelegate.messenges.count; 
    if (self.editing) 
    {
        numberOfRowsInSection++;
    }
    return numberOfRowsInSection;
}

Этот флагопределяет, существует ли в таблице дополнительная строка или нет.По этой причине вы должны установить его между beginUpdates и endUpdates, например:

// assuming the editing flag is set from some IBAction
-addNewRow:(id)sender
{
   int row = ???;  // not sure where you want this new row

   [self.tableView beginUpdates]
   self.editing = YES;
   NSArray *insertIndexPaths = [NSArray arrayWithObject: 
                                [NSIndexPath indexPathForRow:row inSection:1]];
   [self.tableView insertRowsAtIndexPaths:insertIndexPaths
                         withRowAnimation:UITableViewRowAnimationRight];
   [self.tableView endUpdates];
}

Не забудьте аналогичным образом вызвать deleteRowsAtIndexPaths:withRowAnimation:, если пользователь прекращает редактирование, если вы удаляете добавляемую строку.Я считаю, что никаких действий не требуется, если редактирование становится постоянным, но вам нужно будет установить self.editing = NO и добавить новую строку в нужное место в self.messenges.

Также в своем методе insertRows: вы сообщаете UITableView, что вставляете строку с индексом 1, тогда как на самом деле вы всегда вставляете в конец коллекции messenges.Я изменил свою версию метода insertRows, чтобы он имел переменную rowIndex, чтобы обеспечить использование того же индекса при обновлении модели данных и информировании UITableView об изменении.

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

Редактировать # 3

Это ответ на ошибку, которую вы видите:

* Завершение работы приложения из-за необработанного исключения 'NSRangeException', причина: '* - [__ NSArrayM objectAtIndex:]: индекс 4 за пределами [0 .. 3]'

Ваша ошибка не имеет ничего общего со вставкой в ​​UITableView.Это связано с тем, что вы пытаетесь вставить за пределы массива.Я предполагаю, что неправильная строка:

[messenges insertObject:[[NSString alloc] initWithFormat:@"%@", 
                          _textField.text] 
                atIndex:appDelegate.messenges.count+1];

Чтобы вставить в конец массива, используйте индекс appDelegate.messenges.count (убрал +1).

Также ... Вы абсолютно уверены, что ваша модель данных и вызовы для обновления UITableView согласны? Попробуйте вызвать NSLog(@"rowsBeforeUpdate: %i", [self numberOfRowsInSection:0]); сразу после вызова beginUpdates и непосредственно перед вызовом endUpdates. Кроме того, вызовите NSLog(@"inserting index paths: %@", insertIndexPaths) при информировании UITableView об операции вставки. Я предполагаю, что self.messenges точно отражает строки, которые должны быть в таблице, но добавляя +1, когда self.tableView.editing имеет значение true, вы нажимаете numberOfRowsInSection: над тем, что вы сообщаете в своих вызовах, на insertRowsAtIndexPaths:withRowAnimation:.

Попробуйте это:

-(IBAction)insertRows:(id)sender {
    MDAppDelegate *appDelegate = (MDAppDelegate *)[[UIApplication sharedApplication] delegate];
    [self.tableView beginUpdates];
    NSLog(@"rows before update: %i", [self numberOfRowsInSection:0]);

    self.tableView.editing = YES;
    NSLog(@"rows with editing flag set: %i", [self numberOfRowsInSection:0]);

    int rowIndex = appDelegate.messenges.count;  // don't need +1 at end
    int sectionIndex = 0;  // should it be 0 or 1?  I would guess 0
    [messenges insertObject:[[NSString alloc] initWithFormat:@"%@", _textField.text] 
                    atIndex:rowIndex];

    NSArray *insertIndexPaths = [NSArray arrayWithObject:
                                 [NSIndexPath indexPathForRow:rowIndex 
                                                    inSection:sectionIndex]];
    [self.tableView insertRowsAtIndexPaths:insertIndexPaths 
                          withRowAnimation:UITableViewRowAnimationRight];
    NSLog(@"inserting index paths: %@", insertIndexPaths);

    [self.tableView endUpdates];
}

Я изменил индекс строки выше, чтобы исключить +1 в нем. Я изменил индекс раздела на 0, что должно быть правильным, если только этот UITableView не имеет несколько разделов, которые не были упомянуты. Убедитесь, что ваши логи имеют смысл. Если вы увидите, что numberOfRowsInSection: увеличится на два в течение insertRows:, тогда вам также лучше увидеть, как insertRowsAtIndexPaths: отражает то же самое . Я запутался, зачем вам нужен флаг редактирования, чтобы добавить еще одну строку в ваш метод numberOfRowsInSection:. Эта часть (где вы добавляете дополнительную строку, если установлена ​​self.editing), похоже, не работает правильно. Возможно, просто попробуйте пропустить эту часть, чтобы некоторые из них работали, а затем добавьте ее обратно, как только у вас все будет работать правильно. Возможно, измените numberOfRowsInSection: на следующий, пока некоторые вещи не начнут работать:

- (NSInteger)tableView:(UITableView *)tableView 
 numberOfRowsInSection:(NSInteger)section 
{
    MDAppDelegate *appDelegate = (MDAppDelegate *)[[UIApplication sharedApplication] delegate];
    NSUInteger numberOfRowsInSection = appDelegate.messenges.count;
    NSLog(@"numberOfRowsInSection %i: %u", section, numberOfRowsInSection);
    return numberOfRowsInSection;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...