UITableView subview inside UITableViewCell won't display - ios

I'm trying to create a UITableview, where each cell contains another UITableView. It's basically a list of 2-3 rows, where each row then has a sublist of 2-3 rows inside.
The main ViewController behaves just like any other TableViewController, and inside the cellForRowAtIndexPath, it attempts to add a SubTableViewController.view with a dynamic height. The SubTableViewController is the second tableview and each has their own Datasource.
The problem I'm running into is the SubTableViewController appears to render properly using the debugger, with the correct data and number of cells, but it simply isn't appearing when rendering. I noticed it has a contentSize height of "0" in its ViewDidAppear, despite having 2+ rows produced with height. The project uses AutoLayout but the SubTableViewController's frame is set programmatically when added.
Any ideas what I can do to get something to appear here?
Structure of Data (periods are the subTable)
[{name:"activity 1",
periods:[{name:"period1",desc:"desc1"},
{name:"period2",desc:"desc2"},
{name:"period3",desc:"desc3"}
]
},
{name:"activity 2",
periods:[{name:"period1",desc:"desc1"},
{name:"period2",desc:"desc2"},
{name:"period3",desc:"desc3"}
]
}]
ViewController
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [_data count];
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
UIImageView *imageView = (UIImageView*)[cell viewWithTag:1];
UIView *subTableView = (UIView*)[cell viewWithTag:3];
return imageView.frame.size.height + subTableView.frame.size.height + 20;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"tableViewCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
ActivityModel *model = _data[indexPath.row];
UILabel *nameLabel = (UILabel*)[cell viewWithTag:2];
nameLabel.text = model.name;
UIView *internalView = (UIView*)[cell viewWithTag:10];
SubTableViewController *subTableViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"SubTableViewController"];
subTableViewController.activityModel = model;
subTableViewController.view.tag = 3;
subTableViewController.view.frame = CGRectMake(0,
60,
internalView.frame.size.width,
subTableViewController.view.frame.size.height);
[internalView addSubview:subTableViewController.view];
[internalView bringSubviewToFront:subTableViewController.view];
[subTableViewController.tableView reloadData];
return cell;
}
SubTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
int height = 0;
for (int section = 0; section < [self numberOfSectionsInTableView:_tableView]; section++){
for(int row = 0; row < [self tableView:_tableView numberOfRowsInSection:section]; row++){
height += [self tableView:_tableView heightForRowAtIndexPath:[NSIndexPath indexPathForItem:row inSection:section]];
}
}
CGRect tableFrame = self.tableView.frame;
tableFrame.size.height = height;
self.tableView.frame = tableFrame;
CGRect viewFrame = self.view.frame;
viewFrame.size.height = height;
self.view.frame = viewFrame;
[self.view bringSubviewToFront:_tableView];
// [self.tableView layoutIfNeeded];
// [self.tableView setNeedsDisplay];
// [self.tableView reloadData];
// _tableView.contentSize = CGSizeMake(600, 52);
self.view.backgroundColor = [UIColor redColor];
self.tableView.backgroundColor = [UIColor blueColor];
self.tableView.hidden = NO;
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [_activityModel.periods count];
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
UILabel *nameLabel = (UILabel*)[cell viewWithTag:3];
UITextView *textView = (UITextView*)[cell viewWithTag:5];
float textViewHeight = [textView.text sizeWithFont:[UIFont systemFontOfSize:13] constrainedToSize:CGSizeMake([UIScreen mainScreen].bounds.size.width - 40, MAXFLOAT) lineBreakMode:NSLineBreakByWordWrapping].height;
return nameLabel.frame.size.height + textViewHeight + 20;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"tableViewCell"];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
PeriodModel *model = _activityModel.periods[indexPath.row];
UILabel *nameLabel = (UILabel*)[cell viewWithTag:3];
UILabel *durationLabel = (UILabel*)[cell viewWithTag:4];
UITextView *textView = (UITextView*)[cell viewWithTag:5];
nameLabel.text = model.name;
durationLabel.text = [NSString stringWithFormat:#"%d minutes", (int)(model.durationSeconds/60)];
textView.text = model.description;
textView.textContainerInset = UIEdgeInsetsZero;
textView.textContainer.lineFragmentPadding = 0;
textView.scrollEnabled = NO;
CGRect frame = textView.frame;
frame.size.height = [textView.text sizeWithFont:[UIFont systemFontOfSize:12] constrainedToSize:CGSizeMake(cell.contentView.frame.size.width, MAXFLOAT) lineBreakMode:NSLineBreakByWordWrapping].height;
textView.frame = frame;
return cell;
}
Simulator Image. The title is the outer UITableCell, and below it in red is the added SubTableViewController.view. The UITableView should cover the red, but is not appearing at all here. It is not hidden, underneath, etc.

