Stretch UIView over multiple UICollectionViewCells in UIScrollView - ios

I have a UIScrollview with a UICollectionview in it which has multiple custom UICollectionViewCells.
What a I want is to stretch a UIView over multiple cells.
I tried to use: cell.clipToBounds = NO; which is working but when I scroll to left and scroll back the expanded part of the UIView is being cut off again.
I guess it has something to do with dequeuing of the cells which aren't used. Can I assign the expanded part to the new cell so that it won't get deleted or sth?
I had a former problem where Cells got duplicated therefor I had overwritten the method prepareForReuse of my custom UICollectionViewCell-Class.
-(void)prepareForReuse
{
for(id aView in [self.contentView subviews])
{
if ([aView isKindOfClass:[MyCustomUIView class]])
{
[aView removeFromSuperview];
}
}
}
Any advice and help will be thankfully appreciated.

If your cell.clipsToBounds = NO is working, but only the first time, I agree that the problem probably has to do with cell reuse. If you are overriding -prepareForReuse, you probably want to set self.clipsToBounds = NO in there. Then, in -collectionView:cellForItemAtIndexPath:, set clipsToBounds to YES or NO every time as needed.

Related

UIView is overlapping / creating again in cellForRowAtIndexPath

I have UITableViewCell that contains UIView (lets call it CPView) which is created while cellForRowAtIndexPath is called. CPView is just a plain coloured view and for every cell its width is different (that's why needed to create in cellForRowAtIndexPath).
Problem is
1)The CPView 's colour gets darker every time cell loads (May be due to every time that cell creates the same view so overlapping effect).
2) The cell overlaps / inherits other cell's CPView (we can see this because of light and dark colour of two CPView).
How can I prevent cell to recreate if it already exist or creation of this CPView again?
Edit
- (void)configureCell:(CreditDebitCell *)cell atIndexPath:(NSIndexPath *)indexPath {
//other code
UIView * CPView;
if (CPView){
CPView =nil;
}
else
{
CPView = [[UIView alloc] initWithFrame:CGRectMake(cell.bounds.origin.x, cell.bounds.origin.y, cell.frame.size.width*[self.percentArray[indexPath.row] floatValue] ,cell.frame.size.height )];
[CPView setClipsToBounds:YES];
[CPView setBackgroundColor:[UIColor colorWithRed:107/255.0 green:15/255.0 blue:47/255.0 alpha:0.5]];
[cell addSubview: CPView];
}
}
The issue here is reuse of the cells - and therefore you get multiple views added to your cell view.
You can:
-remove subview
-check if subview exists and do/don't do anything.
You can check if the subview is there by going through subviews:
for (UIView *v in cell.contentView.subview) {
if ([v isKindOfClass:[CPView class]]) {
// remove or flag that it exists
}
}
But I think that you should handle this in your cell - not your view controller that implements table view delegate. Better tell cell to use some view/hide some view based on some kind of logic then to do that inside cellForRowAtIndexPath
According to your i question(without cellforRowAtIndexpath) i can assume that you should check every time something like in cellForRowAtIndexPath
if(cpView){
cpView = nil;
}
// alloc again with required size for particular row.
Make a subclass of your UITableViewCell and make a property of it that will reference your CPView. This will now let you have a better control whether your subclassed cell does / doesn't have any CPView that needs to be added.

Additional View getting inserted into UICollectionView loaded from Xib

