SDWebImage loading issue.! - ios

hello i am loading remote images into my collection view with SDWebImage.
for the first time its loading fine like first showing placeholder image and when image is retreived then change the image of cell... but second time i load the same image the top visible rows are not showing image but when i scroll down to other images, they load fine. then i load back to top, images are there.
In my collectionview class -
- (void)startIconDownload:(TrialImages *)appRecord forIndexPath:(NSIndexPath *)indexPath
{
TrialPicDownloader *iconDownloader = [_imageDownloadsInProgress objectForKey:indexPath];
if (iconDownloader == nil)
{
iconDownloader = [[TrialPicDownloader alloc] init];
iconDownloader.productRecord = appRecord;
[iconDownloader setCompletionHandler:^{
MyCollectionViewCell *cell = (MyCollectionViewCell *)[self.myCollectionView cellForItemAtIndexPath:indexPath];
cell.trialImageView.image = appRecord.trialImage;
[_imageDownloadsInProgress removeObjectForKey:indexPath];
}];
[_imageDownloadsInProgress setObject:iconDownloader forKey:indexPath];
[iconDownloader startDownload];
}
}
this method is in TrialPicDownloader class -
- (void)startDownload
{
[[SDWebImageManager sharedManager] downloadWithURL:
[NSURL URLWithString:self.productRecord.TrialImagesUrl]
options:SDWebImageCacheMemoryOnly
progress:nil
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished) {
if (image != NULL) {
self.productRecord.trialImage = image;
if (self.completionHandler)
self.completionHandler();
}
}];
}

NeverMind i was able to solve my issue by this code ...
- (void)startIconDownload:(TrialImages *)appRecord forIndexPath:(NSIndexPath *)indexPath
{
TrialPicDownloader *iconDownloader = [_imageDownloadsInProgress objectForKey:indexPath];
if (iconDownloader == nil)
{
iconDownloader = [[TrialPicDownloader alloc] init];
iconDownloader.productRecord = appRecord;
[iconDownloader setCompletionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
MyCollectionViewCell *cell = (MyCollectionViewCell *)[self.myCollectionView cellForItemAtIndexPath:indexPath];
cell.trialImageView.image = appRecord.trialImage;
[UIView animateWithDuration:0.3f animations:^{
[self.myCollectionView.collectionViewLayout invalidateLayout];
}];
[_imageDownloadsInProgress removeObjectForKey:indexPath];
});
}];
[_imageDownloadsInProgress setObject:iconDownloader forKey:indexPath];
[iconDownloader startDownload];
}
}
i used dispatch_async to process the completion handler on main queue and now its working great..!!

Related

Download images from parse and show in UICollection View