Do not put a table view inside your cells.
There is little benefit to doing this for your described use. Use sections and rows. Each section would be your current cell. And each row in that section would be a row from your current embedded table view.
Further, if your internal table views had differing sizes you would be needing to calculate all of the internal cell heights for each external cell. That is exactly what happens with sections and rows, but you as the developer only need to worry about the rows and UITableView will handle the section sizing.

Related

How to populate, Expandable TableView with two NSMutableArray using objective-c

I am using an Expandable UITableview created by Tom Fewster. I want to tweak the example using two NSMutableArrays, which is a scenario whereby if someone wants to populate an expandable/collapse treeview table from webservice json data would want to achieve. So since in his example the GroupCell does not have an array of, I am wondering how can I do it? Please bear in mind that my Objective-C is still rusty hence, I'm asking this question.
With my attempt is only displaying the first ObjectAtIndex:indexPath:0 for the group.
I want to be able to populate the table and get output like this;
Group A
Row 1a
Row 2a
Row 3a
Group B
Row 1b
Row 2b
Group C
Row 1c
Row 2c
Row 3c
and so on.
You may use JSON data as well to explain your answer if you understand it better that way.
Here i want to populate the table with JSON data so the GroupCell show class_name and rowCell show subject_name. This is the console of what I am parsing from the JSON web-service;
(
{
"class_id" = 70;
"class_name" = Kano;
subject = (
"subject_id" = 159;
"subject_name" = "Kano Class";
}
);
},
{
"alarm_cnt" = 0;
"class_id" = 71;
"class_name" = Lagos;
subject = (
"subject_id" = 160;
"subject_name" = "Lagos Class";
}
);
},
{
"alarm_cnt" = 3;
"class_id" = 73;
"class_name" = Nasarawa;
subject = (
"subject_id" = 208;
"subject_name" = "DOMA Class";
},
"subject_id" = 207;
"subject_name" = "EGGON Class";
},
"subject_id" = 206;
"subject_name" = "KARU Class";
},
"subject_id" = 209;
"subject_name" = "LAFIA Class";
},
"subject_id" = 161;
"subject_name" = "Nasarawa State Class";
}
);
},
{
"alarm_cnt" = 2;
"class_id" = 72;
"class_name" = Rivers;
subject = (
"subject_id" = 162;
"subject_name" = "Rivers Class";
}
);
}
)
I have tried this here is my snippet
- (UITableViewCell *)tableView:(ExpandableTableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"RowCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSDictionary *d=[_sitesJson objectAtIndex:0] ;
NSArray *arr=[d valueForKey:#"subject_name"];
NSDictionary *subitems = [arr objectAtIndex:0];
NSLog(#"Subitems: %#", subitems);
NSString *siteName = [NSString stringWithFormat:#"%#",subitems];
cell.textLabel.text =siteName;
//}
NSLog(#"Row Cell: %#", cell.textLabel.text);
// just change the cells background color to indicate group separation
cell.backgroundView = [[UIView alloc] initWithFrame:CGRectZero];
cell.backgroundView.backgroundColor = [UIColor colorWithRed:232.0/255.0 green:243.0/255.0 blue:1.0 alpha:1.0];
return cell;
}
- (UITableViewCell *)tableView:(ExpandableTableView *)tableView cellForGroupInSection:(NSUInteger)section
{
static NSString *CellIdentifier = #"GroupCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UILabel *textLabel = (UILabel *)[cell viewWithTag:2];
NSDictionary *d2 = [_regionsJson objectAtIndex:0];
NSArray *arr2 = [d2 objectForKey:#"class_name"];
NSString *regions = [[arr2 objectAtIndex:section]objectAtIndex:0];
textLabel.textColor = [UIColor whiteColor];
textLabel.text = [NSString stringWithFormat: #"%# (%d)", regions, (int)[self tableView:tableView numberOfRowsInSection:section]];
NSLog(#"Group cell label: %#", textLabel.text);
// We add a custom accessory view to indicate expanded and colapsed sections
cell.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"ExpandableAccessoryView"] highlightedImage:[UIImage imageNamed:#"ExpandableAccessoryView"]];
UIView *accessoryView = cell.accessoryView;
if ([[tableView indexesForExpandedSections] containsIndex:section]) {
accessoryView.transform = CGAffineTransformMakeRotation(M_PI);
} else {
accessoryView.transform = CGAffineTransformMakeRotation(0);
}
return cell;
}
He, just need to update one single method little bit way
- (UITableViewCell *)tableView:(ExpandableTableView *)tableView cellForGroupInSection:(NSUInteger)section
{
static NSString *CellIdentifier = #"GroupCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSIndexPath *indexPath;
NSString *regions = [[_dataGroup objectAtIndex:section]objectAtIndex:0];
cell.textLabel.text = [NSString stringWithFormat: #"%# ", regions];
// We add a custom accessory view to indicate expanded and colapsed sections
cell.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"ExpandableAccessoryView"] highlightedImage:[UIImage imageNamed:#"ExpandableAccessoryView"]];
UIView *accessoryView = cell.accessoryView;
if ([[tableView indexesForExpandedSections] containsIndex:section]) {
accessoryView.transform = CGAffineTransformMakeRotation(M_PI);
} else {
accessoryView.transform = CGAffineTransformMakeRotation(0);
}
return cell;
}
May help it you.
HTH, Enjoy Coding !!
I think you need to create a TableView which will have a sections array, and each sections row will be populated using the corresponding sections array. Tapping on a section will expand it and it's all rows will be visible.
To meet your requirements, you could follow the below steps as well -
1) Your modal should have a array for sections. The sections array will contain the sections objects, name of the section and corresponding array of the rows.
2) Implement the data source methods of the table view like
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return [section count];
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 50; // sections height
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return nil;
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return nil;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *view = [[[UIView alloc] initWithFrame:CGRectMake(0 , 0, tableView.frame.size.width , 50)] autorelease];
[view setBackgroundColor:[UIColor redColor]];
view.layer.masksToBounds = YES;
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(5 , 2 , view.frame.size.width - 10 , view.frame.size.height - 3)];
label.text = ((SectionObject *)[section objectAtIndex:indexPath.section]).sectionName;
label.backgroundColor = [UIColor clearColor];
label.textAlignment = NSTextAlignmentLeft;
label.textColor = [UIColor WwhiteColor];
label.clipsToBounds = YES;
label.font = [UIFont fontWithName:#"HelveticaNeue-CondensedBold" size:14.0f];
label.layer.masksToBounds = YES;
UIImageView *arrowImage = [[UIImageView alloc] initWithFrame:CGRectMake(view.frame.size.width - 30, 0, 17 , 17)];
[arrowImage setCenter:CGPointMake(arrowImage.center.x , (view.frame.size.height/2) ) ];
if(section == self.m_currentSelectedSection)
[arrowImage setImage:self.m_upArrowImage];
else
[arrowImage setImage:self.m_downArrowImage];
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, view.frame.size.width, view.frame.size.height)];
button.tag = section;
[button addTarget:self action:#selector(sectionTapped:) forControlEvents:UIControlEventTouchUpInside];
button.backgroundColor = [UIColor clearColor];
[view addSubview:label];
[label release];
[view addSubview:arrowImage];
[arrowImage release];
[view addSubview:button];
[button release];
view.clipsToBounds = YES;
return view;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger count = 0;
if(self.m_currentSelectedSection == section)
count = [((SectionObject *)[section objectAtIndex:indexPath.section]).rowArray count];
return count;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 40.0;
}
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * cellId = #"cellIdentifier";
UITableViewCell *cell = nil;
cell = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:cellId];
if(cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
//customize cell
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
When ever any section will be tapped following event will be invoked
- (void) sectionTapped:(UIButton *)button
{
self.m_currentSelectedSection = button.tag;
[self performSelector:#selector(refreshView) withObject:nil afterDelay:POINT_ONE_SECOND];
if(m_winnerSlotList->at(self.m_currentSelectedSection).m_leaderboardList.size())
[self.m_leaderboardTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:button.tag] atScrollPosition:UITableViewScrollPositionTop animated:YES];
UIView *baseView = [button superview];
if(baseView)
{
for(int ii = 0 ; ii < [[baseView subviews] count] ; ii++ )
{
UIView *anyView = [[baseView subviews] objectAtIndex:ii];
if([anyView isKindOfClass:[UIImageView class]])
[(UIImageView *)anyView setImage:self.m_upArrowImage];
}
}
}
Initialize self.m_currentSelectedSection = 0, for the first time, this will show the rows for 0th section. As any section is tapped it's rows will be visible (corresponding section rows will expand) and the rows for the previous selected section will be hidden(previous section rows will collapse).
If you need to show more than one section as expanded than you need to keep track of all the section whether a section is expanded or not and accordingly load show/ hide the cells for the corresponding section.

Counting UITableView ContentView height is not always correct

I'm creating a messenger app, which displays a dialog between two people.
Parsing API response, I got text for inbox and outbox messages. Then I create a cell, using a UITableViewCell prototype from the storyboard.
All constraints are adjusted correctly.
The problem is that I use
[self.tableView scrollToRowAtIndexPath:lastIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];
to scroll the tableView to the bottom to have the last message in focus but the contentView of the upper cells is not counted at this time.
So i had to count height of cell before it is displayed. For this i use
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
RJMessageCell *cell = [self configureBasicCellAtIndexPath:indexPath];
[cell setNeedsLayout];
[cell layoutIfNeeded];
CGSize maximumSize = CGSizeMake(320.0, UILayoutFittingCompressedSize.height);
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:maximumSize].height;
return height;
}
- (RJMessageCell *)configureBasicCellAtIndexPath:(NSIndexPath *)indexPath {
static NSString *inboxIdentifier = #"Inbox";
static NSString *outboxIdentifier = #"Outbox";
NSString *identifier;
RJMessage *message = [[[self.messageSectionsArray objectAtIndex:indexPath.section] messages] objectAtIndex:indexPath.row];
if (message.messageIsMine) {
identifier = outboxIdentifier;
} else {
identifier = inboxIdentifier;
}
RJMessageCell *cell = [self.tableView dequeueReusableCellWithIdentifier:identifier];
cell.messageView.layer.cornerRadius = 10.f;
cell.messageView.clipsToBounds = YES;
if ([message.text isEqualToString:#""]) {
cell.messageTextLabel.text = #" ";
} else {
cell.messageTextLabel.text = message.text;
}
cell.messageTextLabel.numberOfLines = 0;
cell.messageTextLabel.lineBreakMode = NSLineBreakByWordWrapping;
cell.timeLabel.text = [self stringTimeFromTimeInterval:message.messageInterval];
if (message.messageState == RJMessageStateUnread) {
cell.backgroundColor = [UIColor colorWithRed:151/255.0 green:200/255.0 blue:255/255.0 alpha:0.4];
} else {
cell.backgroundColor = [UIColor clearColor];
}
return cell;
}
Also the UILabel on screenshots are custom, to setBounds to label
- (void)setBounds:(CGRect)bounds {
[super setBounds:bounds];
if (self.numberOfLines == 0 && bounds.size.width != self.preferredMaxLayoutWidth) {
self.preferredMaxLayoutWidth = self.bounds.size.width;
[self setNeedsUpdateConstraints];
}
}
And the last thing i do, adding estimatedRowHeight in viewDidLoad
self.tableView.estimatedRowHeight = self.tableView.rowHeight;
self.tableView.rowHeight = UITableViewAutomaticDimension;
The first time they appear everything looks good, but when I scroll the table up and down, my cells change their size randomly.
What's wrong with the cell height?
You have to return Height by calculating the text width & height which you want to place inside the cell.
//Height For Row at index path
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellText = [NSString stringWithFormat:#"%#",[arrDataSource[indexPath.row]]];
UIFont *cellFont = [UIFont fontWithName:#"Helvetica" size:15.0];
NSAttributedString *attributedText =
[[NSAttributedString alloc]
initWithString:cellText
attributes:#
{
NSFontAttributeName: cellFont
}];
CGRect rect = [attributedText boundingRectWithSize:CGSizeMake(tableView.bounds.size.width - 90.0, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
context:nil];
return rect.size.height + 42;
}

TableView cell disappear on scroll

I found a lot of answer for this question but I don't anderstand how to manage my code.
When I scroll the tableView content, the cell disappear. I just have two texts in the cell question and answer.
I've tried to use an identifier (reusable) but the code doesn't change anything...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UILabel *textLabel = nil;
NSString* text = #"";
//static NSString *CellIdentifier = #"Cell";
NSString *CellIdentifier = [NSString stringWithFormat:#"%ld_%ld",(long)indexPath.section,(long)indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
int y = -20;
float titleFontSize = 14.0f;
if(indexPath.row == 0) {
y = 0;
titleFontSize = 16.0f;
}
if(cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
textLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[textLabel setLineBreakMode:NSLineBreakByWordWrapping];
[textLabel setMinimumFontSize:14.0f];
[textLabel setNumberOfLines:0];
[textLabel setFont:[UIFont systemFontOfSize:14.0f]];
[textLabel setTag:1];
[textLabel setTextColor:[UIColor colorWithRed:57/255.0 green:55/255.0 blue:64/255.0 alpha:1.0]];
// Titre
if(indexPath.row == 0) {
text = question;
[cell setBackgroundColor:[UIColor colorWithRed:220/255.0 green:224/255.0 blue:241/255.0 alpha:1.0]];//[UIColor colorWithRed:.945f green:.921f blue:.78f alpha:1]];
[textLabel setFont:[UIFont fontWithName:#"Helvetica-Bold" size:titleFontSize]];
}
// Contenu
else {
text = answer;
}
CGSize constraint = CGSizeMake(285, 20000.0f);
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:titleFontSize] constrainedToSize:constraint lineBreakMode:NSLineBreakByWordWrapping];
[textLabel setText:text];
[textLabel setFrame:CGRectMake(25, y, 275, MAX(size.height + 60, 44.0f))];
[textLabel setBackgroundColor:[UIColor clearColor]];
[cell addSubview:textLabel];
return cell;
}
UPDATE CALCULATING CELL SIZE
-(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
float titleFontSize = 14.0f;
float offSet = 0;
NSString *text = #"";
if(indexPath.row == 0) {
text = question;
titleFontSize = 16.f;
offSet = 10;
}
else
text = answer;
CGSize constraint = CGSizeMake(285, 20000.0f);
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:titleFontSize] constrainedToSize:constraint lineBreakMode:NSLineBreakByWordWrapping];
CGFloat height = MAX(size.height + 40, 44.0f);
return height + offSet;
}
I've had this problem in the past and it's always due to the cells height being incorrect... You've got quite a few instances where your two cells are not the same, so we are going to have to get a grip on that code.
int y
this value is also concerning me a little as it looks like it sets the textLabel frame origin.y to -20 ... if you want a different offset for Y in each cell but the rest of the cell is the same, that's fine, but in this case I think I'd suggest a different UITableViewCell subclass for the two types of cell... And change all the label properties etc inside that subclass..
So...
Make a UITableViewCell subclass for each type of cell, call them whatever you like... for example MYQuestionTableViewCell and MYAnswerTableViewCell
inside the cell MYQuestionTableViewCell .h file
#define TEXT_LABEL_WIDTH 285.0f
#define TEXT_LABEL_QUESTION_FONT_SIZE 16.0f
inside that cell MYQuestionTableViewCell .m file do
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.backgroundColor = [UIColor redColor];
self.clipsToBounds = YES; // just to make sure we're calculating the height correctly
// set up your textLabel etc. in here
self.textLabel.textColor = [UIColor whiteColor];
self.textLabel.font = [UIFont fontWithName:#"Helvetica-Bold" size: TEXT_LABEL_QUESTION_FONT_SIZE]];
}
return self;
}
-(void)layoutSubviews
{
[super layoutSubviews];
self.textLabel.frame = CGRectMake(0.0f, 0.0f, TEXT_LABEL_WIDTH, self.contentView.frame.size.height);
}
Now our textLabel will be whatever the height of the cell is... Now it's set in only one place, we know where the issue will be if it's not correct.
In your second MYAnswerTableViewCell subclass you'll need the other values set
.h
#define TEXT_LABEL_ANSWER_FONT_SIZE 14.0f
.m
same as the other cell but changing for it's property values
As you are also using different fonts in the two different cells... it might be easier to use a switch.. but I'll try to keep it similar to what you were doing before.. this sort of doubling up of code is the cause of the confusion, but sometimes it's unavoidable.. We'll just try to keep it as simple as we can.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGFloat offSet = 0.0;
NSString *text = #"";
UIFont *fontUsed = nil;
if(indexPath.row == 0) {
fontUsed = [textLabel setFont:[UIFont fontWithName:#"Helvetica-Bold" size: TEXT_LABEL_QUESTION_FONT_SIZE]];
text = question;
offSet = 10.0f;
}
else
{
fontUsed =[UIFont systemFontOfSize:TEXT_LABEL_ANSWER_FONT_SIZE];
text = answer;
}
NSLog (#"TEXT: %#",text); // checking if the text is set...
CGSize constraint = CGSizeMake(TEXT_LABEL_WIDTH, HUGE_VALF); // personally I prefer HUGE_VALF for that
CGSize size = [text sizeWithFont:fontUsed constrainedToSize:constraint lineBreakMode:NSLineBreakByWordWrapping];
CGFloat height = MAX(size.height + 40.0f, 44.0f); // 40.0f for padding?
return height + offSet;
}
You shouldn't be adding labels to a cell unless you need to, they come with some provided... now your cellForRow can look like this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *QuestionCellIdentifier = #"QuestionCell";
static NSString *AnswerCellIdentifier = #"AnswerCell";
if (indexPath.row == 0)
{
MYQuestionTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:QuestionCellIdentifier];
if (!cell)
cell = [[MYQuestionTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:QuestionCellIdentifier];
cell.textLabel.text = question;
return cell;
}
MYAnswerTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: AnswerCellIdentifier];
if (!cell)
cell = [[MYAnswerTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:AnswerCellIdentifier];
cell.textLabel.text = answer;
return cell;
}
You'll also need
#import "MYQuestionTableViewCell.h"
#import "MYAnswerTableViewCell.h"
at the top of your view controller

scrollViewDidScroll method not giving reference to all scrollviews from table view

I have created tableView with custom cells that contain image view and scrollView. All scroll views contain labels wider than screen bounds, so when I scroll to left/right I want all scrollViews to scroll in same direction. Problem is I don't know how to get reference to each scrollView in scrollViewDidScroll method.
my viewController class:
#import "EPGViewController.h"
#interface EPGViewController (){
NSArray *EPGList;
int scrollPositionX;
}
#end
#implementation EPGViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationController.navigationBarHidden = false;
EPGList = [NSMutableArray arrayWithObjects:#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"11",#"12", nil];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return EPGList.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *hlCellID = #"EPGCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:hlCellID];
if(cell == nil) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:hlCellID];
cell.accessoryType = UITableViewCellAccessoryNone;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
UILabel *label = (UILabel *)[cell viewWithTag:15];
label.text = EPGList[indexPath.row];
UIScrollView *scrolView = (UIScrollView *)[cell viewWithTag:16];
scrolView.backgroundColor = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0];
scrolView.delegate = self;
scrolView.tag = indexPath.row+101;
scrolView.scrollEnabled = YES;
scrolView.contentSize = CGSizeMake(scrolView.frame.size.width,scrolView.frame.size.height);
[scrolView setShowsHorizontalScrollIndicator:NO];
[scrolView setShowsVerticalScrollIndicator:NO];
int i=0;
for(i=0;i<15;i++){
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0+i*100, 0, 100, 100)];
label.text = #"HELLO";
label.textColor = [UIColor blackColor];
label.backgroundColor = [UIColor clearColor];
label.textAlignment = NSTextAlignmentCenter;
label.font = [UIFont fontWithName:#"ArialMT" size:18];
[scrolView addSubview:label];
scrolView.contentSize = CGSizeMake(scrolView.frame.size.width+i*label.frame.size.width,scrolView.frame.size.height);
}
[cell addSubview:scrolView];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 80.0;
}
- (void)scrollViewDidScroll:(UIScrollView *)callerScrollView {
scrollPositionX = callerScrollView.contentOffset.x;
//if this is called with table view exit
if (callerScrollView == self.tableView) return;
int indexPath = callerScrollView.tag-101;
NSLog(#"TAG: %d",indexPath);
for (UITableViewCell *cell in self.tableView.visibleCells) {
//TODO: Don’t use tags.
UIScrollView *cellScrollView = (UIScrollView *)[cell viewWithTag:16];
if (callerScrollView == cellScrollView) continue;
cellScrollView.contentOffset = CGPointMake(scrollPositionX, 0);
}
}
#end
EDIT:
I managed to solve my problem by deleting line where I add different tags to scrollviews in table view. now I just set offset to scrollview with tag 16.
There are multiple wrong things in your code. Here is better implementation:
- (void)scrollViewDidScroll:(UIScrollView *)callerScrollView {
scrollPositionX = callerScrollView.contentOffset.x;
if (callerScrollView == self.tableView) return;
for (UITableViewCell *cell in self.tableView.visibleCells) {
//TODO: Don’t use tags.
UIScrollView *cellScrollView = (UIScrollView *)[cell viewWithTag:16];
if (callerScrollView == cellScrollView) continue;
cellScrollView.contentOffset = CGPointMake(scrollPositionX, 0);
}
}
Some additonal notes:
This method will be called by table view too, so you will have to check that.
Don’t use any indexes, just plain iteration.
I used checking of scrollViews, but basically there it would be OK without it.
Using tags to identify subviews is not a good idea.
Don’t call reloadData every time!
You should use [tableView visibleCells]; in order to get all cells that are visible at this moment.
You could use UITableView's visibleCells method to get all visible cells and get your scrollviews from there (you can use viewwithTag like you do, but on all cells).
Then you would have to remember to adjust the scrollview position as cells are reused/recreated - probably keep the scrolled offset as a property of your View Controller.
The problem is that you're using dequeueReusableCellWithIdentifier rather than just looping through the cells that have already been displayed.
Do something like:
for (NSInteger j = 0; j < [tableView numberOfSections]; ++j)
{
for (NSInteger i = 0; i < [tableView numberOfRowsInSection:j]; ++i)
{
if (!(indexPath.section == j && indexPath.row == i))
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:j]]];
UIScrollView *scrollView = (UIScrollView *)[cell viewWithTag:16];
scrollView.contentOffset = CGPointMake(position_x, scrollView.frame.size.height);
}
}
}

