I'm removing labels from a UITableViewCell because i don't need them in that particular cell. The problem is when the cell is reused i need them but they were removed before.
if (post.blockContent == TRUE) {
[cell.titleLabel removeFromSuperview];
[cell.contentLabel removeFromSuperview];
}
How do i add them again to the UITableViewCell?
I remove them because i have constraints linking everything with a dynamic cell height and i can't simply hide them because that will just make a empty space in the middle of the cell.
do like
// set visibile for all cell
[cell.contentView addSubview:cell.titleLabel];
[cell.contentView addSubview:cell.contentLabel];
// when contindition statisfy it will be hide
if (post.blockContent == TRUE) {
[cell.titleLabel removeFromSuperview];
[cell.contentLabel removeFromSuperview];
}
choice-2
cell.titleLabel.hidden = NO;
cell.contentLabel.hidden = NO;
if (post.blockContent == TRUE) {
cell.titleLabel.hidden = YES;
cell.contentLabel.hidden = YES;
}
the tableview reused the cell.
because this reason you cant used removeFromSuperview because all the cells that used the same instance will remove the labels.
the solution is the used constraint.
you need to wrap the labels to view, and the other objects will have constraint that skip over the view and reduce the constant of the cell.
in the heightForRow you need to calc the height without the view .
changed priority at runtime , its the main idea for the solution.
Related
I have a UITableViewCell with a description label pinned to the bottom as shown below:
Tapping on the description label toggles the numberOfLines between 3 and 0:
- (void)awakeFromNib {
[super awakeFromNib];
[self setupView];
}
-(void) setupView
{
UITapGestureRecognizer * gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(toggleNumberOfLines)];
self.jobDescriptionLabel.userInteractionEnabled = YES;
[self.jobDescriptionLabel addGestureRecognizer:gestureRecognizer];
}
- (void) toggleNumberOfLines {
if(self.jobDescriptionLabel.numberOfLines != 0){
self.jobDescriptionLabel.numberOfLines = 0;
}else{
self.jobDescriptionLabel.numberOfLines = kNumberOfLines;
}
[self.jobDescriptionLabel sizeToFit];
[self layoutIfNeeded];
}
When I tap on the label, the number of lines does change but the cell does not expand to accommodate the new number of lines. How do I fix this?
Collapsed (Default):
Expanded:
If you have your constraints set up correctly, you do not need to reload the data --- not the whole table, not even the affected rows.
Best method is to add a delegate function back to your tableview controller, and just call these lines back-to-back:
tableView.beginUpdates()
tablebleView.endUpdates()
That will tell auto-layout to re-calc the row heights.
Edit: Check my answer - which includes a link to a demo project - here: Expand UILabel inside UITableView with "more" button like Instagram
When the cell lays out it's subviews, the table view doesn't actually have any way of knowing anything has changed. You manually have to tell the table view to recalculate.
You probably want to reload the table view, or at least the cell the changes are happening in.
Take a look at reloadData for reloading the whole table view, or reloadRowsAtIndexPaths: if you want to just reload specific indexes in the Apple reference docs.
You can update the tableview cell height in heightForRowAtindexPath:.
Call reloadRowsAtIndexPath Method to update single row of UITableView.
self.dataTableView.beginUpdates()
self.dataTableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: UITableViewRowAnimation.automatic)
self.dataTableView.endUpdates()
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];
}
}
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.
I have a tableview in my View. The cells are created using custom Cells. I need to display a large string in the table view cells So I had added the text Label in a Scrollview. Also I need to execute some code when the user taps on table view cell. Please see the below code:
[cell.textLabelLine2 setFrame:CGRectMake(cell.textLabelLine2.frame.origin.x, cell.textLabelLine2.frame.origin.y, 500, cell.textLabelLine2.frame.size.height)];
cell.scrollView.contentSize = CGSizeMake(cell.textLabelLine2.text.length*10 , 10);
cell.scrollView.pagingEnabled = NO;
The problem is when the user touches above the Scroll View, the Tableview did select method will not be called. The solution I found for this problem is to add a gesture recogniser to the scroll view. But in this solution, we have no ways to check which cell(or which gesture recogniser) was selected. Could anyone help me to find a solution for this problem?
You can get to know the cell by the following code
if(gestureRecognizer.state == UIGestureRecognizerStateBegan) {
CGPoint p = [gestureRecognizer locationInView:[self tableView]];
NSIndexPath *indexPath = [[self tableView] indexPathForRowAtPoint:p];
if(indexPath != nil) {
UITableViewCell *cell = [[self tableView] cellForRowAtIndexPath:indexPath];
...
}
}
It's generally a bad idea putting scroll views inside scroll views. UITableView is also just a UIScrollView. That only kind of works if they are scrolling on different axis, i.e. the outer scroll view scrolling vertically and the inner scrolling horizontally.
For your specific scenario you would have to trigger the selection yourself. Once you have a reference to the cell you can ask the table view for the indexPath of it. Then you would call the delegate method for didSelectRow... yourself.
In the solution with the scrollview you are not able to scroll in the scrollview because the gestureRecognizer 'gets' the touch. Therefor I would not use the scrollview at all.
Make the label resize to its content like:
CGSize customTextLabelSize = [cell.customTextLabel.text sizeWithFont:cell.customTextLabel.font constrainedToSize:CGSizeMake(cell.customTextLabel.frame.size.width, 999999)];
cell.customTextLabel.frame = CGRectMake(cell.customTextLabel.frame.origin.x, cell.customTextLabel.frame.origin.y, cell.customTextLabel.frame.size.width, customTextLabelSize.height);
You also need to implement this in the heightForRowAtIndexPath
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
CGSize cellSize = [bigTextString sizeWithFont:customTextLabel.font constrainedToSize:CGSizeMake(generalCellWidth, 999999)];
return cellSize.height;
}
This way you can just use the didSelectRowAtIndex method.
If you really want to use the scrollview, add a button to your cell in the cellForRowAtIndexPath: method. Make the button just as big as the cell and add a button tag like this:
UIButton *cellButton = [UIButton buttonWithType:UIButtonTypeCustom];
cellButton.frame = CGRectMake(0, 0, cell.frame.size.width, cell.frame.size.height);
cellButton.tag = indexPath.row;
[cellButton addTarget:self action:#selector(cellButtonAction:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:cellButton];
Then add:
-(void)cellButtonAction:(UIButton*)sender
{
//do something with sender.tag
}
I am adding a custom button into my cell.contentView, and I noticed that every time a cell is scrolled off the visible part of the screen and back on, the button gets re-added - the translucent parts of it get more and more solid. What is the correct way to handle it so that it does not keep stacking more objects on top when scrolling through the tableView? Note that custom content is different for each cell, so I cannot put it into the if (cell == nil) {...} block.
The code I have is:
UISegmentedControl *btn = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObject:btn_title]];
// set various other properties of btn
...
[cell.contentView addSubview:btn];
Every time the cell is dequeued, you have to remove the old subviews before adding new ones, or else you'll get that stacking effect. You can do this in one of two places:
a) In tableView:cellForRowAtIndexPath:, remove your old views after the dequeueReusableCellWithIdentifier: call and before adding your new ones.
b) If you're using a subclass of UITableViewCell, you can override prepareForReuse to remove unwanted views. prepareForReuse is called every time a cell is dequeued for reuse, so it's a good place to get rid of old views from the last time the cell was configured.
I'll post a sample fix for the code you posted. It can be extended to take care of more views.
The steps are:
Create a method in your CustomCell class that takes care of the whole setup (for example: setupWithItems:)
Once you have a cell in cellForRowAtIndexPath: (after dequeueing it or creating it), you should call setupWithItems: with the new list of items the cell should display.
In your setupWithItems: implementation, make sure you remove the UISegmentedControl from its parent view. You can easily do this it the segmented control is stored as a property of your custom cell.
In your setupWithItems: implementation, create a new UISegmentedControl and add it to the CustomCell's view hierarchy.
Sample code:
-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
CustomCell* cell = [tableView dequeueReusableCellWithIdentifier:kSomeIdentifier];
if (!cell)
{
// Create a new cell
}
NSArray* currentCellItems = [self cellItemsForRow:indexPath.row];
[cell setupWithItems:currentCellItems];
return cell;
}
And in your CustomCell subclass:
- (void)setupWithItems:(NSArray*)items
{
if (self.segmentedControl)
{
[self.segmentedControl removeFromSuperView];
self.segmentedControl = nil;
}
// More setup...
UISegmentedControl *btn = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObject:btn_title]];
// set various other properties of btn
[cell.contentView addSubview:btn];
}