Pergunta

Os docs para NSFetchedResultsControllerDelegate fornecer o seguinte código de exemplo

- (void)controller:(NSFetchedResultsController *)controller
   didChangeObject:(id)anObject
       atIndexPath:(NSIndexPath *)indexPath
     forChangeType:(NSFetchedResultsChangeType)type
      newIndexPath:(NSIndexPath *)newIndexPath {

    UITableView *tableView = self.tableView;

    switch(type) {

        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeUpdate:
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;

        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
            break;

    }

}

Quando eu criar uma nova NSManagedObject, NSFetchedResultsChangeInsert incêndios (great!). Quando eu mudar o valor de um atributo (usado para o título da célula), o NSFetchedResultsChangeUpdate incêndios. Infelizmente, o novo título não exibir automaticamente a menos que eu recarregar a mesa, seção ou linha. De fato, se o novo nome faz com que o conjunto de resultados de tipo diferente, em seguida, NSFetchedResultsChangeMove incêndios e tudo está bem desde que recarrega código fornecidos toda a seção.

UITableView tem um método reloadRowsAtIndexPaths: withRowAnimation , então eu tentei usar isso sob a NSFetchedResultsChangeUpdate bloco de código. Ele faz de fato o trabalho ... mas a documentação para este método específico ler como se eu não preciso disso (aviso a última linha):

Recarregando uma linha faz com que o exibição de tabela para pedir a sua fonte de dados para uma nova célula para essa linha. A mesa anima que a nova célula, uma vez que anima a linha antiga fora. Chame esse método se você deseja alertar o usuário que o valor de uma célula está a mudar. Se, no entanto, notificando o usuário não é importante, ou seja, você só quer alterar o valor que uma célula é exibindo-você pode obter a célula para uma linha particular e definir o seu novo valor.

E sim, se eu registrar o que está acontecendo, quando

[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; 

é invocado em um NSFetchedResultsChangeUpdate , é capaz de recuperar o valor mais recente 'nome' e colocá-lo em textLabel da célula. O nome simplesmente não está prestando na célula a menos que eu recarregá-lo. Mesmo se eu simplesmente clique na célula o nome aparece. Note-se que para recriar esse comportamento, você deve criar um novo objeto gerenciado e, em seguida, dar-lhe um nome que faz com que ele para classificar em primeiro lugar no NSFetchedResultsController. Dessa forma, o NSFetchedResultsChangeMove não fogo (que faz o trabalho uma vez que recarrega a seção).

Estou faltando alguma coisa ou é esse comportamento esperado? A 'discussão' para reloadRowsAtIndexPaths me leva a crer que eu deveria ser capaz de simplesmente definir textLabel da célula sem recarregar a linha, seção ou mesa.

Foi útil?

Solução

Você deve chamar [cell setNeedsLayout] e / ou [cell setNeedsDisplay] para fazer com que o celular para se refrescado, dependendo da implementação do seu celular.

Se você compõe a célula de subviews como costumamos fazer, você confiar em - layoutSubviews, então você deve chamar [cell setNeedsLayout].

Se você desenhar a célula diretamente com – drawRect:, você deve chamar [cell setNeedsDisplay].

Se você usar tanto a composição e desenho, você deve ligar para ambos.

Outras dicas

Embora seja verdade que você não precisa recarregar o celular, a fim de que as alterações tenham lugar, você tem que lembrar que as caches iPhone vista de desenho, tanto quanto possível. Depois de ter configurado recentemente o seu celular, você precisa chamar setNeedsDisplay na célula, a fim de acionar o redesenho.

Embora não seja sido explicitamente indicado, você realmente tem que se certificar de que você incluir os métodos de delegado para controllerWillChangeContent: e controllerDidChangeContent:

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

e

- (void)controllerDidChangeContent:(BSFetchedResultsController *)controller {
    @try {
        [self.tableView endUpdates];
    }
    @catch (NSException * e) {
        NSLog(@"caught exception: %@: %@", [e name], [e description]);
    }
    @finally { }
}

O NSFRC pode disparar controlador múltipla: didChangeObject: atIndexPath: forChangeType: newIndexPath: métodos durante qualquer mudança, por isso não esperamos a linha da tabela para atualizar logo após cada um desses. Eles só vou atualizar depois que você chamar endUpdates sobre a mesa.

Chamando a lógica UI atualização em thread principal resolvido o meu problema (ambos [cell setNeedsLayout] & [cell setNeedsDisplay] não funciona para mim):

...
case NSFetchedResultsChangeUpdate:
    dispatch_async(dispatch_get_main_queue(), {
      [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
    });
    break;
...

O que é mais (e não sobre esta questão, mas será útil), é melhor optar por usar o newIndexPath se ele está disponível:

...
case NSFetchedResultsChangeUpdate:
    dispatch_async(dispatch_get_main_queue(), {
      NSIndexPath * targetIndexPath = (newIndexPath ?: indexPath)
      [self configureCell:[tableView cellForRowAtIndexPath:targetIndexPath]
              atIndexPath:targetIndexPath];
    });
    break;
...
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top