How to show an UIActivityindicator in UICollectionview customcell untill the image downloads at server side? - objective-c

Can Any one help me how to show the activity indicator until the image for UICollection cell downloads at back end.
In my code the activity indicator is show only for last cell..Don't know where I m making the mistake
Here is my code:
- (collectionCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath{
[self.menuCollectionView registerNib:[UINib nibWithNibName:#"collectionCell"
bundle:nil] forCellWithReuseIdentifier:#"CELL"];
collectionCell= [menuCollectionView dequeueReusableCellWithReuseIdentifier:#"CELL"
forIndexPath:indexPath];
MenuItems *item=[itemsfinal objectAtIndex:indexPath.row];
NSMutableString *str = [NSMutableString stringWithFormat:#"%#", item.itemImage];
NSLog(#" url %#",str);
UIImage *image = [UIImage imageWithContentsOfFile:[self loadImage:str]];
if(image !=nil){
collectionCell.menuRecipeImage.image = image;
collectionCell.activityIndicator.hidden = YES;
[collectionCell.activityIndicator stopAnimating];
}else{
collectionCell.activityIndicator.hidden = NO;
[collectionCell.activityIndicator startAnimating];
collectionCell.menuRecipeImage.image = [UIImage imageNamed:#"menudefualt.png"];
}
return collectionCell;
}

In cellForItemAtIndexPath you should set up your activity indicator. Then start loading your image for the cell in the background. When the image have loaded apply it to the cell on the main thread.
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
__block UICollectionViewCell* cell = [cv dequeueReusableCellWithReuseIdentifier:#"cell"
forIndexPath: indexPath];
// Placeholder text --
UILabel* label = [[UILabel alloc] initWithFrame:cell.bounds];
label.text = #"Downloading...";
[cell.contentView addSubview:label];
// Load image in background --
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSURL* url = [NSURL URLWithString: [NSString stringWithFormat:#"http://example.com/img/img%02lu.png", (long unsigned)indexPath.row]];
// Load and decode image --
NSData * imageData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData];
// Apply image on the main thread --
dispatch_sync(dispatch_get_main_queue(), ^{
UIImageView* iv = [[UIImageView alloc] initWithImage:image];
[cell.contentView addSubview:iv];
});
});
return cell;
}
The example in action...

One thing about Niels example is that this may cause an incorrect image to be set for a cell, if the cell is re-used before the image is completely loaded (eg. if you're scrolling quickly). So you need to keep a map of what URL should be set for each cell, so roughly modifying Niels' example above:
#property NSDictionary *cellMap;
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
__block UICollectionViewCell* cell = [cv dequeueReusableCellWithReuseIdentifier:#"cell"
forIndexPath: indexPath];
// Placeholder text --
UILabel* label = [[UILabel alloc] initWithFrame:cell.bounds];
label.text = #"Downloading...";
[cell.contentView addSubview:label];
NSURL* url = [NSURL URLWithString: [NSString stringWithFormat:#"http://example.com/img/img%02lu.png", (long unsigned)indexPath.row]];
[cellMap setObject:url forKey:cell];
// Load image in background --
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// Load and decode image --
NSData * imageData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData];
// Apply image on the main thread --
dispatch_sync(dispatch_get_main_queue(), ^{
NSURL *cellUrl = [cellMap objectForKey:cell];
if (cellUrl == url) {
// Only set the image if the url is still the same for this cell
UIImageView* iv = [[UIImageView alloc] initWithImage:image];
[cell.contentView addSubview:iv];
}
});
});
return cell;
}

You should use async image load. It can be done with GCD
//First start your activityIndicator
collectionCell.activityIndicator.hidden = NO;
[collectionCell.activityIndicator startAnimating];
//Then using GCD load your image on secondary thread
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//Here your image loading from url
UIImage *image = [UIImage imageWithContentsOfFile:[self loadImage:str]];
dispatch_async(dispatch_get_main_queue(), ^{
//This block (Main thread) waits until your image will be downloaded
//(Note that all operation with views must be executed on a main thread)
//Then loading is done just set your image and stop activityIndicator
collectionCell.menuRecipeImage.image = image;
collectionCell.activityIndicator.hidden = YES;
[collectionCell.activityIndicator stopAnimating];
});
});

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section{
return _items.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
ImageFilterCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"ImageFilterCell" forIndexPath:indexPath];
cell.lblEffectName.text = [_itemsName objectAtIndex:indexPath.row];
cell.imgLoader.hidden = NO;
cell.imgFilter.image = nil;
NSString *effectName = _items[indexPath.row];
if([effectName isEqualToString:#"Original"]){
cell.imgLoader.hidden = YES;
cell.imgFilter.image = _imgThumb;
}
else {
UIImage *filteredImage = [_filteredImages objectForKey:effectName];
if(filteredImage){
cell.imgLoader.hidden = YES;
cell.imgFilter.image = filteredImage;
} else {
__weak ImageFilterCell *weakCell = cell;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self getFilteredImageForEffect:effectName forImage:_imgThumb completionBlock:^(UIImage *image) {
dispatch_async(dispatch_get_main_queue(), ^{
weakCell.imgLoader.hidden = YES;
weakCell.imgFilter.image = image;
[_filteredImages setObject:image forKey:effectName];
});
}];
});
}
}
if(_checkedIndexPath==indexPath.row)
cell.highlighted = YES;
else
cell.highlighted = NO;
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
if(_checkedIndexPath != indexPath.row && indexPath.row!=0){
_checkedIndexPath = indexPath.row;
NSString *effectName = _items[indexPath.row];
if([_delegate respondsToSelector:#selector(showIndicator)]){
[_delegate showIndicator];
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self getFilteredImageForEffect:effectName forImage:_orgImage completionBlock:^(UIImage *image) {
dispatch_async(dispatch_get_main_queue(), ^{
if([self.delegate respondsToSelector:#selector(filterImageView:filteredImage:)]){
[self.delegate filterImageView:self filteredImage:image];
}
});
}];
});
} else if (indexPath.row == 0) {
_checkedIndexPath = 0;
if([self.delegate respondsToSelector:#selector(filterImageView:filteredImage:)]){
[self.delegate filterImageView:self filteredImage:_orgImage];
}
}
[_collectionView reloadData];
}

