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









share|improve this question
















bumped to the homepage by Community 5 mins ago


This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.















  • Have you taken a look at the approaches existing tools like github.com/rs/SDWebImage have used?
    – Jonah
    Jun 22 '16 at 21:10















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









share|improve this question
















bumped to the homepage by Community 5 mins ago


This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.















  • Have you taken a look at the approaches existing tools like github.com/rs/SDWebImage have used?
    – Jonah
    Jun 22 '16 at 21:10













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









share|improve this question















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Sep 29 '17 at 18:44









200_success

127k15148411




127k15148411










asked Jun 20 '16 at 1:55









trapper

1665




1665





bumped to the homepage by Community 5 mins ago


This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.







bumped to the homepage by Community 5 mins ago


This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.














  • 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




Have you taken a look at the approaches existing tools like github.com/rs/SDWebImage have used?
– Jonah
Jun 22 '16 at 21:10










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.






share|improve this answer





















  • You don't need the scrollToRowAtIndexPath bit Just call [self loadImagesForOnScreenRows]; manually
    – trapper
    May 8 at 2:48











Your Answer





StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");

StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














 

draft saved


draft discarded


















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

























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.






share|improve this answer





















  • You don't need the scrollToRowAtIndexPath bit Just call [self loadImagesForOnScreenRows]; manually
    – trapper
    May 8 at 2:48















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.






share|improve this answer





















  • You don't need the scrollToRowAtIndexPath bit Just call [self loadImagesForOnScreenRows]; manually
    – trapper
    May 8 at 2:48













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.






share|improve this answer












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.







share|improve this answer












share|improve this answer



share|improve this answer










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


















  • 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


















 

draft saved


draft discarded



















































 


draft saved


draft discarded














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





















































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







Popular posts from this blog

404 Error Contact Form 7 ajax form submitting

How to know if a Active Directory user can login interactively

TypeError: fit_transform() missing 1 required positional argument: 'X'