How to increase the label and cell size on clicking a button in a TableViewCell.

I want to expand the cell size after clicking a seeMoreBtn on cell.
The label and cells have varying length, but their is a constraint in size of label.
When a status is too big, I added a seeMoreBtn, after clicking on see more the remaining text will be shown below, then how to increase the label and cell size.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
NSString *text = [items objectAtIndex:[indexPath row]];
CGSize constraint = CGSizeMake(300.0f, 150.0f);
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:constraint lineBreakMode:NSLineBreakByCharWrapping];
CGFloat height1 = MAX(size.height, 44.0f);
return height1 + (40.0f);
}
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = [NSString stringWithFormat:#"Cell-%d",indexPath.row];
cell=[tv dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
int lbltag = 1000;
label=[[UILabel alloc]initWithFrame:CGRectZero];
[label setLineBreakMode:NSLineBreakByWordWrapping];
[label setMinimumScaleFactor:14.0f];
[label setNumberOfLines:0];
[label setFont:[UIFont systemFontOfSize:14.0f]];
NSString *text = [items objectAtIndex:[indexPath row]];
[label setText:text];
label.tag = lbltag;
[cell addSubview:label];
CGSize constraint1=CGSizeMake(300.0f, 150.0f);
CGSize size1=[text sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:constraint1 lineBreakMode:NSLineBreakByWordWrapping];
[label setFrame:CGRectMake(10.0f, 10.0f, 300.0f, MAX(size1.height, 44.0f))];
int countText=text.length;
if (countText>=350) {
seeMoreBtn=[UIButton buttonWithType:UIButtonTypeRoundedRect];
[seeMoreBtn setTitle:#"See More" forState:UIControlStateNormal];
seeMoreBtn.frame=CGRectMake(220.0f, MAX(size1.height, 44.0f)-10, 80.0f, 20.0f);
seeMoreBtn.tag=indexPath.row ;
[seeMoreBtn addTarget:self action:#selector(increaseSize:) forControlEvents:UIControlEventTouchUpInside];
[cell addSubview:seeMoreBtn];
}
return cell;
}
-(void)increaseSize:(UIButton *)sender{
//What to write here that can adjust the label and cell size
}
It would be better, if you subclass the UITableViewCell and use the layoutSubviews to adjust when you adjust the size of the cell.
//In SMTableViewCell.h
#interface SMTableViewCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel *statusLabel;
#property (weak, nonatomic) IBOutlet UIButton *seeMoreButton;
//SMTableViewCell.m
- (void)layoutSubviews
{
CGRect labelFrame = self.statusLabel.frame;
labelFrame.size.height = self.frame.size.height - 55.0f;
self.statusLabel.frame = labelFrame;
CGRect buttonFrame = self.seeMoreButton.frame;
buttonFrame.origin.y = labelFrame.origin.y+labelFrame.size.height+10.0f;
self.seeMoreButton.frame = buttonFrame;
}
Keep an array to store the selectedIndexPaths:
#property (nonatomic, strong) NSMutableArray *selectedIndexPaths;
Calculate the height of the cell:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
BOOL isSelected = [self.selectedIndexPaths containsObject:indexPath];
CGFloat maxHeight = MAXFLOAT;
CGFloat minHeight = 40.0f;
CGFloat constrainHeight = isSelected?maxHeight:minHeight;
CGFloat constrainWidth = tableView.frame.size.width - 20.0f;
NSString *text = self.items[indexPath.row];
CGSize constrainSize = CGSizeMake(constrainWidth, constrainHeight);
CGSize labelSize = [text sizeWithFont:[UIFont systemFontOfSize:15.0f]
constrainedToSize:constrainSize
lineBreakMode:NSLineBreakByCharWrapping];
return MAX(labelSize.height+75, 100.0f);
}
Initialize custom Show more TableViewCell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"CellIdentifier";
SMTableViewCell *cell= (SMTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell = [[[NSBundle mainBundle]loadNibNamed:NSStringFromClass([SMTableViewCell class])
owner:nil
options:nil] lastObject];
}
BOOL isSelected = [self.selectedIndexPaths containsObject:indexPath];
cell.statusLabel.numberOfLines = isSelected?0:2;
NSString *text = self.items[indexPath.row];
cell.statusLabel.text = text;
NSString *buttonTitle = isSelected?#"See Less":#"See More";
[cell.seeMoreButton setTitle:buttonTitle forState:UIControlStateNormal];
[cell.seeMoreButton addTarget:self action:#selector(seeMoreButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[cell.seeMoreButton setTag:indexPath.row];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
Button click event method:
- (void)seeMoreButtonPressed:(UIButton *)button
{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:button.tag inSection:0];
[self addOrRemoveSelectedIndexPath:indexPath];
}
- (void)addOrRemoveSelectedIndexPath:(NSIndexPath *)indexPath
{
if (!self.selectedIndexPaths) {
self.selectedIndexPaths = [NSMutableArray new];
}
BOOL containsIndexPath = [self.selectedIndexPaths containsObject:indexPath];
if (containsIndexPath) {
[self.selectedIndexPaths removeObject:indexPath];
}else{
[self.selectedIndexPaths addObject:indexPath];
}
[self.tableView reloadRowsAtIndexPaths:#[indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
Same Event is given if the cell is selected:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[self addOrRemoveSelectedIndexPath:indexPath];
}
Sample Demo project link.
Add a property like:
#property(strong, nonatomic) NSArray *enlargedIndexPaths;
Initialize it to an empty array. Implement the table view delegate:
- (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return ([self.enlargedIndexPaths containsObject:indexPath])? 88.0 : 44.0;
}
Then in increaseSize:
UITableViewCell *cell = sender.superview;
NSIndexPath *indexPath = [self.tableView indexPathFoCell:cell];
[self.enlargedIndexPaths addObject:indexPath];
// do you want to enlarge more than one at a time? You can remove index paths
// here, too. Just reload them below
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:self.enlargedIndexPaths] withRowAnimation:UITableViewRowAnimationTop];
[self.tableView endUpdates];
Well, the idea is to change the cells height, based on a value you set for the cell.
You can have an NSArray, where you just mark for all cells a value: isExpanded (true or false).
To recap, create an NSMutableArray that will store the cell state (expanded or not):
NSMutableArray *cellState;
init the array and fill it with zeros for all your UITableView items Array.
When the cell is expanded, set the cellState objectAtIndex value from 0 to 1 (marking it as expanded).
Then reload that cell:
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section]] withRowAnimation:UITableViewRowAnimationFade];
and modify the - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath; function to return a bigger value if the cell is expanded...
That's it!

Resources