Related

Best Way utilize PHPhotoLibrary for display Camera Roll image in UICollectionView with custom Cell

Hello everyone I have a problem with my app ... Within my View Controller hp a CollectionView with a custom cell that should return all of the photos in the Camera Roll section of the app pictures of my iphone.
Now I've done all the steps to show the photos in a ImageView in the custom cell and up to now I have no problem ... My problem is that when I start to scroll through photos, uploading photos is very slow and immediately after the app crashes giving me back this error in the log ..
[GatekeeperXPC]
Connection to assetsd was interrupted or assetsd died 25/02/2017 20:
[Generic] Creating an image
format with an unknown type is an error
Can you tell me if I've taken the right way to show pictures in my collection view? Where did I go wrong? because my app crashes?
Thank you all for any help you can give me
This is the code i utilize
- (void)viewDidLoad {
[super viewDidLoad];
self.nameTextField.delegate = self;
self.emailTextField.delegate = self;
self.passwordTextField.delegate = self;
self.collectionView.delegate = self;
self.collectionView.dataSource = self;
_collectionView.backgroundColor = [UIColor clearColor];
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[_nameTextField becomeFirstResponder];
[self queryImage];
}
-(void)queryImage {
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
PHFetchResult *collection = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumUserLibrary options:fetchOptions];
if (collection.firstObject != nil ) {
_photoFound = YES;
_assetCollection = collection.firstObject;
} else {
}
_photoAsset = [PHAsset fetchAssetsInAssetCollection:_assetCollection options:nil];
[_collectionView reloadData];
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return CGSizeMake(80,80);
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
NSUInteger count = 0;
if (_photoAsset != nil) {
count = [_photoAsset count];
}
return count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static NSString *reuseIdentifier = #"imageCell";
UPCameraRollCollectionViewCell* cell = [cv dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
cell.backgroundColor = [UIColor redColor];
PHAsset *asset = [_photoAsset objectAtIndex:indexPath.item];
PHImageManager *imageManager = [PHImageManager defaultManager];
[imageManager requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFill options:nil resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
[cell setThumbnailImage:result];
}];
return cell;
}
Use PHCachingImageManager.
Apple has an example that shows exactly how to do the sort of thing you're after. Collection views are precisely the intended use case.

