Multiple Times load of image from API in ImageView in IOS - ios

I have made a custom Tableview which has an image view and some labels that takes values from API.
It's a custom table in which images and labels recieve data from API related to real estate .
When my custom table loads, values start coming from API.
The actual issue ocurr in image view . I have set a local image in an image view , when the data coming from API has no image than it should show local image otherwise data should show its original image submitted by customer in the forms.
Now when data start coming from API first in image view it shows local image than when images start coming from API it start showing that but when we scroll down in image view it shows same images in image view and repeats many times and we cannot identify that which is original image.
I can provide the code if anyone sent me his email , he can get all the senario of work.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier=#"Cell";
CustomTableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
StringData* cust = [_Title objectAtIndex:indexPath.row];
cell.Title.text = cust.title1;
cell.area.text=cust.Area;
cell.city.text=cust.City;
cell.phone.text=cust.phoneno;
cell.price.text=cust.Price;
cell.location1.text=cust.location;
cell.name1.text=cust.dealer_name;
cell.email1.text=cust.dealer_email;
cell.type1.text=cust.property_type;
cell.status1.text=cust.status;
cell.room1.text=cust.rooms;
cell.washrooms.text=cust.bath;
cell.floor1.text=cust.floors;
cell.imagetext.text = cust.images;
tii=cell.Title.text;
NSLog(#"Tittttttle is %#",tii);
NSURL *url = [NSURL URLWithString:cust.images];
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (data) {
UIImage *image = [UIImage imageWithData:data];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
cell.images1.image = image;
});
}
}
}];
[task resume];
return cell;
}

