I have a TableViewController with Prototype Cells that look like this:
I would like to only display the Title and Date labels by default when the TableView data loads. Additionally, when the user selects a particular cell, I want the cell to expand so that the user can read the contents of the Description label (which can run over multiple lines).
I have looked through several sources on StackOverflow including the following: https://stackoverflow.com/a/3075493/4481169.
Most of the sources I have looked through assume that when the cell is not selected it will return to a specific height. However, the text of my Title label can take up multiple lines in some cases which means that each cell will have a different initial row height to begin with.
How can I make it so when the user selects a particular cell it expands to display all three labels and when that cell is unselected, the cell reverts to its original height?
Probably the easiest way to achieve this would be having 2 cell prototypes for:
Not selected: with 2 UILabels; // let is be with ID: #"NotSelectedCell"
Selected: with all 3 UILabels; // #"SelectedCell"
Also You need to store the indexPath of the selected cell, so:
#implementation InformationTableViewController {
NSIndexPath* selectedCellIndexPath;
}
In your tableView delegate method tableView:cellForRowAtIndexPath: you need to switch between NotSelectedCell and SelectedCell according to the indexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([indexPath isEqual:selectedCellIndexPath]) {
NSString* cellIdentifier = #"SelectedCell";
InformationTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: cellIdentifier forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
if (cell == nil) {
cell = [[InformationTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
cell.accessoryType = UITableViewCellAccessoryNone;
}
cell.title = // title
cell.date = // date
cell.description = // description
return cell;
}
else {
NSString* cellIdentifier = #"NotSelectedCell";
InformationTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: cellIdentifier forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
if (cell == nil) {
cell = [[InformationTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
cell.accessoryType = UITableViewCellAccessoryNone;
}
cell.title = // title
cell.date = // date
return cell;
}
}
Also you must save a new indexPath everytime user selects a new cell and reload the tableView:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
selectedCellIndexPath = [indexPath copy];
[self.tableView reloadData];
}
In order to recover the scroll position of the tableView before reload, you have to do the following:
CGFloat tableViewContentHeight = self.tableView.contentSize.height;
[self.tableView reloadData];
CGFloat newTableViewContentHeight = self.tableView.contentSize.height;
self.tableView.contentOffset = CGPointMake(0, newTableViewContentHeight - tableViewContentHeight);
For your requirement i would suggest you to do this steps
First of don't give height constraint to description and even don't
add any text in it i.e keep it blank
Then when you get the values for description store in other array
After this on delegate method for didselectrowatindex you can fill
the value of that description and it reload it .
So it will fill the text in description and take the height as
required
Related
when I set UITableViewCellSeparatorStyleNone to a tableView, still the separatorview is visble?
I set the property of tableview,
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
and in view debug I still found a UITableViewCellSeparatorView in my cell,
How do I remove the separator view?
You can set UITableViewCellSeparatorStyleNone in tableview in storyboard.
Here i attach screenshot for more clarification.
since cells are being reused when presented (with dequeueReusableCellWithIdentifier): you have to use a different identifier for that cell.. I made a custom UITableViewCell subclass for it too.
this is a code where my last cell is a special cell that will load X more cells..
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == lastIndex) {
LoadingNextCellView *cell = [tableView dequeueReusableCellWithIdentifier:#"LoadingNextCell"];
if (cell == nil) {
cell = [[LoadingNextCellView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"LoadingNextCell"];
}
cell.indexPath = indexPath;
cell.titleLabel.text = [NSString stringWithFormat:#"Loading next %d trees..",PRELOAD_TREES];
return cell;
} else {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
}
return cell;
}
Customie your cell according to this logic.
I have the following behavior: i have a table with number of rows = 20
i have created in Storyboard UITableView Cell with UIImage.
I use the function didSelectRowAtIndexPath for show and hide a image. If i select a row the UIImage xxx.png will be displayed. If i select the same row again then the Image is hidden. That works perfekt but when i scroll down i see rows with images and i have no selected this rows.
I will only see the image when i select a row and not duplicates wehen i scroll the table.
Here is my code:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
WorkoutTableVCell *cell = [tableView dequeueReusableCellWithIdentifier:#"WorkoutCell" forIndexPath:indexPath];
cell.checkImage= nil;
if (cell == nil)
{
cell = [[WorkoutTableVCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"WorkoutCell"];
}
else{
//Old cell...get previously createdd imageview
cell.checkImage = (UIImageView*)[cell.contentView viewWithTag:1001];
}
long row = [indexPath row];
cell.lblCell.text = _MyValues[row];
return cell;
}
I hope you can understand my problem and sorry for my bad english :(
Cells are reused, so you need set the image of the cell each time. You need to have another data source that contains images for each indexPath and use that to set the image for the UIImageView in the cell.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
WorkoutTableVCell *cell = [tableView dequeueReusableCellWithIdentifier:#"WorkoutCell" forIndexPath:indexPath];
if (cell == nil)
{
cell = [[WorkoutTableVCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"WorkoutCell"];
}
long row = [indexPath row];
cell.lblCell.text = _MyValues[row];
// set the image for the UIImageView here
cell.imageView.image = _MyImages[row];
return cell;
}
An even better approach would be to have your data source be a key/value pair (like a NSDictionary) with the Keys being the text that goes in the label and the value being the image that should be assigned to the UIImageView.
I have a UITableView filled with stuff from a database.
Now I want to change the background color of only one cell, the one that is set 'active item'.
How can I find the cell with (as example) name = "Active Cell" and change the background color of that cell only.
Hope you guys understand, don't know how to explain it.
If you know which is the active item as the table is loaded / reloaded you can use...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
// permanent cell styling in here.
}
// non-permanent cell styling in here.
// example
MYCustomDataObject *object = [myDataArray objectAtIndex:indexPath.row];
cell.textlabel.text = object.text;
BOOL isActive = [object.text isEqualToString:#"Active Cell"];
cell.backgroundColor = isActive ? [UIColor redColor]:[UIColor clearColor];
return cell;
}
If you need to access it at another time... say you've just set a new active item... you should call [myTableView reloadData]; when the active item changes also.
Example code below;
UITableViewCell *cell = [self.tblView cellForRowAtIndex:index];//desired cell index and this could be get whenever you have data
cell.backgroundColor = [UIColor blackColor];//desired color
I am using UITextField, and every row have a CellLable and TextField. Lable and TextField data comes from array. While running the app, all data are comes fine but when scrolling the Tableview, Last 2-3 Rows unorganized.
NSMutableArray
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
ArrFieldData= [NSMutableArray arrayWithObjects:#"Fist Name", #"Last Name",#"User Name", #"Password",#"Confirm Password", #"Gender",#"DOB", #"Profile Pic",#"Deparment", #"Joining Date",#"Education", #"Role", nil];
Now cellForRowAtIndexPath Function
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
UITextField *txtField ;
if (cell ==nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
txtField = [[UITextField alloc]initWithFrame:CGRectMake([UIScreen mainScreen].bounds.size.width/3 + 40, 2, [UIScreen mainScreen].bounds.size.width/2, cell.layer.frame.size.height -5)];
[self setUpCell:cell withIndexPath:indexPath withTextField:txtField];
}
[self UpdateCell:cell withIndexPath:indexPath withTextField:txtField];
return cell;
}
SetUp Cell Function
-(void)setUpCell:(UITableViewCell *)cell withIndexPath:(NSIndexPath *)indexPath withTextField: (UITextField *)txtField {
cell.textLabel.text = [ArrFieldData objectAtIndex:indexPath.row];
cell.textLabel.font = [UIFont systemFontOfSize:12.0];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
txtField.tag = indexPath.row+1;
txtField.layer.borderColor = [UIColor grayColor].CGColor;
txtField.delegate = self;
txtField.layer.borderWidth = 1.0f;
txtField.layer.cornerRadius = 4.0f;
txtField.placeholder = cell.textLabel.text;
txtField.font = [UIFont systemFontOfSize:12.0];
[cell.contentView addSubview: txtField];
}
Update Cell Function
-(void)UpdateCell:(UITableViewCell *)cell withIndexPath:(NSIndexPath *)indexPath withTextField: (UITextField *)txtField {
[self setUpCell:cell withIndexPath:indexPath withTextField:txtField];
}
First time Running the application, It is showing all cell and textfield data are Serialize. but when scroll some cell and TextField are not serialize as per Array Value. I am attaching the Simulator Screenshot.
First Screenshot for First time running, and second for when i scroll the Tableview. See the last 4-5 cell and Textfield Placeholder text. They are un-organized. I want it shouldn't change.
As you are using reusable cells, like
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
UITextField *txtField ;
if (cell ==nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
txtField = [[UITextField alloc]initWithFrame:CGRectMake([UIScreen mainScreen].bounds.size.width/3 + 40, 2, [UIScreen mainScreen].bounds.size.width/2, cell.layer.frame.size.height -5)];
[self setUpCell:cell withIndexPath:indexPath withTextField:txtField];
}
[self UpdateCell:cell withIndexPath:indexPath withTextField:txtField];
return cell;
}
UITableView uses the concept of reusable cell to achieve maximum performance by reducing the memory consumption, and to exploit this feature of reusing cells you can use the above UITableView's API's to achieve that.
But before using any feature it's very important to understand the working and the usage of any feature.
In your above implementation of tableView: cellForRowAtIndexPath: method, you have used the concept of cell reusability.
If the cells doesn't exist and are created for the first time, than they are allocated(every subview is created and added on the content view of the cell), customized and initialized with the data from the data source of the respective index path.
But in case the cells are reused(as they were already created for any other index path), there subviews exist with the data already filled for the previous index path for which it was created.
Now there are two things we can do to use already created cell for the current index path,
1) if the cells contain subview with data then remove the subviews and recreate the new ones, customize and populate them with the data.
2) rather than releasing the previous subviews and creating new ones, refill the data for the data model of the corresponding index path.
In your case, if the cell is being created for any index path, than the text filed for it is also created and if it's reused than the new text field is not created and it's being reused from the previously created cell thus the issue of the placeholder text not matching with the left text.
So, in order to solve your problem I think you should either create the textfield when the cell is created and if the cells are reused than refill the data in the text filed from the data source of the corresponding index path.
Your problem is due to a feature of UITableView. When you scroll a UITableView the indexPath is updated so you are not getting the index values you are expecting from the tableView.
Instead of adding a UITextField programmatically. Create a Custom UITableViewCell and from the method cellForRowAtIndexPath: update the placeHolder of your UITextField. The tableView will take care of scrolling for you.
Use this code :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
UITextField *txtField ;
if (cell ==nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
cell.textField.placeholder= cell.textLabel.text;
}
cell.textField.placeholder= cell.textLabel.text;
return cell;
}
I am an iOS newbie. I am using a checkmark to in a UITableView, and storing the checked value in a local database. When I load the app for the first time, I want to set the value of the checkmark depending on which value exists in the db. How would I do that? Presently this is what I do -
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
if ([indexPath compare:self.lastIndexPath] == NSOrderedSame)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
// Set up the cell...
NSString *cellValue = [[self countryNames] objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}
and then in didSelectRowAtIndexPath -
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// some stuff
self.lastIndexPath = indexPath;
[tableView reloadData];
}
Are you asking simply how and when to set the checkmark or are you asking how to populate a table from a database (eg Core Data)?
From your code, the only data you represent is in [self countryNames] and it's not clear under what circumstances you'd want the cell to display a checkmark. Whatever that is, just check the condition and set the checkmark when you are configuring your cell for data (after your "Set up the cell..." comment).
Example if you stored the users country and checked that cell:
// get the current country name
NSString *cellValue = [[self countryNames] objectAtIndex:indexPath.row];
// configure the cell
cell.textLLabel.text = cellValue;
UITableViewCellAccessoryType accessory = UITableViewCellAccessoryNone;
if ([cellValue isEqualToString:self.usersCountry]) {
accessory = UITableViewCellAccessoryCheckmark;
}
cell.accessoryType = accessory;
If you have static table data, then you simply need to store the section and row of the selected table view cell. If you have dynamic data, then you will need to store the unique data for that selected cell in the database and compare that with the cell's content on load. When you are loading your cells in cellForRowAtIndexPath:, simply set that cell's accessory to UITableViewCellAccessoryCheckmark as well as set self.lastIndexPath = indexPath for comparison later.
Also, I typically use [indexPath isEqual:self.lastIndexPath] instead of compare:. Doesn't really make a difference either way, just for readability.