UICollectionView - How to increase the number of cells to be allocated - ios

My UICollectionView has a non-zero contentInset
self.collectionView.contentInset = UIEdgeInsetsMake(self.mainNavigation.bounds.size.height, 0, 0, 0);
MainNavigation is a transparent navigationBar - once the user scrolls down, collectionView can be seen partially through MainNavigation. More cells are initialized because the "on-screen" frame of the collectionView has increased (these new cells are not dequeued).
The initialization of cells is quite expensive and causes the UI to lag.
What I need is collectionView to initially load in more cells into memory, so that the initial scrolling is smoother.
How can I increase the number of cells that is initially loaded?

I don't think you need to load more cells because it's the natural behavior. If your scroll is not smooth maybe it's because you don't load your images in cells properly.
You must apply the lazy loading pattern. For example, you can do something like this (assuming you have set a NSMutableDictionary* imageDic = [[NSMutableDictionary alloc] init];, your cell have the property #property(nonatomic, retain) UIImageView *imageView; and you are using AFNetworking)
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView_ cellForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell *cell = [collectionView_ dequeueReusableCellWithReuseIdentifier:#"identifier" forIndexPath:indexPath];
NSString* cellKey = [NSString stringWithFormat:#"%d_%d", indexPath.section, indexPath.row];
NSString* imgName = [cellKey stringByAppendingPathExtension:#"jpg"];
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* imagePath = [documentsDirectory stringByAppendingPathComponent:imgName];
BOOL isDirectory = NO;
NSFileManager* fm = [NSFileManager defaultManager];
// set image with image in cache (it will be fast!)
if ([imageDic objectForKey:cellKey])
{
UIImage *img = [imageDic objectForKey:cellKey];
cell.imageView.image = img;
}
// set image with image saved in document directory and put it in cache
else if ([fm fileExistsAtPath:imagePath isDirectory:&isDirectory])
{
UIImage *img = [UIImage imageWithContentsOfFile:imagePath];
cell.imageView.image = img;
[imageDic setObject:img forKey:cellKey];
}
// download image at imageURL, save it and put it in cache too. Until then set image with a placeholder
else
{
__weak UICollectionViewCell* weakCell = cell;
[cell.imageView setImageWithURLRequest:[NSURLRequest requestWithURL:imageURL] placeholderImage:[UIImage imageNamed:#"placeholder"] success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
weakCell.imageView.image = image;
[weakCell setNeedsLayout];
[imageDic setObject:img forKey:cellKey];
[UIImageJPEGRepresentation(img, 1.f) writeToFile:imagePath atomically:YES];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(#"failed to dl image");
}];
}
return cell;
}

There is no way to do that.
What you can do is:
Optimize cell code and remove heavy parts from initialization, maybe delay some operations until scroll stops.
Use solid backgrounds for subviews and set proper value for view.opaque. It gives a good performance boost when scrolling. This is something you can debug in simulator with "Color Blended Layers" option turned on. You'll see hits and misses rendered in green and red.
If you use custom CALayers, try rasterizing when/if they are static.
Cache data if delegate performs heavy operations before returning cell.

Related

uiimageview corner radius not working in tableView cellForRowAtIndexPath

UIImageView corner radius not working inside cellForRowAtIndexPath method
and and i am using is prototype cell.
I also tried layoutIfNeeded method unfortunately doesn't work.
Here is my code:
[cell layoutIfNeeded];
client_image=[cell viewWithTag:3];
[client_image setImageWithURL:[NSURL URLWithString:[info objectForKey:PRE_USERIMAGE]] placeholderImage:[UIImage imageNamed:PLACE_HOLDER_IMAGE]];
// client_image=(UIImageView*)[TMGlobalFunction changebgLayer:client_image];
;
// [client_image layoutIfNeeded];
client_image.layer.masksToBounds=YES;
client_image.layer.borderColor=[UIColor whiteColor].CGColor;
client_image.layer.cornerRadius = client_image.frame.size.height/2;
client_image.layer.borderWidth=[TMGlobalFunction IsiPad]?4.0f:2.0f;
This works fine when I set it manually like:
client_image.layer.cornerRadius = 50;
But it's not working when i upgrade Xcode 7 to 8 with below code:
client_image.layer.cornerRadius = client_image.frame.size.height/2;
Any idea how why i am getting this wired issue?
Update: it is issue in Xcode 8 when I print client_image.frame.size.height it's return 1000 first time. after scrolling it's return right value.
call layoutIfneed and LayoutSubviews both before using frame of Imageview. Try following..Working for me.
client_image=[cell viewWithTag:3];
[cell layoutIfNeeded];
[cell layoutSubviews];
[client_image setImageWithURL:[NSURL URLWithString:[info objectForKey:PRE_USERIMAGE]] placeholderImage:[UIImage imageNamed:PLACE_HOLDER_IMAGE]];
// client_image=(UIImageView*)[TMGlobalFunction changebgLayer:client_image];
;
// [client_image layoutIfNeeded];
client_image.layer.masksToBounds=YES;
client_image.layer.borderColor=[UIColor whiteColor].CGColor;
client_image.layer.cornerRadius = client_image.frame.size.height/2;
client_image.layer.borderWidth=[TMGlobalFunction IsiPad]?4.0f:2.0f;
In your cellForRow, Try this.
NSURL *url = [NSURL URLWithString:#"http:url..."];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
UIImage *placeholderImage = [UIImage imageNamed:#"your_placeholder"];
__weak UITableViewCell *weakCell = cell;
[cell.imageView setImageWithURLRequest:request
placeholderImage:placeholderImage
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
weakCell.imageView.image = image;
// Write your code here for Rounding corner
[weakCell setNeedsLayout];
} failure:nil];
Below is the method which will return the image and you can set this in YourImageView.
Try to give round corner to YourImageView in this method's sucessblock.
From AFNetworking Documentation :
/**
Asynchronously downloads an image from the specified URL request, and sets it once the request is finished. Any previous image request for the receiver will be cancelled.
If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished.
If a success block is specified, it is the responsibility of the block to set the image of the image view before returning. If no success block is specified, the default behavior of setting the image with `self.image = image` is applied.
#param urlRequest The URL request used for the image request.
#param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the image view will not change its image until the image request finishes.
#param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`.
#param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred.
*/
- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest
placeholderImage:(nullable UIImage *)placeholderImage
success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success
failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure;
/**
Try to give corner radius in willDisplayCell of tableView delegate and check:
-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *) cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
client_image=[cell viewWithTag:3];
[client_image setImageWithURL:[NSURL URLWithString:[info objectForKey:PRE_USERIMAGE]] placeholderImage:[UIImage imageNamed:PLACE_HOLDER_IMAGE]];
//client_image=(UIImageView*)[TMGlobalFunction changebgLayer:client_image];
;
//[client_image layoutIfNeeded];
client_image.clipsToBounds = YES;
client_image.layer.masksToBounds=YES;
client_image.layer.borderColor=[UIColor whiteColor].CGColor;
client_image.layer.cornerRadius = client_image.frame.size.height/2;
client_image.layer.borderWidth=[TMGlobalFunction IsiPad]?4.0f:2.0f;
[cell layoutSubviews];
[cell layoutIfNeeded];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"YOURCELLINDETIFIER";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
UIImageView *profile = (UIImageView*)[cell viewWithTag:90]; // your imageview tag
profile.layer.cornerRadius = profile.frame.size.width / 2;
profile.layer.masksToBounds = YES;
profile.layer.borderWidth = 1.0f;
profile.layer.borderColor = [UIColor grayColor].CGColor;
NSURL *url = [NSURL URLWithString:"YOUR URL HERE"; // your URL
[profile sd_setImageWithURL:url completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
if (!error) {
profile.image=image;
}
}];
return cell;
}
Size of the client_image frame is reported incorrect thats why you need to call layoutIfNeeded method. Just try calling [self layoutIfNeeded]; inside viewDidLoad method before doing any cornerRadius stuff.
This should work.
#import <QuartzCore/QuartzCore.h>
cell.previewImage.clipsToBounds = YES;
cell.previewImage.layer.cornerRadius = 20;//asper your requirement give the value of radius.

SDWebImage repeating images in cell instead of waiting to load.

I am using SDWebImage for fetching images from server to my table view app in IOS.
But the problem is that when I scroll down in table view instead of waiting for the images to load it put the images downloaded in the first few rows of table view and repeat those images till the end row and when it downloads the images it changes those repeated images to the actual image for that row.
NSURL * url = [NSURL URLWithString:string];
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadImageWithURL:url
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize)
{
// progression tracking code
}
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished,NSURL * url)
{
if (finished && image )
{
NSArray *visibleIndexPaths = [tableView indexPathsForVisibleRows];
if ([visibleIndexPaths containsObject:indexPath]) {
cell.myImage.image = image;
}
}
}];
Actually, it is not a bug with SDWebImage, but rather it's the nature of how UITableView works. downloadImageWithURL, is an async process,so when your tableView delegate/datasource methods are called, the image isn't downloaded yet, therefore cellForRow doesn't have an image to display.
To overcome this issue you should first check image from cache as
[[SDWebImageManager sharedManager] diskImageExistsForURL:[NSURL URLWithString:ImageUrl]]
if yes then set image to UIImageView otherwise use downloadImageWithURL to download image and add cell tag(To display image to correct row) as
cell.tag = indexPath.row;
on successfull download first check correct row as
if(cell.tag == indexPath.row){
and set image to UIImageView.Here is setImage method.
-(void)setImage:(SLFirstTableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath{
SLFirstTableViewCellItem * slFirstTableViewCellItem = [self.categories objectAtIndex:indexPath.row]; // categories is array of items,replace with yours.
NSString *ImageUrl = slFirstTableViewCellItem.imageUrl; //assume image url is in slFirstTableViewCellItem object.
cell.tag = indexPath.row;
if([[SDWebImageManager sharedManager] diskImageExistsForURL:[NSURL URLWithString:ImageUrl]]){
[cell.imgItem setImage: [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:ImageUrl]];
[self hideProgressView:cell];
}else{
[self showProgressView:cell];
[SDWebImageDownloader.sharedDownloader downloadImageWithURL:[NSURL URLWithString:ImageUrl]
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize)
{
// progression tracking code
}
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished)
{
if (image && finished)
{
[[SDImageCache sharedImageCache] storeImage:image forKey:ImageUrl]; // cache image
if(cell.tag == indexPath.row){ // check if correct row
[cell.imgItem setImage:image];
[self hideProgressView:cell];
}
}else{
cell.imgItem.hidden = YES;
cell.progressBar.hidden = YES;
}
}];
}
}
And define showProgressView and hideProgressView methods as
-(void)showProgressView:(SLFirstTableViewCell *)cell {
cell.progressText.hidden = NO;
cell.progressBar.hidden = NO;
cell.imgItem.hidden = YES;
[cell.progressBar startAnimating];
[cell.progressText setText:#"Loading Image..."];
}
-(void)hideProgressView:(SLFirstTableViewCell *)cell{
cell.progressBar.hidden = YES;
cell.progressText.hidden = YES;
cell.imgItem.hidden = NO;
[cell.progressBar stopAnimating];
}
finally call setImage from cellForRowAtIndexPath method(before returning cell) as
[self setImage:cell atIndexPath:indexPath];

NSCache holds strong pointer to UIImage instantiated with imageWithData: and does not remove from memory on unload

I have a View Controller with a property galleryCache and when an image is downloaded using GCD and imageWithData: the image is added to the cache successfully with a key. However, when the view controller is dismissed it keeps strong pointers to those downloaded images causing them not to be removed from memory. Even if I use the removeAllObjects method on the cache in viewDidDisappear: memory does not clear up.
Does anyone know why this might be?
Here is the code for the method which downloads the images.
- (void)imageForFootageSize:(FootageSize)footageSize withCompletionHandler:(void (^)(UIImage *image))completionBlock
{
if (completionBlock) {
__block UIImage *image;
// Try getting local image from disk.
//
__block NSURL *imageURL = [self localURLForFootageSize:footageSize];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
image = [UIImage imageWithData:[NSData dataWithContentsOfURL:imageURL]];
dispatch_async(dispatch_get_main_queue(), ^{
if (image) {
completionBlock(image);
} else {
//
// Otherwise try getting remote image.
//
imageURL = [self remoteURLForFootageSize:footageSize];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
dispatch_async(dispatch_get_main_queue(), ^{
image = [UIImage imageWithData:imageData];
if (image) {
//
// Save remote image to disk
//
NSURL *photoDirectoryURL = [Footage localURLForDirectory];
// Create the folder(s) where the photos are stored.
//
[[NSFileManager defaultManager] createDirectoryAtPath:[photoDirectoryURL path] withIntermediateDirectories:YES attributes:nil error:nil];
// Save photo
//
NSString *localPath = [[self localURLForFootageSize:footageSize] path];
[imageData writeToFile:localPath atomically:YES];
}
completionBlock(image);
});
});
}
});
});
}
}
Methods which use the above class method to fetch and process the UIImage in the completionHandler.
Method inside UICollectionViewCell subclass.
- (void)setPhoto:(Photo *)photo withImage:(UIImage *)image
{
[self setBackgroundColor:[UIColor blackColor]];
[self.imageView setBackgroundColor:[UIColor clearColor]];
if (photo && !image) {
[photo imageForFootageSize:[Footage footageSizeThatBestFitsRect:self.bounds]
withCompletionHandler:^(UIImage *image) {
if ([self.delegate respondsToSelector:#selector(galleryPhotoCollectionViewCell:didLoadImage:)]) {
[self.delegate galleryPhotoCollectionViewCell:self didLoadImage:image];
}
image = nil;
}];
}
[self.imageView setImage:image];
BOOL isPhotoAvailable = (BOOL)(image);
[self.imageView setHidden:!isPhotoAvailable];
[self.activityIndicatorView setHidden:isPhotoAvailable];
}
Method in UICollectionView data source delegate
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
DIGalleryPhotoCollectionViewCell *photoCell = [collectionView dequeueReusableCellWithReuseIdentifier:photoCellIdentifier forIndexPath:indexPath];
[photoCell setDelegate:self];
Footage *footage = [self footageForIndexPath:indexPath];
Photo *photo = ([footage isKindOfClass:[Photo class]]) ? (Photo *)footage : nil;
if (photo) {
//
// Photo
//
[photoCell setPhoto:photo withImage:[self.galleryCache objectForKey:photo.footageID]];
}
return photoCell;
}
Here are the other relevant methods:
- (void)galleryPhotoCollectionViewCell:(DIGalleryPhotoCollectionViewCell *)cell didLoadImage:(UIImage *)image
{
NSIndexPath *indexPath = [self.galleryCollectionView indexPathForCell:cell];
Footage *footage = [self footageForIndexPath:indexPath];
if ([footage isKindOfClass:[Footage class]]) {
Photo *photo = (Photo *)footage;
UIImage *cachedImage = [self.galleryCache objectForKey:photo.footageID];
if (!cachedImage) {
cachedImage = image;
[self.galleryCache setObject:image forKey:photo.footageID];
}
[cell setPhoto:photo withImage:image];
}
}
And also my getter method for the NSCache property galleryCache
- (NSCache *)galleryCache
{
if (!_galleryCache) {
_galleryCache = [[NSCache alloc] init];
}
return _galleryCache;
}
EDIT
Here is a snapshot of Instruments showing the retain count history of one of the NSCache once its owner (a View Controller) is dismissed.
I'm not seeing anything obvious here, though I'd suggest putting a breakpoint where you purge the cache and make sure that's actually happening like you think it is.
If you still don't find it, you can run Allocations tool in Instruments and turn on "record reference counts" (see latter part of this answer, iOS app with ARC, find who is owner of an object), and you can find out precisely where your lingering strong reference is, at which point you can tackle the remediation.
The other obvious solution is to eliminate all of this code and use a proven image caching tool, like SDWebImage which does a lot of the memory and persistent storage caching for you. It's a pretty decent implementation.
OK, so after re examining my own code and re examining properties for the billionth x n time, it turns out my error was assigning the delegate property as a 'strong' type. Lesson learned: ALWAYS set delegates as WEAK.
I will definitely have to learn more about Instruments, however.

Is there a way to load a text string first and then the image using AFNetworking?

I'm using AFNetworking to parse JSON to my app (using Rails as my backend). Right now my app is very slow so I'm trying to figure out a way to make it smoother. When I first load the app it takes a few seconds for it to populate (it shows the Nav items and a white page, then a few seconds later my "posts" appear).
Collection View Controller
- (void)viewDidLoad
{
[super viewDidLoad];
self.upcomingReleases = [[NSMutableArray alloc] init];
[self makeReleasesRequests];
[self.collectionView registerClass:[ReleaseCell class] forCellWithReuseIdentifier:#"ReleaseCell"];
}
-(void)makeReleasesRequests
{
NSURL *url = [NSURL URLWithString:#"http://www.soleresource.com/upcoming.json"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"#");
self.upcomingReleases = [responseObject objectForKey:#"upcoming_releases"];
[self.collectionView reloadData];
} failure:nil];
[operation start];
}
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return [self.upcomingReleases count];
}
#pragma mark - Show upcoming release shoe
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"Cell";
ReleaseCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
NSDictionary *upcomingReleaseDictionary = [self.upcomingReleases objectAtIndex:indexPath.row];
NSString *thumbURL = nil;
cell.release_name.text = [NSString stringWithFormat:#"%# — $%#",[upcomingReleaseDictionary objectForKey:#"release_name"], [upcomingReleaseDictionary objectForKey:#"release_price"]];
if ([upcomingReleaseDictionary[#"images"] isKindOfClass:[NSArray class]] && [upcomingReleaseDictionary[#"images"] count]) {
thumbURL = upcomingReleaseDictionary[#"images"][0][#"image_file"][#"image_file"][#"thumb"][#"url"];
if (thumbURL)
{
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:thumbURL]];
UIImage *image = [UIImage imageWithData:imageData];
cell.thumb.image = image;
}
}
else {
cell.thumb.image = [UIImage imageNamed:#"air-jordan-5-fear.png"];
}
return cell;
}
Each of my posts has a text string and a image. Is there a way to load the text so that it appears right away and then load my image? Or is there another way to speed up my app load speed (Maybe loadin a certain of posts first and then loading the rest - the ones that the user cant see until they scroll down).
Thanks.
You should load your image lazily and asynchronously (DON'T block main thread) when coming from server. (AFNetworking already has caching category method on UIImageView. (Check out this for more)
if (thumbURL)
{
[cell.thumb setImageWithURL:[NSURL URLWithString:thumbURL] placeholderImage:[UIImage imageNamed:#"air-jordan-5-fear.png"]];
}
EDIT -
Ensure to pull UIKit+AFNetworking folder into your project and #import "UIKit+AFNetworking.h" into your .m file. The link to download complete AFNetworking can be found here and documentation specific to this question here.
Your problem is this:
if (thumbURL)
{
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:thumbURL]];
UIImage *image = [UIImage imageWithData:imageData];
cell.thumb.image = image;
}
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:thumbURL]];
UIImage *image = [UIImage imageWithData:imageData];
You should never be getting data in cellForItemAtIndexPath:. You should only be displaying what you have already. Your code makes it so no cell is returned until a thumbnail is downloaded. You can measure this using the Time Profiler in Instruments.
I'm assuming thumb is a UIImageView. Try this:
if (thumb) {
[thumb setImageWithURL:[NSURL URLWithString:#"http://i.imgur.com/r4uwx.jpg"]];
}
This method, also included with AFNetworking, will download the image, and update it in the cell once it's done downloading. Documentation and other similar methods are here.

Image not loading immediately in Table View

I'm using SDWebImage and grabbing Images associated with a news article from a news API.
The problem is, the images for the cells on screen aren't loading until I start scrolling on the UITableView. Once I scroll past a cell, and it goes off screen, once I come back to it the Image will finally be loaded.
Here is my (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath code:
if ([feedLocal.images count] == 0) {
[cell.imageView setImage:[UIImage imageNamed:#"e.png"]];
}
else {
Images *imageLocal = [feedLocal.images objectAtIndex:0];
NSString *imageURL = [NSString stringWithFormat:#"%#", imageLocal.url];
NSLog(#"img url: %#", imageURL);
// Here we use the new provided setImageWithURL: method to load the web image
__weak UITableViewCell *wcell = cell;
[cell.imageView setImageWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#", imageURL]]
placeholderImage:[UIImage imageNamed:#"115_64.png"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {
if(image == nil) {
[wcell.imageView setImage:[UIImage imageNamed:#"115_64.png"]];
//];
}
}
];
}
Any idea why this would be happening? It just seems like when the UITableView loads, the Images aren't being told to load or something until scrolling begins?
Any suggestion is much appreciated, thanks!
There is little chance this will solve your problem, but this is too long to fit in a comment:
Tip 1:
If you are reusing cells, you should not do [wcell.imageView setImage:] in the callback. At the time the callback code is executed, there a non-null chance that wcell will point to a different cell in the table view than the one you wanted to change the image.
Instead, use the indexPath to refer to the cell you wanted to modify:
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {
if(image == nil) {
UITableViewCell *actualCell = [tableView cellForRowAtIndexPath:indexPath];
[actualCell.imageView setImage:[UIImage imageNamed:#"115_64.png"]];
}
}
Note that if the cell you wanted to change the image is not shown anymore, cellForRowAtIndexPath: will return nil, which is absolutely fine:
Return Value
An object representing a cell of the table or nil if the cell is not visible or indexPath is out of range.
Tip 2:
There is no need to re-create a string when you already have one ;)
[NSURL URLWithString:[NSString stringWithFormat:#"%#", imageURL]]
[NSURL URLWithString:imageURL] // imageURL is already a string
Your problem:
I'm a little bit puzzled, the code you showed really is a simple application of SDWebImage "how-to" examples, and I just tested with the v3.3 of the framework, and my cells update just fine. So try to reduce your code to the bare minimum to identify the real issue.
I'd say get rid of all your application logic (the feedLocal.images for example), and just find out if the problem actually comes from SDWebImage or not.
Not sure if you have solved your problem, but I get my problem solved by the following code.
Basic idea is to set the cell reference usable inside block and manually set the cell's image in the completed method. Hope it helps.
__block UITableViewCell *cell2 = cell;
id data = [[self itemArray] objectAtIndex:[indexPath item]];
if ([data isKindOfClass:[MyItems class]]) {
MyItems *myData = (MyItems *)data;
[[cell2 imageView] setImageWithURL:[myData url]
placeholderImage:[UIImage imageNamed:#"placeHolder.png"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {
// do check that this is the right cell to put your image
// your methods here
if (image) {
[[cell2 imageView] setImage:image];
}
}];
}
For the checking if that is the right cell, I guess it is something like this (I don't have time to check it)
__block UITableViewCell *cell2 = cell;
id data = [[self itemArray] objectAtIndex:[indexPath item]];
if ([data isKindOfClass:[MyItems class]]) {
__block MyItems *myData = (MyItems *)data;
[[cell2 imageView] setImageWithURL:[myData url]
placeholderImage:[UIImage imageNamed:#"placeHolder.png"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {
// do check that this is the right cell to put your image
// your methods here
id currentData = [[self itemArray] objectAtIndex:[indexPath item]];
if ([currentData isKindOfClass:[MyItems class]] && [ [[(MyItems *)currentData url] absoluteString] isEqualToString:[[myData url] absoluteString] ]) {
// it is the right cell to put in :)
if (image) {
[[cell2 imageView] setImage:image];
}
}
}];
}
I have earlier encountered a similar issue and it turned out the images in the tableview were downloading correctly. The real issue you are facing is the refresh issue. When each image is downloaded, it has to be refreshed in order to be shown in the tableview. In my case, the downloading part was done in a separate file, so i used NSNotificationCenter to tell the tableview controller class to refresh it. Here is what you can do with your code:
if ([feedLocal.images count] == 0) {
[cell.imageView setImage:[UIImage imageNamed:#"e.png"]];
}
else {
Images *imageLocal = [feedLocal.images objectAtIndex:0];
NSString *imageURL = [NSString stringWithFormat:#"%#", imageLocal.url];
NSLog(#"img url: %#", imageURL);
// Here we use the new provided setImageWithURL: method to load the web image
__weak UITableViewCell *wcell = cell;
[cell.imageView setImageWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#", imageURL]]
placeholderImage:[UIImage imageNamed:#"115_64.png"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {
if(image == nil) {
[wcell.imageView setImage:[UIImage imageNamed:#"115_64.png"]];
[[NSNotificationCenter defaultCenter] postNotificationName:#"ImageDownloaded" object:nil];
//];
}
}
];
}
and then you can call reload data using it as below:
- (void) imageDownloaded:(NSNotification *)notification{
[self.tableView reloadData];
}
This way you don't need to scroll in order to see the image, instead they will be shown right after they are downloaded.
Hope this helps!
NSString *mainimg=[NSString stringWithFormat:#"%#",[[xmlDataDictionary valueForKeyPath:#"eg.main.img1"]objectAtIndex:indexPath.row]];
NSURL *url = [NSURL URLWithString:mainimg];
NSData *imge = [[NSData alloc] initWithContentsOfURL:url];
cell.img.image=[UIImage imageWithData:imge];

Resources