How to refresh UICollectionViewCell's image while bouncing? - ios

My image view inside my cell is not refreshing when the collection view is bouncing. I mean the image is being fetched over the network and is sometimes received during bouncing. If the reception completes during bouncing, there is no image refresh. So this basically happens to the last row cells.
Here's my code:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
MyCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"myIdentifier" forIndexPath:indexPath];
//[...]
[cell.imageView setImage:nil];
if(currentProduct.imageUrl){
if(currentProduct.image){
[cell.imageView setImage:[UIImage imageWithData:currentProduct.image]];
}else{
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:currentProduct.imageUrl]];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:urlRequest uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
if(responseObject){
[[MainController sharedController] updateProduct:currentProduct withImage:responseObject];
dispatch_async(dispatch_get_main_queue(), ^{
UICollectionViewCell *updateCell = [collectionView cellForItemAtIndexPath:indexPath];
if(updateCell){
[collectionView reloadItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]];
}
});
}
}];
[dataTask resume];
}
}
return cell;
}
The reloadItemsAtIndexPaths seems not to be called (or the collection view doesn't care).
Is there a way to ensure refreshing while bouncing? The only solution I came with is:
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath{
MyCell *myCell = (MyCell *)cell;
Product *currentProduct = [self.category.products objectAtIndex:indexPath.row];
if(!myCell.imageView.image && currentProduct.image){
[collectionView reloadItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]];
}
}
Edit: By bouncing I mean that scrolling is not brutally stopped when reaching the edges:

Related

Multiple UICollectionView in one view update second UICollectionView data