I am downloading the images from parse and after that I am showing that images in uicollection view but when I scroll the collection view it hangs. Here is my code
-(void)viewWillAppear:(BOOL)animated
{
CGRect screen = [[UIScreen mainScreen] bounds];
CGFloat height = CGRectGetHeight(screen);
PFQuery * query = [PFQuery queryWithClassName:#"Static_Wallpaper"];
[query selectKeys:#[#"thumbnail"]];
if (height == 480) {
[query selectKeys:#[#"image4s"]];
}
else{
[query selectKeys:#[#"image6s"]];
}
[query findObjectsInBackgroundWithBlock:^(NSArray * objects, NSError * error){
if (!error) {
[imgArry arrayByAddingObjectsFromArray:objects];
imgArry = [[NSMutableArray alloc] initWithArray:objects];
NSLog(#"%lu",(unsigned long)imgArry.count);
[cllectionView reloadData];
}
}];
}
and Here is the code populating the images in collection view
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return imgArry.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"Cell";
CustomCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
PFObject *imageObject = [imgArry objectAtIndex:indexPath.row];
PFFile *imageFile = [imageObject objectForKey:#"image4s"];
[imageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error){
NSLog(#"is there any data? %#", data);
cell.imgView.image = [UIImage imageWithData:data];
;
}
else {
NSLog(#"no data!");
}
}];
return cell;
}
By adding SDWebimage you need to do code like following:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"Cell";
CustomCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
PFObject *imageObject = [imgArry objectAtIndex:indexPath.row];
PFFile *imageFile = [imageObject objectForKey:#"image4s"];
NSString *UserProfile = [file url];
if([[SDWebImageManager sharedManager] diskImageExistsForURL:[NSURL URLWithString:UserProfile]])
{
NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:[NSURL URLWithString:UserProfile]];
UIImage *image = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:key];
cell.imgView.image=image;
}
else
{
cell.imgView.image=[UIImage imageNamed:#"UserUpdationImageCell.png"];
[cell.imgView sd_setImageWithURL:[NSURL URLWithString:UserProfile] placeholderImage:[UIImage imageNamed:#"UserUpdationImageCell.png"]];
}
return cell;
}
Your download task may block the main thread.You can try to use SDWebImage or asynchrous download task and display image

Slow iOS Share Extension

I'm working on a sharing extension to simply grab a link, choose a few names to share it to, and Share. The data layer isn't added yet, only the UI to display some names in a tableview (using a custom cell) and I'm pulling in the shared URL from the extension context. All of the code in the VC is below. All views are set up in the Storyboard. Two UIButtons, Two UILabels, One TableView and a UIView to hold it all, so I can easily round the corners.
The issue I'm having is that the _linkLabel that I'm using the display the URL doesn't visually update for nearly 10 seconds! What.In.The.World. What I'm a doing here that's causing this?
I'm logging out the URL in the callback from hasItemConformingToTypeIdentifier and it happens as soon as the extension appears, but doesn't update the label??!! Helps. Please.
#import "ShareViewController.h"
#import "UserCell.h"
#interface ShareViewController ()
#end
#implementation ShareViewController
- (void)viewDidLoad{
self.view.alpha = 0;
_friends = [#[#"Ronnie",#"Bobby",#"Ricky",#"Mike"] mutableCopy];
_containerView.layer.cornerRadius = 6.f;
_selectedIndexPaths = [[NSMutableArray alloc] init];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[UIView animateWithDuration:0.5 animations:^{
self.view.alpha = 1;
}];
}
- (void)viewDidAppear:(BOOL)animated{
//pull the URL out
NSExtensionItem *item = self.extensionContext.inputItems[0];
NSItemProvider *provider = item.attachments[0];
if ([provider hasItemConformingToTypeIdentifier:#"public.url"]) {
[provider loadItemForTypeIdentifier:#"public.url" options:nil completionHandler:^(id<NSSecureCoding> item, NSError *error) {
NSURL *url = (NSURL*)item;
_linkLabel.text = url.absoluteString;
NSLog(#"Link: %#", url.absoluteString);
}];
}
else{
NSLog(#"No Link");
}
}
#pragma mark - UITableView Delegate Methods
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
UserCell *cell = (UserCell*)[tableView cellForRowAtIndexPath:indexPath];
if([_selectedIndexPaths containsObject:indexPath]){
[_selectedIndexPaths removeObject:indexPath];
cell.selected = NO;
}
else{
cell.selected = YES;
[_selectedIndexPaths addObject:indexPath];
}
NSLog(#"Share to %i friends", (int)[_selectedIndexPaths count]);
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
//Later, calc height based on text in comment
return 44;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [_friends count];
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"UserCell";
UserCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil){
cell = [[UserCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.selected = ([_selectedIndexPaths containsObject:indexPath]) ? YES : NO;
cell.nameLabel.text = [_friends objectAtIndex:indexPath.row];
return cell;
}
- (IBAction)dismiss {
[UIView animateWithDuration:0.34 animations:^{
self.view.alpha = 0;
} completion:^(BOOL finished) {
[self.extensionContext completeRequestReturningItems:nil completionHandler:nil];
}];
}
#end
Delays in updates to UI elements is a classic sign of trying to update the UI from outside the main queue. Which is what is happening here. You have this:
[provider loadItemForTypeIdentifier:#"public.url" options:nil completionHandler:^(id<NSSecureCoding> item, NSError *error) {
NSURL *url = (NSURL*)item;
_linkLabel.text = url.absoluteString;
NSLog(#"Link: %#", url.absoluteString);
}];
Except that NSItemProvider does not guarantee that the completion handler will be called on the same queue that you started on. You're almost guaranteed to be on a different queue here, so you're getting this weird delay. You need to dispatch back to the main queue to perform the update:
[provider loadItemForTypeIdentifier:#"public.url" options:nil completionHandler:^(id<NSSecureCoding> item, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
NSURL *url = (NSURL*)item;
_linkLabel.text = url.absoluteString;
NSLog(#"Link: %#", url.absoluteString);
});
}];

Icon downloader downloads images only when UITableViewcell is outside screen.iPhone

I tried searching but did not find any solutions helpful.
I am using the following code for icons lazy loading.
The issue is, the icons are downloaded via lazy loading, but they are only seen once that particular cell is out of screen and is scrolled back into the screen.
I think it is some issue with dequeueReusableCellWithIdentifier but am not sure how to resolve it.
The images are downloaded alright, but are only visible in the cell once the cell goes out of screen.
// -------------------------------------------------------------------------------
// tableView:cellForRowAtIndexPath:
// -------------------------------------------------------------------------------
- (UITableViewCell *)tableView:(UITableView *)tableVw cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// customize the appearance of table view cells
//
static NSString *CellIdentifier = #"LazyTableCell";
static NSString *PlaceholderCellIdentifier = #"PlaceholderCell";
// add a placeholder cell while waiting on table data
NSUInteger nodeCount = [dataArray count];
if (nodeCount == 0 && indexPath.row == 0)
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:PlaceholderCellIdentifier];
cell.detailTextLabel.text = #"Loading…";
return cell;
}
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.backgroundColor = [UIColor grayColor];
// Leave cells empty if there's no data yet
if (nodeCount > 0)
{
// Set up the cell...
AppRecord *appRecord = [dataArray objectAtIndex:indexPath.row];
cell.textLabel.text = appRecord.appName;
cell.detailTextLabel.text = appRecord.artist;
// Only load cached images; defer new downloads until scrolling ends
if (!appRecord.appIcon)
{
if (tableView.dragging == NO && tableView.decelerating == NO)
{
[self startIconDownload:appRecord forIndexPath:indexPath];
}
// if a download is deferred or in progress, return a placeholder image
cell.imageView.image = [UIImage imageNamed:#"Placeholder.png"];
}
else
{
cell.imageView.image = appRecord.appIcon;
}
}
return cell;
}
- (void)startIconDownload:(AppRecord *)appRecord forIndexPath:(NSIndexPath *)indexPath
{
IconDownloader *iconDownloader = [imageDownloadsInProgress objectForKey:indexPath];
if (iconDownloader == nil)
{
iconDownloader = [[IconDownloader alloc] init];
iconDownloader.appRecord = appRecord;
[iconDownloader setCompletionHandler:^{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
// Display the newly loaded image
cell.imageView.image = appRecord.appIcon;
// Remove the IconDownloader from the in progress list.
// This will result in it being deallocated.
[imageDownloadsInProgress removeObjectForKey:indexPath];
}];
[imageDownloadsInProgress setObject:iconDownloader forKey:indexPath];
[iconDownloader startDownload];
}
}
- (void)loadImagesForOnscreenRows
{
if ([dataArray count] > 0)
{
NSArray *visiblePaths = [tableView indexPathsForVisibleRows];
for (NSIndexPath *indexPath in visiblePaths)
{
AppRecord *appRecord = [dataArray objectAtIndex:indexPath.row];
if (!appRecord.appIcon)
// Avoid the app icon download if the app already has an icon
{
[self startIconDownload:appRecord forIndexPath:indexPath];
}
}
}
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (!decelerate)
{
[self loadImagesForOnscreenRows];
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self loadImagesForOnscreenRows];
}
I did code like following,
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadWithURL:aURL
options:0
progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished)
{
if (image)
[aCell.imgViewThumb setImage:image];
else
[aCell.imgViewThumb setImage:[UIImage imageNamed:#"Dummy-image.jpg"]];
[aCell.indicator stopAnimating];
}];

Implementing tableView:cellForRowAtIndexPath: with a ALAssetRepresentation image

Here's my method inside of the UITableViewDataSource view controller
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"studentCell";
StudentTableCell *cell = (StudentTableCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
// Never gets called
}
Student *student = self.studentList[indexPath.row];
cell.nameFirst.text = student.nameFirst;
cell.nameLast.text = student.portrait.assetURL;
// Portrait
CGImageRef portraitRef = [cell.portrait.image CGImage];
CIImage *portraitImage = [cell.portrait.image CIImage];
if (portraitRef == NULL && portraitImage == nil) {
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library assetForURL:[NSURL URLWithString:student.portrait.assetURL] resultBlock:^(ALAsset *asset) {
ALAssetRepresentation *representation = [asset defaultRepresentation];
CGImageRef assetRef = [representation fullResolutionImage];
if (assetRef) {
[cell.portrait setImage:[UIImage imageWithCGImage:assetRef]];
}
} failureBlock:^(NSError *error) {}];
}
return cell;
}
This works as expected for the first few rows that fit inside of the initial scroll position of the table.
But as I scroll down, cell.nameFirst.text change as expected, while cell.portrait.image gets recycled and starts repeating the images loaded inside of the first scroll position.
Questions
How do I make sure every cell has the appropriate image
Can cell every be nil?
You need to update image whether it is set or not. Your code only sets the image if there isn't one already. Cells get reused as you scroll. So each cell needs to be updated with the image appropriate for the indexPath.
Also note that assetForURL:resultBlock:failureBlock:. It's asynchronous. This means you need to update the cell on the main thread once you get the image in the resultBlock.
cell.nameFirst.text = student.nameFirst;
cell.nameLast.text = student.portrait.assetURL;
// Portrait
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library assetForURL:[NSURL URLWithString:student.portrait.assetURL] resultBlock:^(ALAsset *asset) {
ALAssetRepresentation *representation = [asset defaultRepresentation];
CGImageRef assetRef = [representation fullResolutionImage];
if (assetRef) {
dispatch_async(dispatch_get_main_queue(), ^{
[cell.portrait setImage:[UIImage imageWithCGImage:assetRef]];
});
}
} failureBlock:^(NSError *error) {}];
return cell;
The best way to make sure every cell has the appropriate image is create dictionary and in cellForRowAtIndexPath: check value(image) in the dictionary object at key (I like use indexPath.row as a key). If it is set it up for the cell if it isn't call:
[library assetForURL:[NSURL URLWithString:student.portrait.assetURL] resultBlock:^(ALAsset *asset) {...
and once image is downloaded add it to the dictionary with key (indexPath.row).
You should reload the cell when you download your image, just remember to do it on the main thread.
I would suggest to utilize an image cache. Suppose the image cache has the following API:
typedef void (^completion_t)(id result, NSError* error);
#interface SimpleImageCache : NSObject
/**
Returns the image for the specified URL if it exists, otherwise nil.
*/
- (UIImage*) imageWithURL:(NSURL*)url;
/**
Asychronounsly loads the image from the asset library. The compeltion handler will
be called when the image is available or when an error occured.
The execution context where the compeltion handler will be executed is
implementation defined.
*/
- (void) loadImageWithURL:(NSURL*)url completion:(completion_t)completionHandler;
#end
In your code you would use it as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"studentCell";
StudentTableCell *cell = (StudentTableCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
// Never gets called
}
Student *student = self.studentList[indexPath.row];
cell.nameFirst.text = student.nameFirst;
cell.nameLast.text = student.portrait.assetURL;
// Portrait
NSURL* url = [NSURL URLWithString:student.portrait.assetURL];
UIImage* portrait = [self.imageCache imageWithURL:url];
if (portrait == nil) {
portrait = self.placeholderImage;
[self.imageCache loadImageWithURL:url completion:^(id image, NSError*error){
dispatch_async(dispatch_get_main_queue(), ^{
StudentTableCell* cell = (StudentTableCell *)[tableView cellForRowAtIndexPath:indexPath];
[cell.portrait setImage:image];
});
}];
}
[cell.portrait setImage:portrait];
return cell;
}
Implementation of SimpleImageCache
Warning: It's not tested, but it might give you a jump start, or an idea.
#interface SimpleImageCache ()
#property (nonatomic, strong) NSMutableDictionary* images;
#property (nonatomic, strong) ALAssetsLibrary* assetsLibrary;
#property (nonatomic, strong) UIImage* missingImage;
#end
#implementation SimpleImageCache {
dispatch_queue_t _sync_queue;
}
- (id)init {
self = [super init];
if (self) {
_sync_queue = dispatch_queue_create("sync_queue", NULL);
}
return self;
}
- (NSMutableDictionary*) images {
if (_images == nil) {
_images = [[NSMutableDictionary alloc] init];
}
return _images;
}
- (ALAssetsLibrary*) assetsLibrary {
if (_assetsLibrary == nil) {
_assetsLibrary = [[ALAssetsLibrary alloc] init];
}
return _assetsLibrary;
}
- (UIImage*) imageWithURL:(NSURL*)url {
__block UIImage* image;
dispatch_sync(_sync_queue, ^{
id obj = self.images[url];
if ([obj isKindOfClass:[UIImage class]]) {
image = obj;
}
});
return image;
}
- (void) loadImageWithURL:(NSURL*)url completion:(completion_t)completionHandler {
dispatch_async(_sync_queue, ^{
if (self.images[url] != nil) {
return;
}
self.images[url] = #"pending";
[self.assetsLibrary assetForURL:url resultBlock:^(ALAsset *asset) {
ALAssetRepresentation* representation = [asset defaultRepresentation];
__block UIImage* image = CFBridgingRelease([representation fullResolutionImage]);
dispatch_async(_sync_queue, ^{
if (image == NULL) {
image = self.missingImage;
NSAssert(image, #"image is nil");
}
self.images[url] = image;
if (completionHandler) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
completionHandler(image, nil);
});
}
});
} failureBlock:^(NSError *error) {
if (completionHandler) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
completionHandler(nil, error);
});
}
}];
});
}
#end

