I have a table with one custom cell with one label and one textfield.Am re-using the same cell with some 5 rows. I want to display one view beneath my textfield only if user enters on the textfield. When am entering data in first textfield view should be hidden for other rows and respectively for all other rows. Default height must be 120 and when am displaying my view row height should increased to 250. Here is my code,
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 120;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"DetailsTableCell";
cell = (NewsDetailsTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[NewsDetailsTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
if (indexPath.row == 0) {
cell.newView.tag = 200;
}
else if (indexPath.row == 1) {
cell.newView.tag = 201;
}
else {
cell.newView.tag = 202;
}
return cell;
}
Here new view is my UIView which am displaying on text change.
- (IBAction)textFieldChanged:(UITextField *)textField replacementString:(NSString *)string {
NSIndexPath *indexPath = [newsTable indexPathForCell:(NewsDetailsTableViewCell*)[[textField superview] superview]];
NSIndexPath *myPath = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];
NewsDetailsTableViewCell *newsTableCell = (NewsDetailsTableViewCell*)[newsTable cellForRowAtIndexPath:myPath];
if (indexPath.row == 0 && newsTableCell.newView.tag == 100) {
newsTable.rowHeight = 250;
newsTableCell.newView.hidden = false;
} else if (indexPath.row == 1 && newsTableCell.newView.tag.tag == 101) {
newsTable.rowHeight = 250;
newsTableCell.newView.hidden = false;
} else {
newsTable.rowHeight = 250;
newsTableCell.newView.hidden = false;
}
}
Am getting my view in my text change but row height is not getting changed. Also It still showing 120 height.
Also I tried this,
NSArray* rowsTobeReloaded = [NSArray arrayWithObjects:indexPath, nil];
[newsTable reloadRowsAtIndexPaths:rowsTobeReloaded withRowAnimation:UITableViewRowAnimationNone];
But it didn't work. Should I re-load my tableview again in text change? or am I missing something?
You have to modify the logic in the below method:
The selectedRowNum can be modified as per the row in which the textField is to be shown.
Also, cellForRowAtIndexPath is not used for setting the height of rows in a tableview. Everytime, we call the reloadData or reloadIndexes the heightForRowAtIndexPath method is called.
YOu need to change height in
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (usingNewView){
return 250;
}
return 120;
}
And set logic for usingNewView in
- (IBAction)textFieldChanged:(UITextField *)textField replacementString:(NSString *)string
Related
So I've got a universal app, and I'm trying to hide a border of a cell, if the next cell in the tableView has an error state. I use this code in tableView:cellForRowAtIndexPath: to determine if the cell should hide it's bottom border or not:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
DCSMCDynamicBaseCell * cell = [self.cellManager dynamicCellForComponent:[self componentForIndexPath:indexPath] atIndexPath:indexPath canShowErrors:self.shouldDisplayErrors];
NSIndexPath *nextIndexPath = [self.tableView nextRowForIndexPath:indexPath];
if (nextIndexPath) {
DCSMCDynamicBaseCell *nextCell = [self.tableView cellForRowAtIndexPath:nextIndexPath];
DCSMCDynamicBaseCell *currentCell = [self.tableView cellForRowAtIndexPath:indexPath];
if (nextCell.invalidState) {
cell.shouldHideBottomBorder = YES;
NSLog(#"%#", currentCell);
}
}
if ([cell isKindOfClass:[DCSMCDynamicCopyCell class]]) {
[((DCSMCDynamicCopyCell *)cell).webView setNavigationController:self.navigationController];
}
[cell setDelegate:self];
self.previousIndexPath = indexPath;
return cell;
}
My nextRowForIndexPath looks like this:
- (NSIndexPath *)nextRowForIndexPath:(NSIndexPath *)indexPath {
NSInteger currentRow = indexPath.row;
NSInteger currentSection = indexPath.section;
NSInteger numberOfRows = [self numberOfRowsInSection:indexPath.section];
NSInteger numberOfSections = [self numberOfSections];
NSIndexPath *newIndexPath;
if (currentRow + 1 < numberOfRows) {
newIndexPath = [NSIndexPath indexPathForRow:currentRow + 1 inSection:currentSection];
} else if (currentSection + 1 > numberOfSections) {
newIndexPath = [NSIndexPath indexPathForRow:0 inSection:currentSection + 1];
} else {
newIndexPath = nil;
}
return newIndexPath;
}
On my iPhone 6 simulator, everything works fine. It gets the cell and hides the bottom border of it. I may refactor this a bit in order to make sure it works in all scenarios, but for a first pass before testing, it works great.
The issue is on the iPad, every single cell returns nil when using the cellForRowAtIndexPath: method. Even weirder, on iPad, the screen is big enough so that all cells are visible at all times, so when I call reload data, the cells are already visible and should get returned by this method.
Does anyone know why a cell wouldn't get returned by cellForRowAtIndexPath:on iPad but would on iPhone?
I am making a UITableViewController that resizes UITableViewCell when I select the cell.
My problem is that the content that should be hidden when the cell is not expanded (UILabel), is always visible.
What am I doing wrong? Here is my code:
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return 5;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"TableViewCell";
TableViewCell *cell = (TableViewCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[NSBundle mainBundle] loadNibNamed:#"TableViewCell" owner:self options:nil] objectAtIndex:0];
}
cell.label.text = #"cenas";
return cell;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
if (self.selectedCellIndexPath != nil && indexPath.row == self.selectedCellIndexPath.row) {
return 200;
}else{
return 44;
}
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
if (self.selectedCellIndexPath != nil && self.selectedCellIndexPath.row == indexPath.row) {
self.selectedCellIndexPath = nil;
}else{
self.selectedCellIndexPath = indexPath;
}
[tableView beginUpdates];
[tableView endUpdates];
}
And here are some pictures of what is happening:
In cellForRowAtIndexPath put the following code
if (self.selectedCellIndexPath != nil && indexPath.row == self.selectedCellIndexPath.row)
{
[YourLabel setHidden:NO];
}
else
{
[YourLabel setHidden:YES];
}
I found! I finally found!
I inserted inside of cellForRowAtIndexPath: the following:
[cell setClipsToBounds:YES];
This makes the content be only inside the cell
You need to manually hide/show the labels (Preferably with animation). Just because they are not inside the cells height, it doesn't mean they are hidden.
-(void)setSelectedCellIndexPath:(NSIndexPath*)indexPath
{
UITableViewCell *oldCell = [self.tableView cellForRowAtIndexPath:_selectedIndexPath];
//oldCell can be nil due to possibility that it's not on screen.
[self hideOutletsForCell:oldCell];
_selectedIndexPath = indexPath;
UITableViewCell *newCell = [self.tableView cellForRowAtIndexPath:_selectedIndexPath];
[self showOutletsForCell:newCell];
}
Also, you need to reset the outlets in cellForRowAtIndexPath, and only show outlets for the indexPath that is your _selectedIndexPath
I have a custom table view & cell where a cell is expanded when selected. It is now functioning properly and accordingly. However, When I select cells to expand them, it takes about half a second to respond. The code below is located in the didSelectRowAtIndexPath. My hypothesis is that between beginUpdates and endUpdates, there are too many things going on to increase the height of the original cell and then updating the whole table view. Is there another way I can better implement this?
**[_tableView beginUpdates];**
ReviewTestTableCell *reviewCell1 = (ReviewTestTableCell *)[tableView cellForRowAtIndexPath:indexPath];
CGRect rect = CGRectMake(reviewCell1.review.width, 900, reviewCell1.review.width, 900);
CGRect textRectb = [reviewCell1.review textRectForBounds:rect limitedToNumberOfLines:1000];
float labelHeight = textRectb.size.height;
reviewCell1.review.height = labelHeight;
expandHeight = labelHeight + 75 ;
if ([[userdefaults objectForKey:#"merchantType"] isEqual:#"T"])
{reviewCell1.height = labelHeight + 50;
reviewCell1.bottomRatingView.height = reviewCell1.bottomRatingView.height - 20;
}
else
{
reviewCell1.height = labelHeight + 75;}
reviewCell1.bottomRatingView.hidden = NO;
reviewCell1.bottomRatingView.top = reviewCell1.review.bottom;
**[_tableView endUpdates];**
[_isExpandList replaceObjectAtIndex:indexPath.row withObject:#1];
}
EDIT/ADD:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell3 = [self tableView:tableView cellForRowAtIndexPath:indexPath];
if ([_isExpandList[indexPath.row] isEqual: #1] && [[_dataList objectAtIndex:indexPath.row] valueForKey:#"Item1Score"] != nil) {
return cell3.height + 3 + 65;
}
else if ([_isExpandList[indexPath.row] isEqual: #0])
{
return cell3.height +5;
}
else return cell3.height +3;
}
You should not go through that elaborate dance of trying to manually expand the cell. You should certainly not manually call willDisplayCell.
Using the method described in the answer to this question, you should have a property or something to keep track of which cell was selected and make your heightForRowAtIndexPath: method adjust for that particular indexPath, then just call
[tableView beginUpdates];
[tableView endUpdates];
Which will call heightForRowAtIndexPath for every cell, which your method will give a larger height for when it matches the selected row. The tableView will smoothly adjust the height of your cell.
Something similar to:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([self.selectedIndexPath isEqual:indexPath]) {
return 80.0f;
}
return tableView.rowHeight;
}
Am I able to create a UILabel that is layouted upon many UITableViewCells?
I'm trying to make something like (that is just one section of my UITableView, each section can have one or more rows):
---------------------------------------------
| Multi-lined label | row1 values |
| with some useless | row2 values |
| text | row3 values |
---------------------------------------------
I managed to create a UILabel (in the first row of a section) that is multi-lined and is not clipping to bounds. That works really well (it was a bit tricky to count each sections row heights, but doable) besides one case: when I'm scrolling UITableView from bottom to top - UITableView renders last row (without UILabel) so it has "no evidence" of having UILabel (because it is maintained in the first row of section). Can I force some kind of relayouting first cell in section? I tried reloadRowsAtIndexPaths:withRowAnimation: with first row in each section whenever I layouted not first cell in section but it gave me layouting errors that I really do not understand. Or maybe there is another idea to do so?
-- EDITED
To be clear: I have a custom UITableViewCell with an IB view, it has a few labels that each row consist of and a label named labelName that I want to be "multi-lined" along rows in that section. LabelName.text is empty for each row besides first one in each section.
I am adding somescreenshots:
Good screenshot - when I am scrolling to bottom I'm getting proper effect:
Bad screenshot - when I am scrolling up, UITableView renders last row of section firstly, and afterwards renders upper rows - that gives effect of cut label (because multi-line label is in the first row)
I am not sure if code here will add anything to question - it is rather simple and almost whole logic is in tableView:heightForRowAtIndexPath. I can only present how do I create custom UITableViewCell:
CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[CustomTableViewCell reuseIdentifier]];
if (cell == nil) {
cell = [[CustomTableViewCell alloc] initWithOwner:self];
cell.clipsToBounds = NO;
cell.labelName.clipsToBounds = NO;
cell.contentView.superview.clipsToBounds = NO;
}
-- EDIT 2
Here is most of the code:
- (void) reloadData
{
NSUInteger index = 0;
for (NSDictionary *object in self.list) {
CGFloat height = [[object objectForKey:#"name"] sizeWithFont:self.labelFont constrainedToSize:self.labelSize].height;
[self.labelHeights addObject:NSNumberFloat(ceilf(height))];
index++;
}
[self.tableView reloadData];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *object = [self.list objectAtIndex:indexPath.section];
CGFloat height = [[self.labelHeights objectAtIndex:indexPath.section] floatValue];
NSUInteger count = [[object objectForKey:#"list"] count];
CGFloat cellHeight = 30.f;
if((indexPath.row + 1) == count){
cellHeight = MAX(8.f + height - 30.f * indexPath.row, 30.f);
}
return cellHeight;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.list count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[[self.list objectAtIndex:section] objectForKey:#"list"] count];
}
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *person = [self.list objectAtIndex:indexPath.section];
NSDictionary *object = [[person objectForKey:#"list"] objectAtIndex:indexPath.row];
CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[CustomTableViewCell reuseIdentifier]];
if (cell == nil) {
cell = [[CustomTableViewCell alloc] initWithOwner:self];
cell.backgroundColor = [UIColor clearColor];
cell.userInteractionEnabled = NO;
cell.clipsToBounds = NO;
cell.labelName.clipsToBounds = NO;
[cell.contentView.superview setClipsToBounds:NO];
}
if(indexPath.row == 0){
cell.labelName.text = [person objectForKey:#"name"];
CGFloat height = [[self.labelHeights objectAtIndex:indexPath.section] floatValue];
cell.labelName.numberOfLines = (int)(height / self.fontSizeHeight);
cell.labelName.frame = CGRectChangeHeight(cell.labelName.frame, height);
}
else{
cell.labelName.text = #"";
}
CGFloat cellHeight = [self tableView:self.tableView heightForRowAtIndexPath:indexPath];
cell.borderTop.hidden = YES;
cell.borderBottom.hidden = YES;
cell.borderBottomSmall.hidden = NO;
if(indexPath.row == 0){
cell.borderTop.hidden = NO;
}
if(indexPath.row + 1 == [[person objectForKey:#"list"] count]){
cell.borderBottom.hidden = NO;
cell.borderBottom.frame = CGRectChangeY(cell.borderBottom.frame, cellHeight - 1.f);
cell.borderBottomSmall.hidden = YES;
}
cell.labelDate.text = [object objectForKey:#"date"];
cell.labelPremium.text = [[object objectForKey:#"premium"];
return cell;
}
-- PARTIAL ANSWER
I managed to create a hack, that makes multi-line UILabel visibile when scrolling bottom to up at some point:
- (void) scrollViewDidScroll:(UIScrollView *)scrollView
{
NSArray *cells = [self.tableView visibleCells];
UITableViewCell *cell = [cells objectAtIndex:0];
[cell.superview bringSubviewToFront:cell];
}
I noticed that the part of the UILabel is covered by a row thats below of the UILabels row and that hack makes it would be properly displayed. But it has a drawback, when scrolling slowly from bottom to top it generates a flicker when label is created (part of it should be visible before real creation of UILabel).
Up mentioned answers are not solutions, but "hacks".
In the cell == nil block should be only the initialization.
You should not add any subviews in cell in cellForRowAtIndexPath.
The reason is simple: I will reuse a cell with some labels already added and add a new label.
Either use the default cell.textLabel, either create a subclass for UITableViewCell, with a
-(void)setData:(dictionary or string)object;
and in implementation just set the proper data to proper UI controls.
Add/create controls either in init method in the subclass, or in IB/Storyboard.
Call the dictionary or string should be picked in correspondence to indexPath, so you will always get proper data for proper cell at proper indexPath.
Try This
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellId = #"cellId";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if (cell==nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
}
for (UIView *subview in [cell.contentView subviews]) {
[subview removeFromSuperview];
}
/// your UI on cell goes here
return cell;
}
I have the following setup in my app:
I have a UITableView which has animated height change, when a cell is selected. In the cell is I have a label, which text I want to be on one line with truncation, when the cell is not selected and then when it is selected (the cell height get's bigger) I want the text (UILabel) to be on multiple lines, based on the text.
This is my code at the moment:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if(self.selectedIndex != nil && [self.selectedIndex isEqual:indexPath]) {
return kCellHeight * 3.9;
}
return kCellHeight;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
ProductCell *cell = (ProductCell *) [tableView cellForRowAtIndexPath:indexPath];
if (![indexPath isEqual:self.selectedIndex]) {
cell.addButton.hidden = NO;
cell.descLabel.numberOfLines = 0;
cell.descLabel.lineBreakMode = NSLineBreakByWordWrapping;
[cell.descLabel sizeToFit];
tableView.scrollEnabled = NO;
self.selectedIndex = indexPath;
}else {
self.selectedIndex = nil;
cell.descLabel.lineBreakMode = NSLineBreakByTruncatingTail;
tableView.scrollEnabled = YES;
cell.addButton.hidden = YES;
}
[tableView beginUpdates];
[tableView endUpdates];
}
The first part (when the cell is selected, expanded and the height gets bigger) the text fits as I want, but the second part (when the cell is contracted, and the cell height get back to the normal height) the text does not fit to the new height and is truncated as I want it to, it just stays multi lined. How can I fix this? So when the cell is selected the text gets multilined and when the cell is deselected the text gets back to its normal truncated state?
EDIT:
Override
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
and add below line in this function
cell.descLabel.numberOfLines = 1;