tableview load image async but sometimes some cell will never load successfully?

I want tableview to load images async,
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
StickerListItemView *rowView = [tableView makeViewWithIdentifier:StickerTableViewIdentifier
owner:self];
if (!rowView) {
rowView = [[StickerListItemView alloc] initWithFrame:NSMakeRect(0, 0, 460, 300)];
rowView.identifier = StickerTableViewIdentifier;
id obj = [self.stickerListArray objectAtIndex:row];
if ([obj isKindOfClass:[StickerListModel class]]) {
StickerListModel *listModel = (StickerListModel *)obj;
rowView.productId = listModel.productId;
rowView.title = listModel.name;
rowView.intro = listModel.intro;
rowView.status = [self hasStickersDownloaded:listModel.productId];
[rowView setDelegate:self];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^(void) {
NSImage *image = [self getPreviewImageWithMd5:listModel.md5OfPrieviewImage];
//****************************************
NSLog(#"get image at index %ld",(long)row);
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
rowView.image = image;
[rowView setNeedsDisplay:YES];
//**************************************
NSLog(#"set image at row %ld",(long)row);
});
}
});
}
}
return rowView;
}
Sometimes, it will load successfully, but sometimes one cell will never load the image, just like the picture below: the
I wait for long time, but the third image never loaded.
It looks like this line is returning nil.
NSImage *image = [self getPreviewImageWithMd5:listModel.md5OfPrieviewImage];
You should check the value of image to make sure it's not nil and possibly add some error handling. Maybe something like this.
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
rowView.image = image;
[rowView setNeedsDisplay:YES];
NSLog(#"set image at row %ld",(long)row);
});
} else {
NSLog(#"Received nil image for row %ld", (long)row);
//possibly set a placeholder image for this row instead
}

Objective C model - How to download image inside a model class?

I have the following model of a box, it is meant to download images in a background thread and create an image from this downloaded image.
The viewcontroller has a custom uicollectioncell, but its just a uiimageview; nothing too complex.
In the cellForItemAtIndexPath I want to assign the cell's imageview using the model's downloaded image.
However, its not quite working;
The image never appears
If I move the background image downloader to the cellForItemAtIndexPath and change a few items then the image loads fine.
But what I'm wanting is to seperate the ViewContoller and the model; the model should do the heavy lifting and the viewcontroller simply handles the display.
Code follows
// ViewController: View Did Load
- (void)viewDidLoad {
[super viewDidLoad];
if (!self.picturesArray) self.picturesArray = [NSMutableArray arrayWithCapacity:kNumberOfCells];
self.collectionView.delegate = self;
self.collectionView.dataSource = self;
self.collectionView.backgroundColor = [UIColor clearColor];
for (int i=0; i<kNumberOfCells; i++)
{
Box *box = [[Box alloc] init];
[self.picturesArray addObject:box];
box = nil;
}
}
// ViewController : collectionView delegage
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCustomCollectionViewCell *cell = (MyCustomCollectionViewCell *) [collectionView dequeueReusableCellWithReuseIdentifier:cellId forIndexPath:indexPath];
Box *box = self.picturesArray[indexPath.row];
cell.backgroundColor = box.bgColor;
cell.imageView.image = box.image;
return cell;
}
The box model is as follows
// Box model with an image property
- (instancetype)init
{
self = [super init];
if (self)
{
NSURL *url = [NSURL URLWithString:kUrlString];
// Block variable to be assigned in block.
__block NSData *imageData;
dispatch_queue_t backgroundQueue = dispatch_queue_create("imagegrabber.bgqueue", NULL);
// Dispatch a background thread for download
dispatch_async(backgroundQueue, ^(void) {
imageData = [NSData dataWithContentsOfURL:url];
if (imageData.length >0)
{
// self.image is a property of the box model
self.image = [[UIImage alloc] initWithData:imageData];
// Update UI on main thread
dispatch_async(dispatch_get_main_queue(), ^(void) {
});
}
});
}
return self;
}
My question is this:
How do I get the box model to download the image and then in my cellAtIndexPath make the cell's imageView assign its image from that downloaded boxmodel image?
A further unrelated question
Isn't it best practice to seperate the model from the actual downloading of items? But if I'm not meant to put this in the view controller, and not the model where does it go and how/whem would you call it?
Thanks for now
Separating the model from its image file and keeping images in a cache with a key which is model's image URL, is better approach.
Also, putting initialization method in your MyCustomCollectionViewCell is better way to configure your cell.
I suggest you to use https://github.com/rs/SDWebImage (or similar libraries) to download and cache images.
MyCustomCollectionViewCell.h :
#interface MyCustomCollectionViewCell : UITableViewCell
-(void) initializeWithBox:(Box*)box;
MyCustomCollectionViewCell.m with SDWebImage:
#implementation MyCustomCollectionViewCell
-(void) initializeWithBox:(Box*)box
{
[self setBackgroundColor:[box bgColor]];
[self.imageView sd_setImageWithURL:[NSURL URLWithString:[box kUrlString]] placeholderImage:nil];
}
MyCustomCollectionViewCell.m without libraries:
#implementation MyCustomCollectionViewCell
-(void) initializeWithBox:(Box*)box
{
[self setBackgroundColor:[box bgColor]];
__weak typeof(self) weakSelf = self;
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:[box kUrlString]] 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(), ^{
typeof(self) strongSelf = weakself;
if(strongSelf)
[self.imageView setImage:image];
});
}
}
}];
[task resume];
}
In your view controller:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCustomCollectionViewCell *cell = (MyCustomCollectionViewCell *) [collectionView dequeueReusableCellWithReuseIdentifier:cellId forIndexPath:indexPath];
Box *box = self.picturesArray[indexPath.row];
[cell initializeWithBox:box]
return cell;
}

