Let's say you have a UITableView
that displays a list of file
metadata, and you want to show the download_progress
of each file in a UILabel
of a custom UITableViewCell
. (This is an arbitrarily long list - thus dynamic cells will be reused).
If you want to update the label without calling either reloadData
or reloadRowsAtIndexPaths
, how can you do it?
For those who are wondering - I don't want to call either of the reload...
methods because there's no need to reload the entire cell for each percentage point update on download_progress
.
The only solutions I've come across are:
Adding the cell as a key-value observer for the file
's download_progress
.
Calling cellForRowAtIndexPath...
directly to obtain the label and change it's text.
However,
KVO in general isn't a fun api to work with - and even less so when you add cell reuse into the mix. Calling cellForRowAtIndexPath
directly each time a percentage point is added feels dirty though.
So, what are some possible solutions? Any help would be appreciated.
Thanks.
As a corollary to Doug's response, here is what I ended up going with:
Each file
has a unique identifier, so I made it responsible for posting notifications about updates to its attributes (think KVO, but without the hassle):
I made a FileNotificationType
enum (i.e. FileNotificationTypeDownloadTriggered
, and FileNotificationTypeDownloadProgress
). Then I would send the progress into the NSNotification
's userInfo NSDictionary
along with the FileNotificationType
.
- (void)postNotificationWithType:(FileNotificationType)type andAttributes:(NSDictionary *)attributes
{
NSString *unique_notification_id = <FILE UNIQUE ID>;
NSMutableDictionary *mutable_attributes = [NSMutableDictionary dictionaryWithDictionary:attributes];
[mutable_attributes setObject:@(type) forKey:@"type"];
NSDictionary *user_info = [NSDictionary dictionaryWithDictionary:mutable_attributes];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:unique_notification_id object:nil userInfo:user_info];
});
}
The file
object also has a method to enumerate what types of notifications it could send:
- (NSArray *)notificationIdentifiers
{
NSString *progress_id = <FILE UNIQUE ID + FILENOTIFICATIONTYPE>;
NSString *status_id = <FILE UNIQUE ID + FILENOTIFICATIONTYPE>
NSString *triggered_id = <FILE UNIQUE ID + FILENOTIFICATIONTYPE>
NSArray *identifiers = @[progress_id, status_id, triggered_id];
return identifiers;
}
So when you update an attribute of a file
elsewhere, simply do this:
NSDictionary *attributes = @{@"download_progress" : @(<PROGRESS_INTEGER>)};
[file_instance postNotificationWithType:FileNotificationTypeDownloadProgress andAttributes:attributes];
On the receiving end, my table view delegate implemented these methods to add / remove my custom UITableViewCells
as observers for these notifications:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
File *file = [modelObject getFileAtIndex:indexPath.row];
for (NSString *notification_id in file.notificationIdentifiers)
{
[[NSNotificationCenter defaultCenter] addObserver:cell selector:@selector(receiveFileNotification:) name:notification_id object:nil];
}
}
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
[[NSNotificationCenter defaultCenter] removeObserver:cell];
}
Finally, the custom UITableViewCell
has to implement the receiveFileNotification:
method:
- (void)receiveFileNotification:(NSNotification *)notification
{
FileNotificationType type = (FileNotificationType)[notification.userInfo[@"type"] integerValue];
// Access updated property info with: [notification.userInfo valueForKey:@"<Your key here>"]
switch (type)
{
case FileNotificationTypeDownloadProgress:
{
// Do something with the progress
break;
}
case FileNotificationTypeDownloadStatus:
{
// Do something with the status
break;
}
case FSEpisodeNotificationTypeDownloadTriggered:
{
// Do something if the download is triggered
break;
}
default:
break;
}
}
Hopefully this helps someone who is looking to update tableview cells without having to reload them! The benefit over key-value observing is that you won't get issues if the File
object is deallocated with the cell still observing. I also don't have to call cellForRow...
.
Enjoy!
I would create a custom cell, which I'm guessing you've done. Then I'd have the cell listen for a specific notification that your download progress method would post, then update the label there. You'd have to figure out a way for your download progress to specify a certain cell, maybe by a title string or something that would be unique that your download progress method could be told, so your cell update method could make sure the note was meant for it. Let me know if you need me to clarify my thought process on this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With