Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITableView, replace cell view on selection

Is it possible to replace a cell view for the selected cell? I have registered my controller as a data source and delegate. Requests for cell views, row numbers, selection status, etc, all come in nicely. In the cell selection callbacks, I'm trying to have the table view reload the cell with a new view (not the actual data!). Basically, I want the cell view to expand and show more of the underlying data, if it is selected. The problem is that I have no clue how to make the table view ask my controller for a new view. I can see that the view is requesting the cell height, but nothing more.

Calling reloadData on the view works, but it's inefficient and comes with a set of its own problems (animation possibilities, maintaining selection state, etc).

like image 943
Jörgen Sigvardsson Avatar asked Jan 31 '13 15:01

Jörgen Sigvardsson


2 Answers

Here is the approach I would take to do this.

@property (nonatomic, strong) NSIndexPath *selectedIndexPath;

Set a property of type NSIndexPath to your controller to store which index path was selected.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    self.selectedIndexPath = indexPath;
    [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation: UITableViewRowAnimationNone];
}

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

    if ([indexPath compare:self.selectedIndexPath] == NSOrderedSame) {
        // Create your custom cell here and return it.
    }
    else {
        // Should create a normal cell and return it.
    }
}

Exactly. Note too that you probably want to deselect. Here's the full code in Swift. Use selectedIndexPath in cellForRowAtIndexPath as appropriate.

// Selecting TableViewController import UIKit

class SelectingTableViewController: UITableViewController
{ internal var selectedIndexPath:NSIndexPath? = nil

override func viewDidLoad()
    {
    super.viewDidLoad()
    tableView.estimatedRowHeight = 68.0
    tableView.rowHeight = UITableViewAutomaticDimension

    self.clearsSelectionOnViewWillAppear = false;
    }

override func tableView
    (tableView:UITableView, didSelectRowAtIndexPath indexPath:NSIndexPath)
        {
        print("did select....")

        // in fact, was this very row selected,
        // and the user is clicking to deselect it...
        // if you don't want "click a selected row to deselect"
        // then on't include this clause.
        if selectedIndexPath == indexPath
            {
            print("(user clicked on selected to deselect)")
            selectedIndexPath = nil
            tableView.reloadRowsAtIndexPaths(
                [indexPath],
                withRowAnimation:UITableViewRowAnimation.None)

            tableView.deselectRowAtIndexPath(indexPath, animated:false)
            return
            }

        // in fact, was some other row selected??
        // user is changing to this row? if so, also deselect that row
        if selectedIndexPath != nil
            {
            let pleaseRedrawMe = selectedIndexPath!
            // (note that it will be drawn un-selected
            // since we're chaging the 'selectedIndexPath' global)
            selectedIndexPath = indexPath
            tableView.reloadRowsAtIndexPaths(
                [pleaseRedrawMe, indexPath],
                withRowAnimation:UITableViewRowAnimation.None)
            return;
            }

        // no previous selection.
        // simply select that new one the user just touched.
        // note that you can not use Apple's willDeselectRowAtIndexPath
        // functions ... because they are freaky
        selectedIndexPath = indexPath
        tableView.reloadRowsAtIndexPaths(
            [indexPath],
            withRowAnimation:UITableViewRowAnimation.None)

        }

}
like image 63
Simon Germain Avatar answered Sep 20 '22 11:09

Simon Germain


Have you tried:

- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation

Then you can specify only your updated cell for reloading. You can also tell your tableview to remember or forget selection status upon reload with:

tableViewController.clearsSelectionOnViewWillAppear = NO;

Then you will also have to deal with the custom view inside

- (UITableViewCell *)tableView:(UITableView *)tableView 
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
like image 26
Putz1103 Avatar answered Sep 19 '22 11:09

Putz1103