Custom UICollectionViewCell with UIProgressView shows progressView on reused cells

I am subclassing UICollectionViewCell because I wanted to add a UIImageView and a UIProgressView to the cell:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// ImageView
_imageView = [[UIImageView alloc] initWithFrame:self.bounds];
_imageView.layer.borderColor = [UIColor whiteColor].CGColor;
_imageView.layer.borderWidth = 0.7;
[self.contentView addSubview:_imageView];
// Progress View
_progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleBar];
_progressView.frame = CGRectMake(5, self.bounds.size.height - 20, self.bounds.size.width - 10, 10);
_progressView.hidden = YES;
[self.contentView addSubview:_progressView];
}
return self;
}
When I touch a cell and it calls collectionView:didSelectItemAtIndexPath: I set cell.progressView.hidden = NO; and start my download and updating of the progressView.
But, as I scroll that cell is reused and the progressView is shown on other cells. I have tried a number of different things to only show it on the correct cell, but nothing I have tried is working.
Is there a better way to do this such as doing something in prepareForReuse?
EDIT: Full methods as requested
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
PUCViewpointItem *item = [self.items objectAtIndex:indexPath.row];
PUCImageGridCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CollectionViewCellIdentifier forIndexPath:indexPath];
[cell.imageView setImageWithURL:[NSURL URLWithString:item.imageURLHigh] placeholderImage:[UIImage imageNamed:#"PUCDefaultBackground.png"]];
// See if we have the file already
if (![self.itemPaths objectForKey:item.name]) {
cell.imageView.alpha = 0.4;
} else {
cell.imageView.alpha = 1.0;
}
// See if we are downloading
if (![self.progressItems objectForKey:item.name]) {
cell.progressView.hidden = YES;
} else {
cell.progressView.hidden = NO;
}
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
PUCViewpointItem *item = [self.items objectAtIndex:indexPath.row];
PUCImageGridCell *cell = (PUCImageGridCell *)[collectionView cellForItemAtIndexPath:indexPath];
// File Path
NSString *path = [self itemPath:item];
if (!path) {
// Set the indexPath we are downloading
[self.progressItems setObject:indexPath forKey:item.name];
Utility *utility = [[Utility alloc] init];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:item.url]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
NSString *localPath = [[utility localDirectory] stringByAppendingFormat:#"/%#.pdf", item.name];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:localPath append:NO];
[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
float totalProgress = (float)totalBytesRead/(float)totalBytesExpectedToRead;
if ([[collectionView indexPathForCell:cell] isEqual:indexPath]) {
cell.progressView.alpha = 1.0;
cell.progressView.progress = totalProgress;
}
}];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// This section seems to not get called or updated correctly when the cell
// that is showing the activityView is offscreen
if ([[collectionView indexPathForCell:cell] isEqual:indexPath]) {
[self.itemPaths setObject:localPath forKey:item.name];
[self.progressItems removeObjectForKey:item.name];
cell.imageView.alpha = 1.0;
cell.progressView.alpha = 0.0;
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
[self.progressItems removeObjectForKey:item.name];
cell.progressView.alpha = 0.0;
}];
[operation start];
} else {
[self readIssueAtPath:path];
}
}//end
The information whether a download is in progress for a certain item or not should be stored
in some data source (or model) and not in the cell (the view).
Then you can update the cell's appearance in the data source delegate method collectionView:cellForItemAtIndexPath: according to the status of the item at that index
path and show or hide the progress view of the cell.
ADDED: The progress, completion and failure block all capture the current cell.
Therefore they will modify this cell even if it has been reused for a different index path.
To solve that, you can check if the cell's (current) index path is still equal
to the original (captured) index path:
[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
float totalProgress = (float)totalBytesRead/(float)totalBytesExpectedToRead;
if ([[collectionView indexPathForCell:cell] isEqual:indexPath]) {
cell.progressView.alpha = 1.0;
cell.progressView.progress = totalProgress;
}
}];
and similar for the completion and failure block:
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[self.itemPaths setObject:localPath forKey:item.name];
[self.progressItems removeObjectForKey:item.name];
if ([[collectionView indexPathForCell:cell] isEqual:indexPath]) {
cell.imageView.alpha = 1.0;
cell.progressView.alpha = 0.0;
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
[self.progressItems removeObjectForKey:item.name];
if ([[collectionView indexPathForCell:cell] isEqual:indexPath]) {
cell.progressView.alpha = 0.0;
}
}];
I think what you need to do is only refer to the cell once at the beginning of your didSelectRowAtIndexPath method, and create a property to keep track of the selected indexPath and the value of the progress indicator. The method below works, but I had to create a dummy slow method to test it out, so I hope you can see how to adapt this to your problem.
#interface TableController ()
#property (strong,nonatomic) NSArray *theData;
#property (strong,nonatomic) NSIndexPath *downloadingCellPath;
#property (nonatomic) float progValue;
#end
#implementation TableController
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
RDCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.label.text = self.theData[indexPath.row];
if ([self.downloadingCellPath isEqual:indexPath]) {
cell.progView.hidden = NO;
}else{
cell.progView.hidden = YES;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
self.downloadingCellPath = indexPath;
[self.tableView reloadData];
[self longMethod:indexPath];
}
-(void)longMethod:(NSIndexPath *) indexPath {
UIProgressView *activeProgressView = [(RDCell *)[self.tableView cellForRowAtIndexPath:indexPath] progView];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
float x = 0;
for (int i=0; i < 40000000; i++) {
x = i * 3.14;
}
dispatch_async(dispatch_get_main_queue(), ^{
self.progValue +=.01;
activeProgressView.progress = self.progValue;
if (self.progValue < .99){
[self longMethod:indexPath];
}else{
NSLog(#"done");
self.downloadingCellPath = nil;
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
});
});
}
[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
float totalProgress = (float)totalBytesRead/(float)totalBytesExpectedToRead;
if ([[collectionView indexPathForCell:cell] isEqual:indexPath]) {
cell.progressView.alpha = 1.0;
cell.progressView.progress = totalProgress;
}
}];

Resources