I am having a weird issue with using a Xib for UICollectionViewCells. The issue is that an additional view is being created, that seems to be on top of everything, rendering my gestures and IBActions useless.
Here's my setup:
I have a UIViewController with a UICollectionView in it.
In the storyboard, the UICollectionView has a single cell, of class "MyCell", with reuse id "cell"
In cellForItemAtIndexPath, I only return the result of [collectionView dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath]. I also hardcoded 30 items in numberOfItemsInSection.
That's that. Besides that, I have MyCell.h, MyCell.m and MyCell.xib.
The xib contains a single UIButton, whose Touch Up Inside event is set to call the buttonPressed IBAction. The view itself is set to the class MyCell and the tag is set to 123 (we'll get to why below).
In MyCell.m, I overrode awakeAfterUsingCoder to return [MyCell initView:self]. The definition of initView is at the bottom. But it basically loads the view from the Xib.
That's it.
When I run the app, 30 cells show up, all with their button, but when the button is pressed, nothing happens. After a lot of investigating, I found that there's an extra UIView added inside the cell, which is on top of the button, and it's covering the entire thing.
If I add the for-loop below inside awakeAfterUsingCoder, then the button works again.
- (MyCell *)awakeAfterUsingCoder:(NSCoder *)aDecoder
{
MyCell *cell = [MyCell initView:self];
for(UIView *v in cell.subviews){
if(![v isKindOfClass:[UIButton class]]) [v removeFromSuperview];
}
return cell;
}
My question is: what is that view, and why is it there??? Even though I can get everything to work by removing that view, it feels like a hack.
Thank you! And I can answer any other questions if necessary.
+ (id)initView:(UIView *)viewToInit
{
UIView *viewToReturn;
if(viewToInit.tag != 123){
//If we're in the storyboard codepath
//Initialize from the xib.
//If the code below is failing, we probably forgot the 666 tag :/
viewToReturn = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([viewToInit class])
owner:nil
options:nil] firstObject];
//copy frame, autoresizing, and layour items.
viewToReturn.frame = viewToInit.frame;
viewToReturn.autoresizingMask = viewToInit.autoresizingMask;
viewToReturn.translatesAutoresizingMaskIntoConstraints = viewToInit.translatesAutoresizingMaskIntoConstraints;
for (NSLayoutConstraint *constraint in viewToInit.constraints){
id firstItem = constraint.firstItem;
if (firstItem == viewToInit){
firstItem = viewToReturn;
}
id secondItem = constraint.secondItem;
if (secondItem == viewToInit){
secondItem = viewToReturn;
}
[viewToReturn addConstraint:[NSLayoutConstraint constraintWithItem:firstItem
attribute:constraint.firstAttribute
relatedBy:constraint.relation
toItem:secondItem
attribute:constraint.secondAttribute
multiplier:constraint.multiplier
constant:constraint.constant]];
}
}else{
//otherwise do nothing and just return what was passed in
viewToReturn = viewToInit;
}
return viewToReturn;
}
I've just had this problem myself, and the answer lies in the cell nib file. If you've created a nib and are using the stock UIView contained as a cell, that's where your problem is. This UIView needs to be deleted immediately and replaced with a newly-dragged-on UICollectionViewCell. This will then be your cell, and you can add views to your hearts content without further issues.
OK, so after a long time of having a nasty hack in there to work around this, I've figured it out.
The issue is that the contentView of the UICollectionViewCell is getting inserted on top of my views. It is very weird to me because this doesn't happen with UITableViewCells which also have a content view.
What I did then, was to put everything in a "container" view and then move that container view into the contentView on init, and that took care of the problem.
I still don't understand WHY this is happening. It seems to me that all my views should be added into the contentView (like the table's cells do), but at least I can now work around it.

UITableViewCell change subviews positions after being selected

