Arrel
Arrel

Reputation: 13658

Refreshing the UITableView from UITableViewCell

Is there an easy way to trigger a [UITableView reloadData] from within a UITableViewCell? I am loading remote images to be displayed after the initial table display, and updating the image with self.image = newImage does not refresh the table. Resetting the cell's text value does refresh the table, but this seems sloppy.

MyTableViewCell.h

@interface MyTableViewCell : UITableViewCell {}
- (void)imageWasLoaded:(ImageData *) newImage;

MyTableViewCell.m

@implementation MyTableViewCell
- (void)imageWasLoaded:(UIImage *) newImageData {
    self.image = newImage; //does not refresh table

    //would like to call [self.tableView reloadData] here,
    //but self.tableView does not exist.

    //instead I use the below line
    self.text = self.text; //does refresh table
}
@end

Upvotes: 9

Views: 22745

Answers (7)

Lucas van Dongen
Lucas van Dongen

Reputation: 9878

Anybody that was looking for a way to reload the table from the cell and figured out that CodeGrue's answer does not work, you can still do it this way but you need to double check.

Disclaimer

You should always use a delegate pattern for this, what follows is a hack that might break again in future versions.

Hack to reload the data from the cell

UITableView *parentTable = (UITableView *)self.superview;
if (![parentTable isKindOfClass:[UITableView class]]) {
    parentTable = (UITableView *) parentTable.superview;
}

[parentTable reloadData];

Upvotes: 0

Marc Nunes
Marc Nunes

Reputation: 238

CodeGrue is right. After days thinking why [subclassOjb.tableView reloadData] wasn't working I gave up trying to understand that and settled for the only two methods I know works and it's very easy to implement: 1- Notification Center (from tableviewcell you shouldn't use this one) 2- or use the superview to get a handle on your tableview property (this is perfect for uitableviewcells).

Note that using the superview method will work on your uitableviewcell class only, chances are if you have a custom cell you will have a uitableviewcell class which the superview is your tableviewcontroller or whateverClass you have with a uitableview delegate + datasource.

Now if you need to reload your tableview from another class, a datasource class let's say a singleton or whatever; you should use a block within your tableview class to load the data on a different thread, with an inner block to reload the table on the mainthread.

//handle tablview to show spinner (you have to lower your tableview out of the way yourself if you are calling this method)
[self.tableView setContentOffset:CGPointMake(0, -rf.frame.size.height) animated:YES];

//start spinner animation here
[rf beginRefreshing];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
                                         (unsigned long)NULL), ^(void) {
    // Load Data from data source class here
    [myDataSourceClass fetchDataMethod];

    dispatch_async(dispatch_get_main_queue(), ^{

         //Reload data and stop spinner
        [self.tableView reloadData];
        [rf endRefreshing];

        //handle tablview to hide spinner atrributed title
        [self.tableView setContentOffset:CGPointMake(0, 0) animated:YES];
    });
});

If you are on a different view and something happens that you need to reload your table on your table view. Well at that point your table is not on screen there is no reason why you should reload your table if no one is going to see it. Just add [self.tableview reloadDAta] to your viewWillAppear method when users go back to your tableviewcontroller view it will reload the table on the fly.

Anyway for tableviewcell just use the following inside your action to reloadData (credit to CodeGrue)

UITableView *parentTable = (UITableView *)self.superview;
[parentTable reloadData];

**different topic but for those of you starting now. This will save you from possible headaches - after creating your tableclass and before you even start to deal with it's delegate methods - do this: in your viewDidLoad add the following code:

self.tableView.delegate = self;
self.tableView.dataSource = self; 

Upvotes: 0

CodeGrue
CodeGrue

Reputation: 5933

Get a reference to the containing UITableView using the superview property. Then tell it to "reloadData":

UITableView *parentTable = (UITableView *)self.superview;
[parentTable reloadData];

Upvotes: 5

bbrown
bbrown

Reputation: 6360

I did the exact thing that you're trying to do. The thing you're looking for is needsLayout. To wit (this is a notification observer on my UITableViewCell subclass):

- (void)reloadImage:(NSNotification *)notification
{
    UIImage *image = [[SSImageManager sharedImageManager] getImage:[[notification userInfo] objectForKey:@"imageUrl"];
    [self setImage:image];
    [self setNeedsLayout];
}

This will pop in your image without having to reload the entire table, which can get very expensive.

Upvotes: 18

Daniel Rinser
Daniel Rinser

Reputation: 8855

Hmm. So imageWasLoaded: is a delegate method called by some asynchronous image loading code? Actually it seems weird that setting self.image does not update the image. Have you tried to add your own UIImageView to the cell rather than using the image property? Might be kind of a hack, but this way the image should update right away (without you having to reload the whole table view, which is definitely not desireable).

Even better: If you are using SDK 3.0, you can use the new imageView property in UITableViewCell (I have not tried this, though):

self.imageView.image = newImage;

Actually, I needed to do exactly the same some weeks ago. However, my approach was to subclass UIImageView and do all the asynchronous image loading/updating in that class. The main reason I did it this way was that I wanted to have a generic and reusable component which can be used in different table view cells (or elsewhere).

Upvotes: 0

Bart Jacobs
Bart Jacobs

Reputation: 9082

It is less expensive to only reload the sections/rows that need reloading. In iPhone SDK 3.0 there are some methods for doing just that.

Upvotes: 0

Rahul Vyas
Rahul Vyas

Reputation: 28740

try [yourtableview reloadData]; after setting the image to a new image

Upvotes: -1

Related Questions