my replaceObjectatIndex:withObject: is not being called when I put it inside a block. I know this because when I NSLog in the outer block the value doesn't change. why is the method inside the inner block not being called while the method in the outer block does? what's the difference?
this is the code:
if (cell.selected) {
[[SDImageCache sharedImageCache] queryDiskCacheForKey:imageID
done:^(UIImage *image, SDImageCacheType cacheType)
{
// image is not nil if image was found
if (image == nil) {
//image is not found
[SDWebImageDownloader.sharedDownloader downloadImageWithURL:[NSURL URLWithString:link]
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize)
{
// progression tracking code
}
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished)
{
if (image && finished)
{
// image is finished being downloaded
// resize image
UIImage *resizedImage = [self imageWithImage:image forRowAtIndexPath:indexPath];
// store resized image in cache
[[SDImageCache sharedImageCache] storeImage:resizedImage forKey:imageID];
//set image view to resized image
[textCell.testImage setImage:resizedImage];
[self.heightArray replaceObjectAtIndex:indexPath.row
withObject:[NSNumber numberWithFloat:image.size.height]];
}
//delete original sized image
image = nil;
}];
} else {
//image is found
[textCell.testImage setImage:image];
NSLog(#"image found %#", [self.heightArray objectAtIndex:indexPath.row]);
}
}];
} else {
//cell is not selected
textCell.testImage.image = nil;
}
By the way, the setImage: method works perfectly but not replaceObjectatIndex:withObject:
I don't know what you are doing but I found an issue in your code: you are using the image even after you set it to nil.
Correction:
//delete original sized image -
//??image = nil;
[self.heightArray replaceObjectAtIndex:indexPath.row
withObject:[NSNumber numberWithFloat:image.size.height]];
image = nil;
Related
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];
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.
I have a class method which fetches images with a completion block. This fetched UIImage is added to an NSCache with a relevant key. This seems to work as expected, however, in the method which fetches images I am using a UIImage's imageWithData: method, which I have discovered does not cache it's data, only imageNamed: does.
I am understandably getting memory warnings because of this, how do I make sure the images loaded with UIImage's imageWithData: method are removed from memory when not needed anymore?
EDIT
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);
});
});
}
});
});
}
}
EDIT 2
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;
}
Instead of rolling your own image downloading and caching solution you might be better off using SDWebImage. Then you don't have to worry about the downloading, caching or anything. SDWebImage also using disk caching so you don't have to worry about freeing memory.
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadWithURL:imageURL options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize)
{
// progression tracking code
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished)
{
if (image)
{
// do something with image
}
}];
I'm not sure but you also might have a retain cycle:
__weak typeof(self) weakSelf = self;
[photo imageForFootageSize:[Footage footageSizeThatBestFitsRect:self.bounds] withCompletionHandler:^(UIImage *image) {
if ([weakSelf.delegate respondsToSelector:#selector(galleryPhotoCollectionViewCell:didLoadImage:)])
{
[weakSelf.delegate galleryPhotoCollectionViewCell:weakSelf didLoadImage:image];
}
image = nil;
}];
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];
I'm creating a PFImageView with a default image. I'm then loading an image in the background and when the image is finished downloading from the server it is immediately shown and the placeholder/default image is replaced. The problem is that the transition from the default to the downloaded image isn't very pleasant. Is there a way to create a fade out of the default and fade in to the downloaded animation?
Here's my code.
// set the pfImageView into the webView
_pfImageView = [[PFImageView alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:#"%dpTall.jpg", [self.drink.glassId intValue]]]];
_pfImageView.frame = CGRectMake(244, 0, 76, 114);
[webView.scrollView addSubview:_pfImageView];
// create a query for finding images on Parse
PFQuery *query = [PFQuery queryWithClassName:#"Images"];
[query whereKey:#"drinkId" equalTo:[NSNumber numberWithInt:self.drink.primaryKey]];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!error) {
PFFile *imageFile = [object objectForKey:#"image"];
_pfImageView.file = imageFile;
[_pfImageView loadInBackground];
} else {
NSLog(#"PFQuery Error:%#", [error localizedDescription]);
}
}];
Thanks
I had a category on PFImageView to get a cross fade animation each time an image appears :
#implementation PFImageView (Utility)
// Add a fade animation to images
-(void) setImage:(UIImage *)image {
if ([self.layer animationForKey:KEY_FADE_ANIMATION] == nil) {
CABasicAnimation *crossFade = [CABasicAnimation animationWithKeyPath:#"contents"];
crossFade.duration = 0.6;
crossFade.fromValue = (__bridge id)(self.image.CGImage);
crossFade.toValue = (__bridge id)(image.CGImage);
crossFade.removedOnCompletion = NO;
[self.layer addAnimation:crossFade forKey:KEY_FADE_ANIMATION];
}
[super setImage:image];
}
#end
You just need a placeholder image so the animation can occur the first time.
In my opinion I think it would be easier to drop the dependency on PFImageView and just use a normal UIImageView and set that with a place holder first, like you are currently doing. Then in your getFirstObjectInBackgroundWithBlock completion block starting at the IF statement do something like this....
if(!error)
{
PFFile *imageFile = [object objectForKey:#"image"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [imageFile getData];
if(data)
{
dispatch_async( dispatch_get_main_queue(), ^{
[UIView animateWithDuration:2.0
animations:^{
//imageView is your UIImageView you are trying to set/change
imageView.alpha = 0.0f;
}
completion:^(BOOL finished){
[UIView animateWithDuration:2.0
animations:^{
imageView.image = [UIImage imageWithData:data];
imageView.alpha = 1.0f;
}
completion:^(BOOL finished)
{
}];
}];//outside block
});
}
else
{
//no data error handling here
}
});
else
{
//error handling here
}
disclosure: I am not near a compiler so I did not run this code yet.
You also instead of doing the outside dispatch_async you could just do use the imageFile getdata method call with a completion block (its on parse.com website API) and then put everything from if(data) down in that block instead of the async block I made. I'm just used to doing it this way.