Labels are lingering in my reusable table cells - ios

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
}

Related

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.

How to remove Views from UITableView custom row on PrepareForReuse?

I have a UITable view with a random number of UIImageView in every Single row. In the costruction of the single row, I've used [self addSubView: xxx] and a for cycle to add every UIImageView i need to add (my model has an array of URL). But now I've noticed that when the UITableView reuse the rows and it doesn't clean the UIImageViews added.
I've tied to manually remove them in method onPrepareForReuse as in the code:
if(_messageContentsFrames != nil){
for(NVChatMessageContent *singleContent in _messageContentsFrames){
[singleContent removeFromSuperview];
}
}
But it gives me error. How can i completely reset the view when it is going to be reused?
You can remove these imageViews in -tableView:cellForRowAtIndexPath: method. That always worked for me.
By the way, do you really need to reuse cells if you make such work with it. Wouldn't it be easier to create new cell?
Or it would be better not to remove imageViews, but reconfigure them on reusing and remove those views, that cell doesn't need.
Option 1. Set tag to your UIImageView (use: imageView.tag = 123456) and then in tableView:cellForRowAtIndexPath: you should get the specific view using:
id imageView = [self.view viewWithTag:yourInteger];
[imageView removeFromSuperview];
Option 2. Use:
for( UIView *view in cell.subviews) {
if ([view isKindOfClass:[UIImageView class]]) {
[view removeFromSuperView];
}
}

Stretch UIView over multiple UICollectionViewCells in UIScrollView

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.

Getting all subviews for a UIScrollView

I need to get an array of all the subviews in a UIScrollView. Right now I'm using
NSArray *subviews = [myScrollView subviews];
but this seems to only be returning the subviews that are visible at the time the code is run. I need all the subviews in the whole extent of the UIScrollView, even those that are currently hidden (as in off screen). How would I get that?
Essentially, I'm looking for something like the contentSize property of a UIScrollView, except instead of returning just the size of the UIScrollView if it were big enough to display all of it's content, I want it to return the content itself.
EDIT: I think I've figured it out: the scroll view this isn't working for is actually a UITableView - and I think it's deque-ing the cells that are off screen on me, and that's why they aren't showing up. I'm going to do some testing to confirm.
Try with following code its working for me.
for(UIView * subView in myScrollView.subviews ) // here write Name of you ScrollView.
{
// Here You can Get all subViews of your myScrollView.
// But For Check subview is specific UIClass such like label, button, textFiled etc.. write following code (here checking for example UILabel class).
if([subView isKindOfClass:[UILabel class]]) // Check is SubView Class Is UILabel class?
{
// You can write code here for your UILabel;
}
}
tl;dr
It turns out that
NSArray *subviews = [myScrollView subviews];
will indeed return all the subviews in a UIScrollView *myScrollView, even if they are off-screen.
The Details
The problem I was actually having was that the scroll view I was trying to use this on was actually a UITableView, and when a UITableViewCell in a UITableView goes off-screen, it actually gets removed from the UITableView - so by the time I was calling subviews, the cells I was looking for were no longer in the scroll view.
My workaround was to build all of my UITableViewCells in a separate method called by my viewDidLoad, then put all of those cells into an array. Then, instead of using subviews, I just used that array. Of course, doing it this way hurts the performance a little (in cellForRowAtIndexPath you just return the cell from the array, which is slower than the dequeueReusableCellWithIdentifier method that is typically used), but it was the only way I could find to get the behavior I needed.

How to remove content from UITableViewCell but still have separator left?

In my app, i have table view in which i am reloading data based on Business logic so it was overlapping earlier then i write this piece of code to remove content before drawing:
for(UIView *view in cell.subviews){
if ([view isKindOfClass:[UIView class]]) {
[view removeFromSuperview];
}
}
And now its removing everything including cell separators. Any idea how to prevent it??
Thanks,
Remove subviews from [cell contentView]
If you don't know what are you doing, you should never add views or remove views from the cell directly. Use only contentView, backgroundView and selectedBackgroundView.
However, there is rarely a use case when you should remove all the views from a cell. Why don't you just create a new cell (or reuse one)? Or just set the hidden property to YES for the subviews?

Resources