Preventing UICollectionView From Redrawing - ios

My aim is to display a row of lazy loaded thumbnails (number not known) using a UICollectionView, the user can then scroll the thumbnails horizontally.
I am using the code below to achieve close to what I am after but I am getting an error.
The first 4 thumbnails load okay but as you scroll the thumbnails start to get drawn on top of each other and in random orders.
I am trying to get it so that it achieves the following:
Item 1 ----- Item 2 ----- Item 3 ---- Item 4
I would like it so that the cells are only drawn once similar to what you would get when you use a UITableView. I only want the UICollectionView to reload the data when I call for it.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:#"cellIdentifier" forIndexPath:indexPath];
UIView *tmpView;
if([self.selectedEffects isEqualToString:#"Effects"]){
int index = indexPath.row;
NSLog(#"%i",index);
tmpView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 70, 90)];
[tmpView setBackgroundColor:[UIColor clearColor]];
UIImageView *imageViewWithEffect = [[UIImageView alloc] initWithFrame:CGRectMake(5, 0, 60, 60)];
imageViewWithEffect.layer.cornerRadius = 10.00;
imageViewWithEffect.clipsToBounds = YES;
UILabel *labelEffect = [[UILabel alloc] initWithFrame:CGRectMake(0, 65, 70, 20)];
[labelEffect setText:[[self.activeEffectsArray objectAtIndex:index] objectAtIndex:1]];
labelEffect.textAlignment = NSTextAlignmentCenter;
labelEffect.textColor = [UIColor whiteColor];
[labelEffect setFont:[UIFont boldSystemFontOfSize:8]];
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
[activityIndicator setFrame:CGRectMake(0, 0, 60, 60)];
activityIndicator.alpha = 1.0;
[activityIndicator startAnimating];
UIButton *buttonSelect = [UIButton buttonWithType:UIButtonTypeCustom];
[buttonSelect setFrame:CGRectMake(0, 0, 60, 90)];
[buttonSelect setTag:index];
[buttonSelect addTarget:self action:#selector(applyEffects:) forControlEvents:UIControlEventTouchUpInside];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
UIImage *imageWithEffect = [self editImage:[[self.activeEffectsArray objectAtIndex:indexPath.row] objectAtIndex:0] ImageToEdit:self.thumbnailImage];
dispatch_async(dispatch_get_main_queue(), ^{
[imageViewWithEffect setImage:imageWithEffect];
[activityIndicator removeFromSuperview];
});
});
[tmpView addSubview:imageViewWithEffect];
[tmpView addSubview:labelEffect];
[tmpView addSubview:activityIndicator];
[tmpView addSubview:buttonSelect];
}
[cell addSubview:tmpView];
return cell;
}

You are adding subviews to the cell every time. This is incorrect because the cells may have been reused. Check if the subviews have already been created, and modify them if they're already there. You can do this by subclassing UICollectionViewCell, or using viewWithTag: (the former is preferable). There are hundreds of examples of this around so I won't rehash here.
You also are referencing the same views in your dispatch block. This is also incorrect, because the cell may have been reused by the time the block is called. Instead, use the indexPath to get the cell again, and modify it that way.
Finally, you may want to consider rounding the image corners on a background thread instead of using layer.cornerRadius - this approach will cause the image to be blended, which can cause choppy scrolling animations when you display lots of images at once.

Related

Double footerview in tableview when reload section

I have uitableview footer that i create programatically like this:
-(UIView*)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
CGRect footerRect = CGRectMake(0, 0, self.view.frame.size.width, 45);
UIView *tableFooter = [[UIView alloc] initWithFrame:footerRect];
tableFooter.backgroundColor = [UIColor clearColor];
UIButton* moreButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[moreButton setBackgroundImage:[UIImage imageNamed:#"moreImage"] forState:UIControlStateNormal];
[moreButton.titleLabel setFont:[UIFont boldSystemFontOfSize:20]];
[moreButton addTarget:self action:#selector(actionClicked:)forControlEvents:UIControlEventTouchUpInside];
moreButton.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[moreButton setFrame:CGRectMake(0, 0, self.view.frame.size.width, 45)];
[tableFooter moreButton];
return tableFooter;
}
And somewhere in my code i retrieve again data from server and reload certain section using this:
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:mySection] withRowAnimation:UITableViewRowAnimationFade];
However when the table present the new data i have two footer view on mysection, one with correct position, one just floating somewhere in my section. How i remove this footer?
Thanks.

UICollectionViewCell disappears while scrolling

