NSURLSession load images on demand
up vote
3
down vote
favorite
I'm trying to come up with a system to populate my item images on demand, this is what I have so far. One obvious bug is that an item image could be downloaded multiple time since the _image
will remain nil until the first download has completed. I could work around this with a loading property.
My main issue is about priority, ideally I would like the images currently visible in the tableview to be downloaded first. ie As soon as the tableview appears it starts downloading the first 20 or so initially visible item.images, but if the users scrolls down to the bottom then items 20-100 all get queued in order which results in the now visible images 80-100 being last to download. Not really sure how to deal with this.
@interface Item : NSObject
@property (nonatomic, strong) UIImage *image;
@end;
@implementation Item
- (UIImage *)image
{
if (!_image) {
NSURL *url = [NSURL URLWithString:@"http://loremflickr.com/50/50/paris"];
NSURLSessionDataTask *imageTask = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
_image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"ItemUpdated" object:self];
});
}];
[imageTask resume];
}
return _image;
}
@end
@interface TableViewController : UITableViewController
@property (nonatomic, strong) NSArray *items;
@end
@implementation TableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *items = [[NSMutableArray alloc] initWithCapacity:100];
for (int i=0; i<100; i++) {
Item *item = [[Item alloc] init];
[items addObject:item];
}
self.items = items;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemUpdated:) name:@"ItemUpdated" object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"ItemUpdated" object:nil];
}
- (void)itemUpdated:(NSNotification *)notification
{
Item *item = (Item *)notification.object;
NSUInteger index = [self.items indexOfObject:item];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.items.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Item *item = [self.items objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
cell.imageView.image = item.image;
cell.textLabel.text = [NSString stringWithFormat:@"Item %d", indexPath.row];
return cell;
}
@end
image objective-c asynchronous ios http
add a comment |
up vote
3
down vote
favorite
I'm trying to come up with a system to populate my item images on demand, this is what I have so far. One obvious bug is that an item image could be downloaded multiple time since the _image
will remain nil until the first download has completed. I could work around this with a loading property.
My main issue is about priority, ideally I would like the images currently visible in the tableview to be downloaded first. ie As soon as the tableview appears it starts downloading the first 20 or so initially visible item.images, but if the users scrolls down to the bottom then items 20-100 all get queued in order which results in the now visible images 80-100 being last to download. Not really sure how to deal with this.
@interface Item : NSObject
@property (nonatomic, strong) UIImage *image;
@end;
@implementation Item
- (UIImage *)image
{
if (!_image) {
NSURL *url = [NSURL URLWithString:@"http://loremflickr.com/50/50/paris"];
NSURLSessionDataTask *imageTask = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
_image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"ItemUpdated" object:self];
});
}];
[imageTask resume];
}
return _image;
}
@end
@interface TableViewController : UITableViewController
@property (nonatomic, strong) NSArray *items;
@end
@implementation TableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *items = [[NSMutableArray alloc] initWithCapacity:100];
for (int i=0; i<100; i++) {
Item *item = [[Item alloc] init];
[items addObject:item];
}
self.items = items;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemUpdated:) name:@"ItemUpdated" object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"ItemUpdated" object:nil];
}
- (void)itemUpdated:(NSNotification *)notification
{
Item *item = (Item *)notification.object;
NSUInteger index = [self.items indexOfObject:item];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.items.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Item *item = [self.items objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
cell.imageView.image = item.image;
cell.textLabel.text = [NSString stringWithFormat:@"Item %d", indexPath.row];
return cell;
}
@end
image objective-c asynchronous ios http
Have you taken a look at the approaches existing tools like github.com/rs/SDWebImage have used?
– Jonah
Jun 22 '16 at 21:10
add a comment |
up vote
3
down vote
favorite
up vote
3
down vote
favorite
I'm trying to come up with a system to populate my item images on demand, this is what I have so far. One obvious bug is that an item image could be downloaded multiple time since the _image
will remain nil until the first download has completed. I could work around this with a loading property.
My main issue is about priority, ideally I would like the images currently visible in the tableview to be downloaded first. ie As soon as the tableview appears it starts downloading the first 20 or so initially visible item.images, but if the users scrolls down to the bottom then items 20-100 all get queued in order which results in the now visible images 80-100 being last to download. Not really sure how to deal with this.
@interface Item : NSObject
@property (nonatomic, strong) UIImage *image;
@end;
@implementation Item
- (UIImage *)image
{
if (!_image) {
NSURL *url = [NSURL URLWithString:@"http://loremflickr.com/50/50/paris"];
NSURLSessionDataTask *imageTask = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
_image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"ItemUpdated" object:self];
});
}];
[imageTask resume];
}
return _image;
}
@end
@interface TableViewController : UITableViewController
@property (nonatomic, strong) NSArray *items;
@end
@implementation TableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *items = [[NSMutableArray alloc] initWithCapacity:100];
for (int i=0; i<100; i++) {
Item *item = [[Item alloc] init];
[items addObject:item];
}
self.items = items;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemUpdated:) name:@"ItemUpdated" object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"ItemUpdated" object:nil];
}
- (void)itemUpdated:(NSNotification *)notification
{
Item *item = (Item *)notification.object;
NSUInteger index = [self.items indexOfObject:item];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.items.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Item *item = [self.items objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
cell.imageView.image = item.image;
cell.textLabel.text = [NSString stringWithFormat:@"Item %d", indexPath.row];
return cell;
}
@end
image objective-c asynchronous ios http
I'm trying to come up with a system to populate my item images on demand, this is what I have so far. One obvious bug is that an item image could be downloaded multiple time since the _image
will remain nil until the first download has completed. I could work around this with a loading property.
My main issue is about priority, ideally I would like the images currently visible in the tableview to be downloaded first. ie As soon as the tableview appears it starts downloading the first 20 or so initially visible item.images, but if the users scrolls down to the bottom then items 20-100 all get queued in order which results in the now visible images 80-100 being last to download. Not really sure how to deal with this.
@interface Item : NSObject
@property (nonatomic, strong) UIImage *image;
@end;
@implementation Item
- (UIImage *)image
{
if (!_image) {
NSURL *url = [NSURL URLWithString:@"http://loremflickr.com/50/50/paris"];
NSURLSessionDataTask *imageTask = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
_image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"ItemUpdated" object:self];
});
}];
[imageTask resume];
}
return _image;
}
@end
@interface TableViewController : UITableViewController
@property (nonatomic, strong) NSArray *items;
@end
@implementation TableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *items = [[NSMutableArray alloc] initWithCapacity:100];
for (int i=0; i<100; i++) {
Item *item = [[Item alloc] init];
[items addObject:item];
}
self.items = items;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemUpdated:) name:@"ItemUpdated" object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"ItemUpdated" object:nil];
}
- (void)itemUpdated:(NSNotification *)notification
{
Item *item = (Item *)notification.object;
NSUInteger index = [self.items indexOfObject:item];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.items.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Item *item = [self.items objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
cell.imageView.image = item.image;
cell.textLabel.text = [NSString stringWithFormat:@"Item %d", indexPath.row];
return cell;
}
@end
image objective-c asynchronous ios http
image objective-c asynchronous ios http
edited Sep 29 '17 at 18:44
200_success
127k15148412
127k15148412
asked Jun 20 '16 at 1:55
trapper
1665
1665
Have you taken a look at the approaches existing tools like github.com/rs/SDWebImage have used?
– Jonah
Jun 22 '16 at 21:10
add a comment |
Have you taken a look at the approaches existing tools like github.com/rs/SDWebImage have used?
– Jonah
Jun 22 '16 at 21:10
Have you taken a look at the approaches existing tools like github.com/rs/SDWebImage have used?
– Jonah
Jun 22 '16 at 21:10
Have you taken a look at the approaches existing tools like github.com/rs/SDWebImage have used?
– Jonah
Jun 22 '16 at 21:10
add a comment |
1 Answer
1
active
oldest
votes
up vote
0
down vote
I have a possible solution for your case, this is what I am thinking.
1) You can implement scrollViewDidEndDragging delegate and load your image for the visible cells.
for ex.
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self loadImagesForOnScreenRows];
}
loadIamgesForOnScreenRows will look like:
- (void)loadImagesForOnScreenRows
{
NSArray *visiblePaths = [self.tracksListing indexPathsForVisibleRows];
for (NSIndexPath *indexPath in visiblePaths){
[[self.tableView cellForRowAtIndexPath:indexPath] bindItemObject:record];
}
}
}
In my case bindItemObject is for downloading the image implemented in tableViewCell class, and record contains the url for the image.
The only problem which you will encounter in this case is, when the tableView appears for the first time.
In that case I suggest you to programmatically call the
[self.tableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionTop
animated:YES];
and give indexpath as indexpath for row:0 and section:0
I think this can solve your problem.
You don't need the scrollToRowAtIndexPath bit Just call[self loadImagesForOnScreenRows];
manually
– trapper
May 8 at 2:48
add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
0
down vote
I have a possible solution for your case, this is what I am thinking.
1) You can implement scrollViewDidEndDragging delegate and load your image for the visible cells.
for ex.
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self loadImagesForOnScreenRows];
}
loadIamgesForOnScreenRows will look like:
- (void)loadImagesForOnScreenRows
{
NSArray *visiblePaths = [self.tracksListing indexPathsForVisibleRows];
for (NSIndexPath *indexPath in visiblePaths){
[[self.tableView cellForRowAtIndexPath:indexPath] bindItemObject:record];
}
}
}
In my case bindItemObject is for downloading the image implemented in tableViewCell class, and record contains the url for the image.
The only problem which you will encounter in this case is, when the tableView appears for the first time.
In that case I suggest you to programmatically call the
[self.tableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionTop
animated:YES];
and give indexpath as indexpath for row:0 and section:0
I think this can solve your problem.
You don't need the scrollToRowAtIndexPath bit Just call[self loadImagesForOnScreenRows];
manually
– trapper
May 8 at 2:48
add a comment |
up vote
0
down vote
I have a possible solution for your case, this is what I am thinking.
1) You can implement scrollViewDidEndDragging delegate and load your image for the visible cells.
for ex.
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self loadImagesForOnScreenRows];
}
loadIamgesForOnScreenRows will look like:
- (void)loadImagesForOnScreenRows
{
NSArray *visiblePaths = [self.tracksListing indexPathsForVisibleRows];
for (NSIndexPath *indexPath in visiblePaths){
[[self.tableView cellForRowAtIndexPath:indexPath] bindItemObject:record];
}
}
}
In my case bindItemObject is for downloading the image implemented in tableViewCell class, and record contains the url for the image.
The only problem which you will encounter in this case is, when the tableView appears for the first time.
In that case I suggest you to programmatically call the
[self.tableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionTop
animated:YES];
and give indexpath as indexpath for row:0 and section:0
I think this can solve your problem.
You don't need the scrollToRowAtIndexPath bit Just call[self loadImagesForOnScreenRows];
manually
– trapper
May 8 at 2:48
add a comment |
up vote
0
down vote
up vote
0
down vote
I have a possible solution for your case, this is what I am thinking.
1) You can implement scrollViewDidEndDragging delegate and load your image for the visible cells.
for ex.
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self loadImagesForOnScreenRows];
}
loadIamgesForOnScreenRows will look like:
- (void)loadImagesForOnScreenRows
{
NSArray *visiblePaths = [self.tracksListing indexPathsForVisibleRows];
for (NSIndexPath *indexPath in visiblePaths){
[[self.tableView cellForRowAtIndexPath:indexPath] bindItemObject:record];
}
}
}
In my case bindItemObject is for downloading the image implemented in tableViewCell class, and record contains the url for the image.
The only problem which you will encounter in this case is, when the tableView appears for the first time.
In that case I suggest you to programmatically call the
[self.tableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionTop
animated:YES];
and give indexpath as indexpath for row:0 and section:0
I think this can solve your problem.
I have a possible solution for your case, this is what I am thinking.
1) You can implement scrollViewDidEndDragging delegate and load your image for the visible cells.
for ex.
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self loadImagesForOnScreenRows];
}
loadIamgesForOnScreenRows will look like:
- (void)loadImagesForOnScreenRows
{
NSArray *visiblePaths = [self.tracksListing indexPathsForVisibleRows];
for (NSIndexPath *indexPath in visiblePaths){
[[self.tableView cellForRowAtIndexPath:indexPath] bindItemObject:record];
}
}
}
In my case bindItemObject is for downloading the image implemented in tableViewCell class, and record contains the url for the image.
The only problem which you will encounter in this case is, when the tableView appears for the first time.
In that case I suggest you to programmatically call the
[self.tableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionTop
animated:YES];
and give indexpath as indexpath for row:0 and section:0
I think this can solve your problem.
answered Aug 5 '16 at 10:50
Sanchit Kumar Singh
1012
1012
You don't need the scrollToRowAtIndexPath bit Just call[self loadImagesForOnScreenRows];
manually
– trapper
May 8 at 2:48
add a comment |
You don't need the scrollToRowAtIndexPath bit Just call[self loadImagesForOnScreenRows];
manually
– trapper
May 8 at 2:48
You don't need the scrollToRowAtIndexPath bit Just call
[self loadImagesForOnScreenRows];
manually– trapper
May 8 at 2:48
You don't need the scrollToRowAtIndexPath bit Just call
[self loadImagesForOnScreenRows];
manually– trapper
May 8 at 2:48
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f132476%2fnsurlsession-load-images-on-demand%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Have you taken a look at the approaches existing tools like github.com/rs/SDWebImage have used?
– Jonah
Jun 22 '16 at 21:10