Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to reload programmatically moved row?

Frustrating fact: After calling tableView:moveRowAtIndexPath:toIndexPath:, tableView:cellForRowAtIndexPath: doesn't get called for that row.

Call reloadRowsAtIndexPaths:withRowAnimation: after or before tableView:moveRowAtIndexPath:toIndexPath: within UITableView updates block also doesn't work: it raises Inconsistency error.

I see 2 workarounds: delete+insert instead of move or do reload within another updates block.

My question is: is there some other way to reload and move UITableView's row within same updates block?

like image 938
folex Avatar asked Aug 06 '13 15:08

folex


3 Answers

I ran into a similar problem when sorting on "dateModified" but I was also displaying that property on the cell's label. Both "move" and "update" were required. "move" was only called so the correct cell was brought to the top of the list, but the label text didn't update.

My solution for a simple UITableViewCell.

First, you make call .move as normal. Directly after you make a call to a custom configure method — which is responsible for animating the "update" on the cell.

 func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
    switch type {
    case .insert:
        tableView.insertRows(at: [newIndexPath!], with: .fade)
    case .delete:
        tableView.deleteRows(at: [indexPath!], with: .fade)
    case .update:
        tableView.reloadRows(at: [indexPath!], with: .fade)
    case .move:
        tableView.moveRow(at: indexPath!, to: newIndexPath!)

        // Can't "reload" and "move" to same cell simultaneously.

        // This is an issue because I'm sorting on date modified and also displaying it within a
        // label in the UITableViewCell.

        // To have it look perfect you have to manually crossfade the label text, while the UITableView
        // does the "move" animation.

        let cell = tableView.cellForRow(at: indexPath!)!
        let note = fetchedResultsController.object(at: newIndexPath!)
        configure(cell: cell, note: note, animated: true)
    }
}

The configure method looks like this (note animated is optional):

internal func configure(cell: UITableViewCell, note: Note, animated: Bool = false) {
    let dateFormatter = DateFormatter()
    dateFormatter.dateStyle = .medium
    dateFormatter.timeStyle = .medium
    let dateString = dateFormatter.string(from: note.dateModified as Date)

    if animated {
        UIView.transition(with: cell.contentView, duration: 0.3, options: .transitionCrossDissolve, animations: {
            cell.textLabel?.text = dateString
        }, completion: nil)
    } else {
        cell.textLabel?.text = dateString
    }
}

Reuse the configure method here without animation:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: CellReuseIdentifier, for: indexPath)
    let note = fetchedResultsController.object(at: indexPath)

    configure(cell: cell, note: note)

    return cell
}

If you have a more complicated cell (subclass), you could probably move the configure method to the subclass code. The important part is having a method to update the cell data with animation optional.

like image 155
Michael Rose Avatar answered Nov 14 '22 12:11

Michael Rose


[_tableview beginUpdates];

// write code here to delete and move row...

[_tableview endUpdates];

 // now after end update call reload method to reload cell..  

    [self.tableview reloadRowsAtIndexPaths:[NSArray arrayWithObjects:
    [NSIndexPath indexPathForRow:_arrayForData.count-1 inSection:indexpathforSelectedRow.section], nil] withRowAnimation:UITableViewRowAnimationNone];
like image 39
user3306145 Avatar answered Nov 14 '22 12:11

user3306145


I do not think it is possible to do a move and a reload at the same time. I've tried several approaches and the best solution I've come up with is to do the reload just before the batch updates. I don't animate reloadRows because it seems to conflict with the batch update animations.

[tableView reloadRowsAtIndexPaths:indexPathsToReload withRowAnimation:UITableViewRowAnimationNone];
[tableView beginUpdates];
//inserts, deletes and moves here
[tableView endUpdates];

Also, I typically put my cell configuration logic in a separate method like:

- (void)tableView:(UITableView *)tableView configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;

So that I can just call it directly and bypass reloadRowsAtIndexPaths altogether. You won't get the built in animation this way either, but you can do your own animations.

like image 31
Timothy Moose Avatar answered Nov 14 '22 12:11

Timothy Moose