I'm using a uicollectionview to create a carousel like scroll view. I think the uicollectionview is the best approach with this use case. I was able to render uicollectionviewcell inside uicollectionview but the problems is, when I scroll the uicolectionview the later cells will disappear and all the cells went black in the end. Another issue is that the cell is not loaded until it's boundary is fully inside the Carousel.
this is how I set up my uicollectionviewcell inside cellForItemAtIndexPath delegate method
NSString *urlString = [NSString stringWithFormat:BASE_URL, panoid, heading, pitch];
NSURL *panoUrl = [NSURL URLWithString:urlString];
//WIDTH calculated based on screen size, roughly about three cells per screen.
CGFloat WIDTH = collectionView.frame.size.width / 3;
UIImageView *panoImg = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, WIDTH, 133.3)];
// add padding to imageview
panoImg.bounds = CGRectInset(panoImg.frame, 2, 2);
// do I need GCD to do async process here? the images are all loaded from urls. not sure if this will cause a problem.
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:panoUrl];
dispatch_async(dispatch_get_main_queue(), ^{
[panoImg setImage:[UIImage imageWithData:data]];
});
});
seeInsideCollectionViewCell *cell = [_panoCarousel dequeueReusableCellWithReuseIdentifier:#"panoCarouselCell" forIndexPath:indexPath];
cell.frame = CGRectMake(indexPath.item * WIDTH + 4, 0, WIDTH, 133.3);
cell.backgroundColor = [UIColor blackColor];
[cell addSubview: panoImg];
UILabel *label = [[UILabel alloc] initWithFrame: CGRectMake(4, 4, WIDTH, 15)];
[label setBackgroundColor: [UIColor colorWithRed:0.63 green:0.63 blue:0.63 alpha:0.5]];
[label setFont: [UIFont fontWithName:#"Roboto-Light" size:10.0]];
[label setText: _BussinessViewsNames[indexPath.item]];
[cell addSubview:label];
return cell;
any suggestions, the cells were all loaded, just the loading is not seamless and smooth, I've attached a gif to demonstrate the problem here.
remove this line due to you should not change frame for cell
cell.frame = CGRectMake(indexPath.item * WIDTH + 4, 0, WIDTH, 133.3);
When cell reusable, it already has uiimageview, and you again create new uiimageview and add it than cell will be has several uiimageviews as subview
Move load images in another class or method like
self.imageLoader
-(void)loadImageWithURL:(NSURL *)url successBlock:(void (^)(UIImage *image, id itemId))successBlock failureBlock:(void (^)(NSError *error))failureBlock
and call it in cellForItem method (check id of gotten image)

UIScrollView in UITableViewCell not scrolling

I am adding a cell to a UITableView which has a scrollview, the code I have in the cell is :
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 30, 320, 44)];
UIView *labelView = [[UIView alloc] initWithFrame:CGRectMake(4, 4, 80 * _labels.count, 44)];
for (NSUInteger i = 0; i < _labels.count; i++) {
NSString *aLabel = [_labels objectAtIndex:i];
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(i * 70, 3, 60, 30)];
button.tag = i;
[button setTitle:aLabel forState:UIControlStateNormal];
[button setBackgroundColor:[UIColor lightGrayColor]];
[labelView addSubview:button];
}
scrollView.contentSize = CGSizeMake(labelView.frame.size.width, labelView.frame.size.height);
[scrollView addSubview:labelView];
[self addSubview:scrollView];
However this does not scroll :(. If I have the same code segment outside the tableview and add this directly to the view, it works as expected. What am I missing?
Try [cell addSubview:scrollView];
Try adding the scrollView to the contentView of the UITableViewCell and maybe setting
self.contentView.userInteractionEnabled = NO
From the UITableViewCell reference doc
Discussion
The content view of a UITableViewCell object is the default superview for content displayed by the cell. If you want to customize cells by simply adding additional views, you should add them to the content view so they will be positioned appropriately as the cell transitions into and out of editing mode.

How to position a image in customized section header so that it appears correctly in both the orientations?

I have a customized section header, in viewForHeaderInSection, I have a view and a button on that so it will be like clicking the section header some action will be performed.Now I need an image in the section header so that it changes on clicking the section header.So I added a image view to the view in viewForHeaderInSection but the image position is not correct,it is different in landscape and portrait.I need the image in the right corner of the section header.I need to do it immediately.
Please help.
Thanks
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
/* Create custom view to display section header... */
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width, 18)];
UIButton *btn= [[UIButton alloc] initWithFrame:CGRectMake(10, 5, self.tableView.frame.size.width, 18)];
[view addSubview:btn];
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(275.0f, 1.5f, 8.0f, 18.0f)];
UIImage *image = [UIImage imageNamed:#"Selection-Indicator.png"];
[imageView setImage:image];
[view addSubview:imageView];
return view;
}
oky try this hope this helps u
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
/* Create custom view to display section header... */
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width, 18)];
UIButton *btn= [[UIButton alloc] initWithFrame:CGRectMake(10, 5, self.tableView.frame.size.width, 18)];
[view addSubview:btn];
UIImageView *imageView = [[UIImageView alloc]initWithFrame: CGRectMake(view.bounds.size.width - 20, 1.5f, 8.0f, 18.0f); //set the offset from right
UIImage *image = [UIImage imageNamed:#"Selection-Indicator.png"];
[imageView setImage:image];
[view addSubview:imageView];
return view;
}
and in rotation handling method u need to reload or update the UI
for example
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
//so u dont want to reload the tableview then u can reload the sections
// [self.tableVIew reloadData]; //just try this (comment)
//try this
NSArray *cells = [self.tableVIew visibleCells];
NSIndexPath *indexPath = [self.tableVIew indexPathForCell:(UITableViewCell *)[cells lastObject]];
NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, totalSectionCount)]; //try by placing the all the section count
[self.tableVIew reloadSections:set withRowAnimation:UITableViewRowAnimationAutomatic];
}
You should have something like this:
UIImageView *imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"yourImage.png"]];
[imgView setFrame:CGRectMake(parentView.frame.size.width-50, 5, 30, 30)];
[parentView addSubview:imgView];
The sizes in CGRectMake are only as an example, but make sure your UIImageView is always related to you parentView.frame.size.width... that should do it!
EDIT: After your code, you should replace:
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(275.0f, 1.5f, 8.0f, 18.0f)];
with
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(view.frame.size.width-15, 1.5f, 8.0f, 18.0f)];
play with the -15 to put the image where you wish...
i hope it helps!