UITableView reloadData lose elements from cell

I have a UITableView and his datasource is an NSMutableArray declared in my interface:
#property (strong, nonatomic) NSMutableArray *arrElements;
Now I implemented a simple "load more" in this way:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
float endScrolling = scrollView.contentOffset.y + scrollView.frame.size.height;
if (endScrolling >= scrollView.contentSize.height)
{
//only if i have others results i will call load more
if([self.items_amount integerValue] > self.arrElements.count) {
if(DEBUG_MODE == 1) {
NSLog(#"Load more");
}
//get start param from array size and call load more
NSNumber *start = [NSNumber numberWithInteger:([self.arrElements count]+1)];
NSArray *passParams = [NSArray arrayWithObjects:self.menuItem,start,[NSNumber numberWithInteger:NUM_RESULTS_FOR_PAGE], nil];
[self performSelector:#selector(loadMore:) withObject:passParams afterDelay:0.1];
}
}
}
and this is my loadMore method:
//load more elements
- (void)loadMore:(NSArray *)arrParams {
//(MenuItem *)menuItem startingFrom:(NSNumber *)start numResults:(NSNumber *)results;
MenuItem *menuItem = [arrParams objectAtIndex:0];
NSNumber *start = [arrParams objectAtIndex:1];
NSNumber *results = [arrParams objectAtIndex:2];
if(DEBUG_MODE == 1) {
NSLog(#"Before load more %lu", (unsigned long)[self.arrElements count]);
}
//call API and parse it
WebServicesClient *restClient = [[WebServicesClient alloc] init];
NSData *data = [restClient callWorkAPI:[menuItem pathAPI] inLanguage:[[NSLocale preferredLanguages] objectAtIndex:0] withLat:self.latitude withLng:self.longitude startingFrom:start numRows:results];
for(Work* work in [[restClient parseWorks:data] objectForKey:#"items"]) {
[self.arrElements addObject:work];
}
if(DEBUG_MODE == 1) {
NSLog(#"After load more %lu", (unsigned long)[self.arrElements count]);
}
[self.tableElem reloadData];
}
And this is my custom cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier;
CellIdentifier = #"HomeCell";
HomeCell *cell = (HomeCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(!cell) {
cell = [[HomeCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
Work *work = (Work *)[[self arrElements] objectAtIndex:indexPath.row];
cell.backgroundColor = [UIColor clearColor];
cell.labelTitleItem.text = [work title];
cell.labelSubtitleItem.text = #"my category"
//if exists image
if([[work image] isKindOfClass:[NSString class]] && [[work image] length] > 0) {
dispatch_async(dispatch_get_global_queue(0,0), ^{
NSData * data = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString:[work image]]];
if ( data == nil )
return;
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageItem.image = [UIImage imageWithData: data];
});
});
} else {
//remove image setting height to 0
[cell.imageItem setFrame:CGRectMake(cell.imageItem.frame.origin.x, cell.imageItem.frame.origin.y, cell.imageItem.frame.size.width, 0)];
}
if([[work distance] isKindOfClass:[NSString class]]) {
[cell.imagePriority setBackgroundColor:[UIColor lightGrayColor]];
cell.labelDistanceItem.text = [self convertDistance:[work distance]];
cell.labelDistanceItem.textColor = [UIColor whiteColor];
} else {
//remove image setting height to 0
[cell.imagePriority setFrame:CGRectMake(cell.imagePriority.frame.origin.x, cell.imagePriority.frame.origin.y, cell.imagePriority.frame.size.width, 0)];
//remove label distance setting height to 0
[cell.labelSubtitleItem setFrame:CGRectMake(cell.labelSubtitleItem.frame.origin.x, cell.labelSubtitleItem.frame.origin.y, cell.labelSubtitleItem.frame.size.width, 0)];
}
return cell;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[self arrElements] count];
}
Now the problem is that after reloadData i lose the text of a UILabel (my category) of my CustomCell
please, Any suggests?
I've had similar problems with TalbeViews. Try using the method
- (void) tableView:(UITableView *)tableView willDisplayCell:(RCGameTableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
to add the elements that you want. Keep the cell initialization that you do at the beginning of cellForRowAtIndexPath, but then return the cell right after and set the background color, labels, images, etc. in the willDisplayCell delegate function I mentioned.

ActivityIndicator doesn't stop animating nor removes from superview in UICollectionViewCell

I am trying to implement UICollectionView and show images. I am using SDWebimage which works perfectly in tableviewcells but when i tried to use it in UICollectionviewCell it doesn't stop and remove activityindicator. It does place the placeholder image if there is no downloaded image. I am not sure what is the difference between tableviewcell and collectionviewcell that might cause this problem.
Here is the code:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"personImageCell";
PersonCollectionViewCell *cell = (PersonCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
Person *person = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSString *imgURL=[person.imageurl stringByAppendingString:#"?maxheight=300&maxwidth=400"];
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
activityIndicator.hidesWhenStopped = YES;
activityIndicator.hidden = NO;
[activityIndicator startAnimating];
activityIndicator.center = CGPointMake(cell.ivPersonImage.frame.size.width /2, cell.ivPersonImage.frame.size.height/2);
[cell.ivPersonImage setImageWithURL:[NSURL URLWithString:imgURL] placeholderImage:nil options:SDWebImageProgressiveDownload success:^(UIImage *image, BOOL cached){
[activityIndicator stopAnimating];[activityIndicator removeFromSuperview];
NSLog(#"activity indicator should be removed");
}failure:^(NSError *error){
[activityIndicator stopAnimating];[activityIndicator removeFromSuperview];
cell.ivPersonImage.image = [UIImage imageNamed:#"placeholder.png"];
}];
[cell.ivPersonImage addSubview:activityIndicator];
return cell;
}
UPDATE:
When i do NSLog(#"activity indicator should be removed %#,activityIndicator);
I get this output:
activity indicator should be removed <UIActivityIndicatorView: 0xa520ab0; frame = (65 90; 20 20); hidden = YES; layer = <CALayer: 0xa520b60>>
It shows that UIActivityindicator is hidden but it is still showing on top of the image
It seems that you are reusing cell so there are more then one UIActivityIndicatorViews.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"personImageCell";
PersonCollectionViewCell *cell = (PersonCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
Person *person = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSString *imgURL=[person.imageurl stringByAppendingString:#"?maxheight=300&maxwidth=400"];
UIActivityIndicatorView *activityIndicator = [cell.ivPersonImage viewWithTag:10];
if (activityIndicator) [activityIndicator removeFromSuperview];
activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
activityIndicator.hidesWhenStopped = YES;
activityIndicator.hidden = NO;
[activityIndicator startAnimating];
activityIndicator.center = cell.ivPersonImage.center;
activityIndicator.tag = 10;
[cell.ivPersonImage addSubview:activityIndicator];
[cell.ivPersonImage setImageWithURL:[NSURL URLWithString:imgURL] placeholderImage:nil options:SDWebImageProgressiveDownload success:^(UIImage *image, BOOL cached){
[activityIndicator stopAnimating];[activityIndicator removeFromSuperview];
NSLog(#"activity indicator should be removed");
}failure:^(NSError *error){
[activityIndicator stopAnimating];[activityIndicator removeFromSuperview];
cell.ivPersonImage.image = [UIImage imageNamed:#"placeholder.png"];
}];
return cell;
}
Hmm....this is weird..can you try to make sure the activityIndicator is working on the main thread -
[cell.ivPersonImage setImageWithURL:[NSURL URLWithString:imgURL] placeholderImage:nil options:SDWebImageProgressiveDownload success:^(UIImage *image, BOOL cached){
dispatch_async(dispatch_get_main_queue(), ^(void){
[activityIndicator stopAnimating];
[activityIndicator removeFromSuperview];
}
NSLog(#"activity indicator should be removed");
}failure:^(NSError *error){
dispatch_async(dispatch_get_main_queue(), ^(void){
[activityIndicator stopAnimating];
[activityIndicator removeFromSuperview];
}
cell.ivPersonImage.image = [UIImage imageNamed:#"placeholder.png"];
}];
I suspect it is not, that is why it is not stopping its animation.
Create the activity indicator in the ViewDidLoad of current View Controller
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc]initWithFrame:CGRectMake(150, 225, 20, 30)];
[spinner setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleGray];
spinner.color = [UIColor blueColor];
[self.view addSubview:spinner];
Use below code right in the beginning of the function which leads to the activity
[NSThread detachNewThreadSelector:#selector(threadStartAnimating:) toTarget:selfwithObject:nil];
Declare these two methods for starting and stoping the Animation
-(void)threadStartAnimating:(id)data
{
[spinner startAnimating];
}
-(void)threadStopAnimating:(id)data
{
[spinner stopAnimating];
}
Please give feedback as this code is working fine in my own project