Reputation: 1732
I would like to refresh a cell where an asynchrone loading has been done.
I know 2 ways to do that but none is exactly what I want. Here is what I know:
[myTableView reloadData];
Or
[myTableView beginUpdates];
[myTableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPathOfMyCell] withRowAnimation:UITableViewRowAnimationNone];
[myTableView endUpdates];
My asynchrone method to load images is called directly from my custom UITableViewCell
. That means it's independent of my UITableView
. The final solution I have is to pass the my UITableViewCell
as parameter with the index on the current cell. It looks like a very ugly solution.
Does anyone know another solution ?
Edit:
[myCacheClass loadUIImageFromPath:photo.mediumDistURL];
loadUIImageFromPath:(NSString *) path{
//Some code to load the image.
if(![weakSelf.delegate respondsToSelector:@selector(MyCacheDidLoadImageFromCache:)])
[weakSelf setDelegate:appDelegate.callbacksCollector];
[weakSelf.delegate MyCacheDidLoadImageFromCache:image];
}
The implementation of MyCacheDidLoadImageFromCache
:
- (void) CacheCacheDidLoadImageFromCache: (UIImage *) image{
DDLogInfo(@"Successfully load image from cache : %@", [image description]);
myImageView.image = image;
}
Upvotes: 1
Views: 1488
Reputation: 12200
You can indirect via NSNotificationCenter. The table can subscribe notifications:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(dataAvailableNotification:)
name:@"MyDataNowAvailable"
object:nil];
When the data is available, the cache will post:
[[NSNotificationCenter defaultCenter] postNotificationName:@"MyDataNowAvailable"
object:self userInfo:@{@"Data": theData}];
Then the table reacts to this notification by mapping the data to appropriate cells and triggering the repaint:
- (void) dataAvailableNotification:(NSNotification *) notification
{
DataObject *theData = notification.userInfo[@"Data"];
// add code here to find which cell(s) this data is displayed in
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
// etc
}
Good thing about this method is it generalizes to situations where you have the same data displayed in multiple views.
Upvotes: 1
Reputation: 3428
So here is the block based solution, no need to pass tableView
and indexPath
pointer to cell. We'll implement a completionBlock
instead. You should be familiar with this type of approach:
CustomCell.h:
// Define block as a typedef
typedef void (^CellCompletionHandler)(BOOL);
@interface MyTableViewCell : UITableViewCell
// Take a predefined block as a parameter
- (void)initWithImageURL:(NSURL *)URL completionHanlder:(CellCompletionHandler)completion;
@end
CustomCell.m:
- (void)initWithImageURL:(NSURL *)URL completionHanlder:(CellCompletionHandler)completion
{
// Take the URL and call cell's inner loading method
// Pass the completion block to it
[self doSomethingWithCellCompletionHandler:completion];
}
- (void)doSomethingWithCellCompletionHandler:(CellCompletionHandler)completion
{
// When you finish loading your image from cache
// call this to tell the block that you finished
completion(YES);
}
cellForRowAtIndexPath:
[cell initWithImageURL:[NSURL URLWithString:@"your URL"]completionHanlder:^(BOOL finishedLoading) {
if(finishedLoading)
{
// Here you can use indexPath to check whether it's visible
// in order to reload it
NSLog(@"Finished loading image");
}
}];
Upvotes: 0
Reputation: 4199
You dont need to pass UITableViewCell
, instead past NSIndexPath
for that perticular cell. & then use reloadRowsAtIndexPaths
method to reload.
To obtain the table container in your chache class file use property.
@property (nonatomic, assign) UITableView *tableView;
Then in implemetation
loadUIImageFromPath:(NSString *) path forCell(NsIndexPath*)indexPath{
//Some code to load the image.
if(![weakSelf.delegate respondsToSelector:@selector(MyCacheDidLoadImageFromCache:)])
[weakSelf setDelegate:appDelegate.callbacksCollector];
[weakSelf.delegate MyCacheDidLoadImageFromCache:image forCell:indexPath];
}
- (void) CacheCacheDidLoadImageFromCache: (UIImage *) image forCell(NSIndexPath*)indexPath{
DDLogInfo(@"Successfully load image from cache : %@", [image description]);
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
[tableView endUpdates];
myImageView.image = image;
//tableView is from property & you have to pass NSIndexPath
}
& in your ViewController assign this propery as
myCacheClass.tableView = yourTableView;
[myCacheClass loadUIImageFromPath:photo.mediumDistURL forCell:indexPath];
Upvotes: 0
Reputation: 1270
Put this in - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
after you setup your subclassed TableViewCell.
NSURL *imageURL = [NSURL URLWithString:@"your image url"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
if (imageData) {
UIImage *image = [UIImage imageWithData:imageData];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
YourCellSubclass *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
if (updateCell) {
updateCell.yourImageView.image = image;
}
});
}
}
});
In this code, every cell makes an asynchronous image request as the cell is being loaded. If data comes back, it attempts to create a UIImage out of it. If that image is valid it then updates the image on the main thread only if the cell is still visible.
This code will make a network call for the image every time the cell is loaded. You will want to implement some sort of caching most likely. The type/amount of caching you want to do depends on how dynamic your data is.
Upvotes: 0
Reputation: 3345
There are plenty of better solutions already defined for this type of situation. You can go for sophisticated third party code AsyncImageView, AFNetworking.
Upvotes: 1