the event touchUpInside of a UIButton don't work

there is only 1 cell in my UITableView, and I add a UIButton to the cell.
set the action when touchUpInside event happens. But it doesn't work at all. Meanwhile, if
i set the trigger as touchDown event instead of touchUpInside, it will fire the action i set.
I search the reason by google, someone says that the problem maybe caused by the superview's dimension. That is the size of the button is beyond its superview.
so I add a UIView to contain the button, the code is as follow:
self.ageDisplay = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
UIImage *ageImage = [UIImage imageNamed:#"long_btn_click.png"];
[self.ageDisplay setImage:ageImage forState:UIControlStateSelected];
[self.ageDisplay setFrame:CGRectMake(10, 5, 145, 34)];//(10, 320, 145, 25)];
[self.ageDisplay setTitle:#"请选择您的年龄" forState:UIControlStateNormal];
[self.ageDisplay setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[self.ageDisplay setTag:3];
[self.ageDisplay addTarget:self action:#selector(showSelectionPage:) forControlEvents:UIControlEventTouchDown];
self.buttonsSubView = [[UIView alloc] initWithFrame:CGRectMake(0, 310, 320, 106)];
//self.buttonsSubView.backgroundColor = [UIColor blueColor];
[self.buttonsSubView setUserInteractionEnabled:YES];
[self.buttonsSubView addSubview:self.ageDisplay];
the above code is in ViewDidLoad function.
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *sugPageCell = #"SuggestionPageCell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:sugPageCell];
if (nil == cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:sugPageCell];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
[cell addSubview:self.sugTitle];
[cell addSubview:self.sugSpecification];
[cell addSubview:self.suggestion];
self.buttonsSubView.userInteractionEnabled = YES;
[cell addSubview:self.buttonsSubView];
/*
[cell addSubview:self.ageDisplay];
[cell addSubview:self.genderDisplay];
[cell addSubview:self.submit];
*/
return cell;
}
However, this doesn't help.
The problem has confused me for 2 days.
I will appreciate if anyone can provide some indication.
Thank you.
It's because your buttonSubView view is outside the cell's frame.
So even if the button is visible (because the cell's clipsToBounds is set to NO), it will not receive touch events from the cell. Why do you set this view's vertical position to 310 ?
If you try this :
self.buttonsSubView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 106)];
The event should fire as you want.
I had this problem recently. I guess there must may be an object of UITapGesture added to your UITableView. If so, please set the property cancelsTouchesInView of the UITapGesture object to NO.
For example:
UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:target action:tapAction];
gesture.cancelsTouchesInView = NO;
[self addGestureRecognizer:gesture];

Resources