I created a dynamic cell using AutoLayout, put code in (void)updateConstraints cell's subview method, set BOOL value so custom AutoLayout code runs only once, and in View Controller call
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
just before returning cell. Everything seems to works fine but I'm experiencing weird issue, when I select cell all its subviews (or itself?) change position.
cell pointed with this beautiful arrow shows it ;]
I experienced the same bug and tinkered for about a day to fix this ... to get to the bottom of the issue I set different background colors on the cell & the content view. Right after first showing the table view everything seemed to work, but after selecting the cell the contentView would jump around - sometimes when selecting, sometimes when deselecting (which I do immediately in tableView:didSelectRowAtIndexPath:), and the cell's background became visible. So I figured the cell somehow didn't reestablish the correct contentView dimensions.
Finally it turned out that at least in iOS 8 you need to set the appropriate autoresizingMasks for the cell as well as the contentView, and then make the layout system translate them into constraints by setting translatesAutoresizingMaskIntoConstraints to YES. At the same time this flag needs to be NO on all subviews of your contentView.
So here's my initialisation for custom cells:
- (void)awakeFromNib {
// enable auto-resizing contentView
[self setTranslatesAutoresizingMaskIntoConstraints:YES];
[self.contentView setTranslatesAutoresizingMaskIntoConstraints:YES];
self.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
// remove autoresizing constraints from subviews
for (UIView *view in [self.contentView subviews]) {
view.translatesAutoresizingMaskIntoConstraints = NO;
}
}
Works like a charm for me. For the record, I'm using the UITableViewCell auto-sizing which was introduced in iOS 8, like this:
// enable automatic row heights in your UITableViewController subclass
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 80.0; // set to whatever your "average" cell height is
I found solution to this issue,I used instructions under this link dynamic-cell-layouts-variable-row-heights to implement dynamic table view cells: [Using Auto Layout in UITableView for dynamic cell layouts & variable row heights
[1]: Using Auto Layout in UITableView for dynamic cell layouts & variable row heights. Author suggests to use this two methods to update constrains when using updateConstraints method inside cell's subview.
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
but what I found in my case is there was some issue that not all cells was updated as they should. Solution is to call [cell updateConstraints] in (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath and not wait until system will update constrains.

Labels are lingering in my reusable table cells

I have a table whose cells contain labels. Whenever I dequeue a reusable cell, the old labels are still lingering on it. I was able to remove them with this:
for(int a=[[newcell subviews]count]-1; a>=0;a--)
{
if([[[[newcell subviews]objectAtIndex:a]class] isSubclassOfClass:[UILabel class]])
{
[[[newcell subviews] objectAtIndex:a] removeFromSuperview];
}
}
But when I select the cell, I can see the old text on top of the new. I tried this:
[[newcell.selectedBackgroundView subviews] makeObjectsPerformSelector: #selector(removeFromSuperview)];
[[newcell.backgroundView subviews] makeObjectsPerformSelector: #selector(removeFromSuperview)];
But it didn't work. How can I make the old labels disappear from the selected cell as well as the regular view of the cell?
Subclass UITableViewCell (if you aren't already). Override prepareForReuse and remove the labels there. Might work
This kind of problem tends to happen when you add subviews to your cells in cellForRowAtIndexPath: regardless of whether it's being dequeued or newly created. As a result, you end up creating a new subview each time the row is reused, and the old subviews accumulate.
What you instead want to do is to use the same subview each time, but just set the relevant attributes (e.g., labels or color) each time. Check out the answers to How do I clear a cell completely when I reuse it? to see some possible approaches.
I kinda did what Yuji suggested. Instead of putting in new labels on each iteration, I checked whether the cell contained labels and then either edited the labels if they were there or put them in if they weren't. Code goes like this:
if([[newcell.contentView subviews] count]>=2 && [[[[newcell.contentView subviews] objectAtIndex:0]class] isSubclassOfClass:[UILabel class]] &&
[[[[newcell.contentView subviews] objectAtIndex:1]class] isSubclassOfClass:[UILabel class]])
{
//change the text of the labels
}
else
{
//add the labels to the cell
}

UITableView separator lines disappear between cells on scroll

Problem: The separator between cells in a table view appear only for those cells shown when the view loads, and only at load time. When the tableview is scrolled down, the cells scrolled into view show no separator between them, then when the tableview is scrolled back up, the initial cells show no separator.
Details: I've got a UITableView to which I'm adding standard UITableViewCells. These cells are created with initWithFrame, frame height = 90px. I'm adding a custom view created from a nib to this cell's view, height = 90px. The cell height is specified at 90px in tableView:heightForRowAtIndexPath:.
Has anyone experienced this behavior?
I had a feeling the solution to this would be simple...
I made the height of my cells 91px and the separator lines appear as they should on scroll.
I couldn't use Douglas's solution because my tables have a huge amount of cells and would become pretty much unusable on older phone. Reusing cells is key for performance.
BUT, I managed to workaround the problem using a transparent separator and adding my own in the contentView of the cell, as follows:
yourTable.separatorColor = [UIColor clearColor];
separatorView.frame = FactRectMake(0, rowHeight-1, appFrame.size.width, 0.2);
I had the same problem, but I used a different solution.
My separators were disappearing because I was clearing my cell using:
for (UIView *eachView in self.subviews) {
[eachView removeFromSuperview];
}
This removed the separator view as well!
Instead, I assigned a tag for each of my customs views (three labels) right before adding them to the sub view:
tempFirstNameLabel.tag = 100;
self.firstNameLabel = tempFirstNameLabel;
[self addSubview:self.firstNameLabel];
Then when I cleared the cell, I just removed those views:
for (int i = 100; i<103; i++) {
UIView *eachView = [self viewWithTag:i];
[eachView removeFromSuperview];
}
Hope this helps!
This also avoids the memory management issues that #Douglas Smith's solution posed.
You should set separator none and then single line again
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// it is a bug in iOS 7
tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;

Resources