It seems you don't store an image into local application memory. You tried to load image on every time when cell is appear. So you download image every time.
Best Solution:
You need to store image local memory using SDWebImage.
Objective-C:
#import <SDWebImage/UIImageView+WebCache.h>
...
[imageView sd_setImageWithURL:[NSURL URLWithString:#"IMAGE_URL"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
Swift:
import SDWebImage
imageView.sd_setImage(with: URL(string: "IMAGE_URL"), placeholderImage: UIImage(named: "placeholder.png"))
If you are loading an image for the first time, then it will download and store an image into local memory. So from second times, it will load from local itself.

Well, its because your cell is getting re-used and the previous image is being displayed. You can override prepareForReuse() method of your tableviewcell subclass or consider setting placeholder for the url image using SDWebImage
Edit: Explaination
You are downloading your image asyc and setting it to your imageview of your tableviewcell, what is happening here is while your next image is being downloaded, before that your cell is re-used and displays previous image.

Related

How would I get my image to occupy the whole image view?

I have an image view with constraints set up, and the image that I load in (from a URL) doesn't fill the whole image view for some reason. I included a picture of the one in question followed by the detail view of the same 'post'. The same code is used for both. In the detail view (one that appears correctly), I set the post image in during viewdidload, and it's a standalone view. The one that loads in wonky is a table view cell with a custom class, and the image is set in the cellForRowAtIndexPath. I do resize the image before I send it to Back4App with parse to 500x500, and the image views are both set to clip to bounds and aspect fit. the uiimageview constraints are exactly the same, with horizontal alignment of zero, a fixed width and height, and constraints for all four sides (8 away from nearest neighbors)
here are code snippets for how both of them are done:
pink background:
- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
PostCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"PostCell"];
Post *post = self.posts[indexPath.row];
PFUser *user = post.author;
[user fetchInBackgroundWithBlock:^(PFObject * _Nullable object, NSError * _Nullable error) {
cell.usernameLabel.text = post.author.username;
}];
cell.titleLabel.text = post.caption;
cell.isLiked = NO;
NSURL *imageURL = [NSURL URLWithString:post.image.url];
[cell.imageView setImageWithURL:imageURL];
return cell;
}
one that loads correctly:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
PFUser *user = self.post.author;
[user fetchInBackgroundWithBlock:^(PFObject * _Nullable object, NSError * _Nullable error) {
self.usernameTextLabel.text = self.post.author.username;
}];
self.titleTextLabel.text = self.post.caption;
self.descriptionTextLabel.text = self.post.bodyText;
NSURL *postImageURL = [NSURL URLWithString:self.post.image.url];
[self.postImageView setImageWithURL:postImageURL];'''
}
Any tips? There may be some basics that I don't have a handle on - i took a crash course basically and I've only been at this for about 3 weeks so I don't understand a lot of this as deeply as I wish I did.
I've set the background color to pink so you could see the image size vs image view
one that loads correctly
side note: i literally can't figure out how to do blocks of code correctly??? i'm following what it says lol
Check following points
Debug the frame size of the ImageView that it is correct which you supplied.
Set following to the ImageView
[cell.imageView setImageWithURL:imageURL];
cell.imageView.contentMode = UIViewContentModeScaleAspectFill;
[cell.imageView setClipsToBounds:YES];
I was accidentally using the default imageView that the cell comes with instead of my own outlet to my new image view.

Which image loading method should i prefer?

I have app with TableView and cells. In each cell there is UIImageView.
All images are stored on server.
I can use two different methods to load images. Which of them should prefer and why?
Method A :
Use library like SDWebImage to load image and place it in cellForRowAtIndexPath function. So image will be downloaded when cell is created.
Method B :
When i load JSON with image list from server i can create array of UIImages. In each of them i will asynchronously download image from server. And in cellForRowAtIndexPath function i can just assign one of previously created UIImages to current cell image.
Method A - SDWebimage is best for you.
and solve reuse in tableviewcell check this link : Handling download of image using SDWebImage while reusing UITableViewCell
Don't ever use method 2 to handle Images in your application. This is a data and memory wise expensive method. To the best of my understanding, this method would extensively increase the memory pressure. If you create an array of images that would remain in the memory as long as your view controller stays. As the size of this array increases the situation will get worse.
SDWebImage is far far better approach for this task. It saves the images to local storage once downloaded thus creating a cache of images. So you do not have to download the images again and again.
i wish to use AFNetworking if you don't need to cache image , this tools faster than SDWebImage when load image from server . //////
if you use custom cell => replace UITableViewCell with your name file for cell
-(void)fetchImageFromURL:(NSString*)imageURLString Cell:(UITableViewCell*)cell {
/*
_ This Function will accept the string url for Image
_ Also the cell that have image icon
_ After take paramter will make request to fetch image
_
1- if return image => will show Image
*/
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#", imageURLString]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
__weak UITableViewCell *weakCell = cell;
// get image with request
[[cell imageView ] setImageWithURLRequest:request placeholderImage:nil
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
[[weakCell ImageIcon ] setImage:image];
[weakCell setNeedsLayout];
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(#"couldn't load image with url :%#", url);
}];
}
The best choice in my opinion is to rely on a stable, fresh, followed and secure framework.
Recently come to the scene a great framework written in Swift called Nuke (Swift 3 - Xcode 8) . I think you could try Nuke, his power is the "preheat images" (preheating/prefetching means loading images ahead of time in anticipation of its use.), it's full compatible with Alamofire , already knowed by community (raywenderlich.com) and now is at v.4.x (stable and mature). This library have custom handlers and requests with many options.

How would I design an asynchronous image downloader for my UITableView that prioritizes downloads depending on location in table view?

In my app, I receive a list of image URLs to use as thumbnails in my table view. The tableview has a small amount of items, approximately 30, so I want to load all the thumbnails immediately (instead of when they become visible, as they undoubtedly will become visible and I want them fully loaded then).
I want to prioritize the image downloads by index path, so the first index path has priority over the second, which has priority over the third, etc.
However, if I suddenly jump to the end of the table view (which shows, say, index paths 24-29) I want the images for those index paths to become highest priority, so if they jump at the very beginning they won't have to wait for all the others to download first.
How would I go about designing something like this? If it's possible with SDWebImage that'd be great, as I'm comfortable with that, but if not I'm more than fine with creating something from scratch with NSURLSession. (I've looked at SDWebImage, and the SDWebImagePrefetcher looks promising, but doesn't allow priority changing from what I've seen.)
I recently had a similar problem, but didn't need to have a priority to load. I can't think of how to change the priority of what is loaded unless you do the loading of thumbnails before loading the tableview. Tableviews load cells as they are needed.
I can think of two options:
If you want all the data loaded and ready before the tableview is even loaded, preemptively load the data in an earlier view controller and save it to be opened in the view controller containing your table view. Then no requests will have to be made by your tableview and everything will appear seamlessly.
The tableview sends each thumbnail request asynchronously and updates the cell as the images arrive on the main thread. You can then cache or save the images after they arrive. But the images won't appear instantly, generally a split second later.
I did option two using NSCache. The value for key "avatar" is the URL of the thumbnail image. This code is located in my - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
if (![teamMember[#"avatar"] isEqualToString:#""]) {
// check for cached image, use if it exists
UIImage *cachedImage = [self.imageCache objectForKey:teamMember[#"avatar"]];
if (cachedImage) {
cell.memberImage.image = cachedImage;
}
//else retrieve the image from server
else {
NSURL *imageURL = [NSURL URLWithString:teamMember[#"avatar"]];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
// if valid data, create UIImage
if (imageData) {
UIImage *image = [UIImage imageWithData:imageData];
// if valid image, update in tableview asynch
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
TeamCommitsCell *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
// if valid cell, display image and add to cache
if (updateCell) {
updateCell.memberImage.image = image;
[self.imageCache setObject:image forKey:teamMember[#"avatar"]];
}
});
}
}
});
}
}

tableView cell image which is asynchronous loaded flickers as you scroll fast [duplicate]

This question already has answers here:
Async image loading from url inside a UITableView cell - image changes to wrong image while scrolling
(13 answers)
Closed 9 years ago.
Im using a asynchronous block (Grand central dispatch) to load my cell images. However if you scroll fast they still appear but very fast until it has loaded the correct one. Im sure this is a common problem but I can not seem to find a away around it.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Load the image with an GCD block executed in another thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[[appDelegate offersFeeds] objectAtIndex:indexPath.row] imageurl]]];
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *offersImage = [UIImage imageWithData:data];
cell.imageView.image = offersImage;
});
});
cell.textLabel.text = [[[appDelegate offersFeeds] objectAtIndex:indexPath.row] title];
cell.detailTextLabel.text = [[[appDelegate offersFeeds] objectAtIndex:indexPath.row] subtitle];
return cell;
}
At the very least, you probably want to remove the image from the cell (in case it is a re-used cell) before your dispatch_async:
cell.imageView.image = [UIImage imageNamed:#"placeholder.png"];
Or
cell.imageView.image = nil;
You also want to make sure that the cell in question is still on screen before updating (by using the UITableView method, cellForRowAtIndexPath: which returns nil if the cell for that row is no longer visible, not to be confused with the UITableViewDataDelegate method tableView:cellForRowAtIndexPath:), e.g.:
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.imageView.image = [UIImage imageNamed:#"placeholder.png"];
// Load the image with an GCD block executed in another thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[[appDelegate offersFeeds] objectAtIndex:indexPath.row] imageurl]]];
if (data) {
UIImage *offersImage = [UIImage imageWithData:data];
if (offersImage) {
dispatch_async(dispatch_get_main_queue(), ^{
UITableViewCell *updateCell = [tableView cellForRowAtIndexPath:indexPath];
if (updateCell) {
updateCell.imageView.image = offersImage;
}
});
}
}
});
cell.textLabel.text = [[[appDelegate offersFeeds] objectAtIndex:indexPath.row] title];
cell.detailTextLabel.text = [[[appDelegate offersFeeds] objectAtIndex:indexPath.row] subtitle];
return cell;
Frankly, you should also be using a cache to avoid re-retrieving images unnecessarily (e.g. you scroll down a bit and scroll back up, you don't want to issue network requests for those prior cells' images). Even better, you should use one of the UIImageView categories out there (such as the one included in SDWebImage or AFNetworking). That achieves the asynchronous image loading, but also gracefully handles cacheing (don't reretrieve an image you just retrieved a few seconds ago), cancelation of images that haven't happened yet (e.g. if user quickly scrolls to row 100, you probably don't want to wait for the first 99 to retrieve before showing the user the image for the 100th row).
This is a problem with async image loading...
Let's say you have 5 visible rows at any given time.
If you are scrolling fast, and you scroll down for instance 10 rows, the tableView:cellForRowAtIndexPath will be called 10 times. The thing is that these calls are faster than the images are returned, and you have the 10 pending images from different URL-s.
When the images finally come back from the server, they will be returned on those cells that you put in the async loader. Since you are reusing only 5 cells, some of these images will be displayed twice on each cell, as they are downloaded from the server, and that is why you see flickering. Also, remember to call
cell.imageView.image = nil
before calling the async downloading method, as the previous image from the reused cell will remain and also cause a flickering effect when the new image is assigned.
The way around this is to store the latest URL of the image you have to display on each cell, and then when the image comes back from the server check that URL with the one you have in your request. If it is not the same, cache that image for later.
For caching requests, check out NSURLRequest and NSURLConnection classes.
I strongly suggest that you use AFNetworking for any server communication though.
Good luck!
The reason of your flicker is that your start the download for several images during the scrolling, every time you a cell is displayed on screen a new request is performed and it's possible that the old requests are not completed, every tile a request completes the image is set on the cell, so it's if you scroll fast you use let's say a cell 3 times = 3 requests that will be fired = 3 images will be set on that cell = flicker.
I had the same issue and here is my approach:
Create a custom cell with all the required views. Each cells has it's own download operation. In the cell's -prepareForReuse method. I would make the image nil and cancel the request.
In this way for each cell I have only one request operation = one image = no flicker.
Even using AFNetworking you can have the same issue if you won't cancel the image download.
The issue is that you download the image on the main thread. Dispatch a background queue for that:
dispatch_queue_t myQueue = dispatch_queue_create("myQueue", NULL);
// execute a task on that queue asynchronously
dispatch_async(myQueue, ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[[appDelegate offersFeeds] objectAtIndex:indexPath.row] imageurl]]];
//UI updates should remain on the main thread
UIImage *offersImage = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *offersImage = [UIImage imageWithData:data];
cell.imageView.image = offersImage;
});
});

UICollectionview Scrolling choppy when loading cells

I have a gallery in my app utilizing a UICollectionView. The cells are approximately 70,70 size. I am using ALAssets from the ALAssetLibrary in the gallery which I have stored in a list.
I am using the usual pattern for populating the cells:
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
mycell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
mycell.imageView.image = [[UIImage imageWithCGImage:[alassetList objectAtIndex:indexpath.row] thumbnail]];
return mycell;
}
My gallery is scrolling choppy. I don't understand why this is. I've tried adding a NSCache to cache the thumbnail images (thinking maybe creating the images was expensive) but this did not help for the performance.
I would expect the UI to be as buttery as the stock app.
I am now suspecting it may be something in the UICollectionViewCell prepareForReuse that may be holding up the dequeueReusableCellWithReuseIdentifier method but using instruments I was not able to find this.
Any other thing that may be be causing this? Is there a "faster" way to prepare the UICollectionViewCell or to dequeue them in a faster fashion?
So anybody having scrolling issues should do this
add these 2 lines after your dequeue
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
I would assume the "choppyness" is coming from the UIImage allocation, not anything with the dequeueReusableCellWithReuseIdentifier method. I'd try doing the image allocation on a background thread and see if that makes things a little more buttery.
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
mycell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void) {
// Load image on a non-ui-blocking thread
UIImage *image = [[UIImage imageWithCGImage:[alassetList objectAtIndex:indexpath.row] thumbnail]];
dispatch_sync(dispatch_get_main_queue(), ^(void) {
// Assign image back on the main thread
mycell.imageView.image = image;
});
});
return mycell;
}
More details can be found here: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Multithreading/Introduction/Introduction.html
Load your images using NSURLConnection's sendAsynchronousRequest:queue:completionHandler:
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
[cell.imageView setImage:[UIImage imageWithData:data]];
}];
Tick on clip subviews and opaque in attributes inspector and tick off paging enabled.
If anyone is using PhImageManager, turning off synchronous solved the problem. (deliveryMode = .FastFormat can also give additional performance enhancement, but the tradeoff is the thumbnail will be of lower quality)
let option = PHImageRequestOptions()
option.deliveryMode = .Opportunistic
option.synchronous = false
PHImageManager().requestImageForAsset(phAsset, targetSize: CGSizeMake(2048, 2048), contentMode: .AspectFit, options: option, resultHandler: { (image, objects) in
self.imageView.image = image!
})
I had issues with UICollectionView scrolling.
What worked (almost) like a charm for me: I populated the cells with png thumbnails 90x90. I say almost because the first complete scroll is not so smooth, but never crashed anymore...
In my case, the cell size is 90x90.
I had many original png sizes before, and it was very choppy when png original size was greater than ~1000x1000 (many crashes on first scroll).
So I select 90x90 (or the like) on the UICollectionView and display the original pngs (no matter the size). Hope this helps others.

Resources