Reputation: 13658
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
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.
You should always use a delegate pattern for this, what follows is a hack that might break again in future versions.
UITableView *parentTable = (UITableView *)self.superview;
if (![parentTable isKindOfClass:[UITableView class]]) {
parentTable = (UITableView *) parentTable.superview;
}
[parentTable reloadData];
Upvotes: 0
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
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
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
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
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
Reputation: 28740
try [yourtableview reloadData]; after setting the image to a new image
Upvotes: -1