I want to design as like image. I use two UICollectionView to design. But I can't update second UICollectionView data after click fast on UICollectionview row. I never think this is the correct way to do this. Any suggestion please. I need to do with Objective-C.
My Code:
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section
{
if (view.tag == 1) {
return 20;//SelfiThemeIm;age.count
} else {
return 15;
}
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
if (cv.tag == 0) {
TopCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"TopCell" forIndexPath:indexPath];
NSData * imageData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: SelfiThemeImage[indexPath.row]]];
cell.imageView.image = [UIImage imageWithData: imageData];
return cell;
} else {
BottomCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"BottomCell" forIndexPath:indexPath];
NSData * imageData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: SelfiThemePhysicalFileName[indexPath.row]]];
cell.imageView.image = [UIImage imageWithData: imageData];
return cell;
}
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSString *ThemeURL;
if (collectionView.tag == 1) {
ThemeURL = #"http://wap.shabox.mobi/sticker_app_server/content.aspx?catid=";
ThemeURL =[ThemeURL stringByAppendingString:[SelfiThemeImageContentCode objectAtIndex:indexPath.row]];
BottomCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"BottomCell" forIndexPath:indexPath];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:ThemeURL
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *array= [responseObject objectForKey:#"stickers"];
self.selfidata = array;
// NSLog(#"JSON responce Selfi: %#", self.selfidata);
for(int i=0; i<=self.selfidata.count-1; i++){
NSString *PhysicalFileName = #"http://wap.shabox.mobi/CMS/content/graphics/Stickers/D200x200/";
PhysicalFileName =[PhysicalFileName stringByAppendingString:[[self.selfidata objectAtIndex:i]objectForKey:#"PhysicalFileName"]];
PhysicalFileName =[PhysicalFileName stringByAppendingString:#".png"];
[SelfiThemePhysicalFileName addObject:[NSString stringWithFormat:PhysicalFileName]];
NSLog(#"PhysicalFileName Array - %#", PhysicalFileName);
}
NSLog(#"SelfiThemePhysicalFileName Array - %#", SelfiThemePhysicalFileName);
NSData * imageData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: SelfiThemePhysicalFileName[indexPath.row]]];
cell.imageView.image = [UIImage imageWithData: imageData];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}else{
}
}
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section
{
if (view.tag == 1) {
return SelfiThemeImage.count;
} else {
return SelfiThemePhysicalFileName.count;
}
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
if (cv.tag == 0) {
TopCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"TopCell" forIndexPath:indexPath];
NSData * imageData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: SelfiThemeImage[indexPath.row]]];
cell.imageView.image = [UIImage imageWithData: imageData];
return cell;
} else {
BottomCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"BottomCell" forIndexPath:indexPath];
NSData * imageData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: SelfiThemePhysicalFileName[indexPath.row]]];
cell.imageView.image = [UIImage imageWithData: imageData];
return cell;
}
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSString *ThemeURL;
if (collectionView.tag == 1) {
ThemeURL = #"http://wap.shabox.mobi/sticker_app_server/content.aspx?catid=";
ThemeURL =[ThemeURL stringByAppendingString:[SelfiThemeImageContentCode objectAtIndex:indexPath.row]];
BottomCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"BottomCell" forIndexPath:indexPath];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:ThemeURL
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *array= [responseObject objectForKey:#"stickers"];
self.selfidata = array;
// NSLog(#"JSON responce Selfi: %#", self.selfidata);
for(int i=0; i<=self.selfidata.count-1; i++){
NSString *PhysicalFileName = #"http://wap.shabox.mobi/CMS/content/graphics/Stickers/D200x200/";
PhysicalFileName =[PhysicalFileName stringByAppendingString:[[self.selfidata objectAtIndex:i]objectForKey:#"PhysicalFileName"]];
PhysicalFileName =[PhysicalFileName stringByAppendingString:#".png"];
[SelfiThemePhysicalFileName addObject:[NSString stringWithFormat:PhysicalFileName]];
NSLog(#"PhysicalFileName Array - %#", PhysicalFileName);
}
NSLog(#"SelfiThemePhysicalFileName Array - %#", SelfiThemePhysicalFileName);
[self.myCollectionView reloadData];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}else{
}
}
I made changes in your code that just replace it with your code .
Happy Coding. give vote and approve it
Thank You.
You need have 2 objects referring CollectioView lets say _collectionView1 and _collectionView1.
Now make sure delegate for these 2 is set to self
in -(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath check
if ([collectionView isEqualTo:_collectionView1]) {
_collectionView2DataSource = ;//Do your calculation and setting
[_collectionView2 reloadData] ;
}
I am considering you have populated initial value and you are aware of all collection-view datasource and delegate functioning.
If you have the 2nd collectionview in storyboard/xib file, create the IBOUTLET for that collectionview and name it "what_ever_name_you_want"
if you have created this programatically, make the collectionview a global variable in the class
in didSelect method reload the collection view by calling ["what_ever_name_you_gave" reloadData];
you need to change this line because this line create new cell instance .
BottomCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"BottomCell" forIndexPath:indexPath];
change with :
BottomCell *cell = [collectionView cellForItemAtIndexPath:indexPath];

dispatch_async(dispatch_get_main_queue() is not working on iPad, but working on iPhone

I am using SDWebImage library and its working on iPhone. But I don't know why this is not called in iPad. I tried to put break points, but it doesn't hit the break point either. I put this method in cellForItemAtIndexPath.
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* cellIdentifier = #"CustomCollectionViewCell";
CustomCollectionViewCell* cell = (CustomCollectionViewCell*)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
[cell.downloadButton setTag:indexPath.row];
[cell.downloadButton addTarget:self action:#selector(deleteButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
catalogModel = [catalogArray objectAtIndex:indexPath.item];
cell.cellDescription.text = catalogModel.catalogName;
SDWebImageManager *manager = [SDWebImageManager sharedManager];
NSString *urlStr = catalogModel.imageNameThumbnail;
NSURL *url = [NSURL URLWithString:urlStr];
dispatch_async(dispatch_get_main_queue(), ^{
if ([self.catalogCollectionView.indexPathsForVisibleItems containsObject:indexPath])
{
catalogModel = [catalogArray objectAtIndex:indexPath.item];
[manager downloadImageWithURL:[NSURL URLWithString:catalogModel.imageNameThumbnail] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {}completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL)
{
cell.cellImageView.image = image;
NSLog(#"%#",catalogModel.catalogName);
}];
};
});
return cell;
}
There is a problem in concurrent image loading implementation. When -collectionView: cellForItemAtIndexPath: is calling, the device executes code on the main queue. Assuming that -downloadImageWithURL:options:progress:completed: method performs image loading on the background thread and returns instantly we can call it without dispatch_async(dispatch_get_main_queue()... wrapping. Otherwise we cannot guarantee that the completion handler is executing on the main thread, so the code should look like this
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* cellIdentifier = #"CustomCollectionViewCell";
CustomCollectionViewCell* cell = (CustomCollectionViewCell*)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
...
SDWebImageManager *manager = [SDWebImageManager sharedManager];
// methods with completion block usually return instantly
[manager downloadImageWithURL:aURL options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize){} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
// ensure ui is updating on main thread and for visible cell
dispatch_async(dispatch_get_main_queue(), ^{
if ([collectionView.indexPathsForVisibleItems containsObject:indexPath]) {
cell.cellImageView.image = image;
}
});
}];
return cell;
}
And the different results on iPhone and iPad can be explained by the architectural differences in technical specifications of testing devices.

Async image loading from url inside a UITableView cell - image changes to wrong image while scrolling?

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{ static NSString *CellIdentifier = #"cell";
TVcell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil)
cell = [[TVcell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.txtlabel.text = [[[arrayData objectAtIndex:indexPath.row] valueForKey:#"description"]stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
cell.IMGLabel.contentMode = UIViewContentModeScaleAspectFill;
cell.IMGLabel.image = nil;
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#",[enclosureUrlArray objectAtIndex:indexPath.row]]];
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (data) {
UIImage *image = [UIImage imageWithData:data];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
cell.IMGLabel.image = image;
[cell.IMGLabel setImage:image];
[cell.IMGLabel.layer setMasksToBounds:YES];
[cell.IMGLabel.layer setCornerRadius:2.5f];
[cell setNeedsLayout];
});
}
}
}];
[task resume];
Your problem is that you have no guarantee that your NSUrlRequests will terminate in the same order than they started. This is bad because your cells are re-used for better performance and it can end with strange behavior.
You can find a fix here: Asynchronous downloading of images for UITableView with GCD
Or you can use tools listed here to help you address this issue: https://stackoverflow.com/a/32601838/3769338

First image in my array is not displayed in collectionview cell

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
cell =[self.imgCllvw dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
if(!cell)
{
cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
}
NSDictionary *tmpDict = [images objectAtIndex:indexPath.row];
NSURL *url = [NSURL URLWithString:[tmpDict objectForKey:#"img"]];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *imge= [[UIImage alloc]initWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = imge;
});
});
cell.layer.borderWidth=1.0f;
cell.layer.borderColor=[UIColor colorWithRed:215.0/255.0 green:214.0/255.0 blue:214.0/255.0 alpha:1.0].CGColor;
return cell;
}
First image is not loaded and if I scroll the collectionview images are displayed and I have used autolayout for collectionview
I think you should try async downloading of image rather than sync, something like this
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
cell =[self.imgCllvw dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
if(!cell)
{
cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
}
NSDictionary *tmpDict = [images objectAtIndex:indexPath.row];
NSURL *url = [NSURL URLWithString:[tmpDict objectForKey:#"img"]];
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (data) {
UIImage *image = [UIImage imageWithData:data];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
MyCell *cell = (id)[collectionView cellForItemAtIndexPath:indexPath];
if (cell)
cell.imageView.image = image;
});
}
}
}];
[task resume];
cell.layer.borderWidth=1.0f;
cell.layer.borderColor=[UIColor colorWithRed:215.0/255.0 green:214.0/255.0 blue:214.0/255.0 alpha:1.0].CGColor;
return cell;
}
Instead of dispatch_async, mainqueue you can use dispatch_sync, like this,
dispatch_sync(dispatch_get_main_queue(), ^{
cell.imageView.image = imge;
});

CollectionView NSURL Session Speed and Reloading

I'm fairly new to iOS and developing weather App in when I download hourly forecast along with icon of conditions. I have been able to implement UICollection with NSURL Connection. However, I'm having issues regarding speed/performance issues regarding NSURL Session. Here are the two issues:
1) the speed of downloading and presenting the downloaded icons is very slow (and there are extremely small images). This download process can take anywhere from 5-10 seconds.
2) when I put a button to reset the collection, all data is reset, however the existing images remain until the new images are downloaded. Again this can take anywhere from 5-10 seconds.
Here is my code:
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.hours.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"ConditionsCell";
ConditionsCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
cell.conditionsTime.text = [self.hours objectAtIndex:indexPath.row];
cell.conditionsTemp.text = [NSString stringWithFormat:#"%#°", [self.hoursTemp objectAtIndex:indexPath.row]];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:[self.hoursIcons objectAtIndex:indexPath.row]] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
UIImage * serverImage = [UIImage imageWithData: data];
cell.conditionsImage.image = serverImage;
}];
[dataTask resume];
return cell;
}
And here is the IBAction for the button and to reload the CollectionView:
- (IBAction)selectDay:(UISegmentedControl *)sender {
if (sender.selectedSegmentIndex == 0)
{
self.todayOrTomorrow = #"today";
}
else if (sender.selectedSegmentIndex == 1)
{
self.todayOrTomorrow = #"tomorrow";
}
self.hours = [self hours];
self.hoursIcons = [self hoursIcons];
self.hoursTemp = [self hoursTemp];
[_collectionViewHours reloadData];
}
You are downloading data in background that, but not updating UI on main thread,try below pattern it willlhelp you
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"ConditionsCell";
ConditionsCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
cell.conditionsTime.text = [self.hours objectAtIndex:indexPath.row];
cell.conditionsTemp.text = [NSString stringWithFormat:#"%#°", [self.hoursTemp objectAtIndex:indexPath.row]];
cell.conditionsImage.image = [UIImage imageNamed:""];//reseting image
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString: [self.hoursIcons objectAtIndex:indexPath.row]] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
#autoreleasepool {//autorelease pool for memory release
if (!error) {
//UIImage * serverImage = [UIImage imageWithData: data];//comment this extra variable and can increase memory overhead.
dispatch_async(dispatch_get_main_queue(), ^{
cell.conditionsImage.image = [UIImage imageWithData: data];//update UI
});
}}//autorelease pool
}];
[dataTask resume];
return cell;
}
it will definetly help you for your first part.
Thanks.

Resources