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;
}
Related
As soon as the table view gets touched the cell titles (and on-tap actions) disappear. I only use standard table view cells and store the values in an array. After the values disappear the table stays scrollable. Any ideas?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
cell.textLabel.text = [[systeme objectAtIndex:indexPath.row] description];
cell.backgroundColor = [UIColor clearColor];
[cell.textLabel setTextColor:[UIColor whiteColor]];
[cell.textLabel setTextAlignment:NSTextAlignmentCenter];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[[NSNotificationCenter defaultCenter] postNotificationName:#"choseSystem" object:[systeme objectAtIndex:indexPath.row]];
}
You should be sure that the reuse identifier is the same for all cells if you use only one type of cells. You should do something similar to the following in the portion of your code where to retrieve a reusable cell:
NSString *CellIdentifier = [NSString stringWithFormat:#"CellReuseIdentifier", (long)indexPath.section];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
And make you you set the #"CellReuseIdentifier" in your xib file or your storyboard.
If you would like to use multiple custom cells for a table view you should do something similar to what you're doing, but take into account that reuse identifiers need to be configured for every type of cells.
Hope this helps!
The table view was fine. I just added its view as a subview to another view without keeping reference to the actual UITableViewController. That was the problem.
I have UITableViewController with a static table view and 2 sections. The first section has two cells, with a layout Table View Cell > Content View > Text Field.
The second section of table view cells has a layout that is Table View Cell > Content View > Label - and the cells expand when they're selected by using beginUpdates and endUpdates and reporting cell heights via tableView:heightForRowAtIndexPath:.
Right now, when I touch one of the first sections' cells, the keyboard comes up and the cursor is placed in the text field, but the cell is not selected - which means that if I had previously selected one of the cells in the second section, it would remain selected (and therefore expanded).
Also, if I have selected a cell in the first section with the text field given first responder, then I select a cell in the second section, the cell expands, but the first cell does not resign first responder and dismiss the keyboard.
Did I layout my table view incorrectly such that the cell selected states are not managed automatically and if not: what is the proper way to manage the selected states of the UITableViewCells?
I dont know how you have written the code. But from above information I have created one sample application. The code is as below and it is working as you want.
I have considered the table with 2 section.
First section has the textfield on Cell.
Second section has the UIlabel on the cell.
txtFieldCopy is of type UITextField declared in .h of class.
Please check the way I have assigned the Tag to the view as we need it to get the indexpath.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 2;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCellStyle style =UITableViewCellStyleSubtitle;
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
int iTag=[indexPath row]+1;
txtFieldCopy=nil;
if(indexPath.section==1)
{
UILabel *lblFirst=nil;
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:style reuseIdentifier:MyIdentifier];
lblFirst = [[UILabel alloc] initWithFrame:CGRectMake(45, 0, 100,
40)] ;
lblFirst.tag=iTag;
[cell.contentView addSubview:lblFirst];
lblFirst=nil;
}
lblFirst = (UILabel *)[cell.contentView viewWithTag:iTag];
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
lblFirst.text =#"Label 1";
}
else
{
UITextField *txtFirst=nil;
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:style reuseIdentifier:MyIdentifier];
txtFirst = [[UITextField alloc] initWithFrame:CGRectMake(40, 0, 50,
40)] ;
txtFirst.tag=iTag;
txtFirst.delegate=(id)self;
[cell.contentView addSubview:txtFirst];
txtFirst=nil;
}
txtFirst = (UITextField *)[cell.contentView viewWithTag:iTag];
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
txtFirst.text =#"text 1";
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(txtFieldCopy)
{
[txtFieldCopy resignFirstResponder];
txtFieldCopy=nil;
}
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
NSLog(#"%d",textField.tag);
NSIndexPath *iRowId =[NSIndexPath indexPathForRow:(textField.tag-1) inSection:0];
[tblSample selectRowAtIndexPath:iRowId animated:NO scrollPosition:UITableViewScrollPositionMiddle];
txtFieldCopy=textField;
}
Make sure if suppose in between you are reloading the table view then txtFieldCopy=nil; in the cellForRowAtIndexPath get called and set to null. so you can ignore this line. But before please check whether you are calling it again and again.
Let me know if it worked for you or not. I am sure it definitely work for you
I have added a UILabel as the content view of my tableview. The text in the UILabel is overlapping upon scrolling. Below is the code.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.chatTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Group"];
static NSString *CellIdentifier = #"Group";
UITableViewCell *cell = [self.chatTableView dequeueReusableCellWithIdentifier:CellIdentifier
forIndexPath:indexPath];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = self.latestTrimText;
UILabel *cellLabel = [[UILabel alloc]init];
cellLabel.text = self.dateOfLatestTrim;
cellLabel.frame = CGRectMake(15, 0, 150, 30);
[cell.contentView addSubview:cellLabel];
return cell;
}
I can fix this by changing to UITableViewCell *cell = [self.chatTableView dequeueReusableCellWithIdentifier:nil];
But then, the scrolling of the tableview won't be smooth. Is there another way to fix the issue?
The tableview cells are recycled and reused potentially an infinite amount of times. This is what the reuse identifier stands for.
Take care not to add any subview outside the if (cell==nil) block. Now you are just adding again and again a new label to the same cell instance. This is why your texts overlaps each other.
I would recommend you to read the documentation on how the cells are recycled and reused.
Your fix is not correct because by passing a nil reuse id, you are just preventing the recycling process to play. You create a new instance for each row of your tableview. This is why you have performance issues then.
I have a master detail app in ios, with SDK 7.0, Xcode 5, using ARC.
master has many items, detail has a table view. When I click on an item, the contents of tableview will change. This works well until I put a UITextField in each cell, because I want to edit the contents in the table.
The problem is: when I click on a new item, the old contents don't disappear,so the contents of a cell is a superposition of the new UITextField's text and the old UITextField's text.
The first normal tableview like this:
After I click on an new item, the tableview will like this:
The snippet of codes of master is:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
LHBPoetry *poetry = poetryArray[indexPath.row];
self.detailViewController.poetryId = poetry.poetryId;
}
I have tried a lot of things in the above method, for example, I make all instances of the detail view controller to be nil; table view's data array removeAllObejects; table view reloadData; It can't fix the problem.
The snippet of detail is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"detailCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
UITextField *textField = textField = [[UITextField alloc] initWithFrame:
CGRectMake(90, 12, 200, 25)];
textField.tag = indexPath.row;
textField.text =_sentenceArray[indexPath.row];
textField.clearsOnBeginEditing = NO;
textField.delegate = self;
textField.returnKeyType = UIReturnKeyDone;
[textField addTarget:self
action:#selector(textFieldDone:)
forControlEvents:UIControlEventEditingDidEnd];
[cell.contentView addSubview:textField];
textField.text = _sentenceArray[indexPath.row];
return cell;
}
I draw this tableview in Main.storyborad, It has a prototype cell with an identifier.
Any help will be appreciated.
k there is something i want to tell, wy because u are keep on adding the textfields for reused cells, there is not one textfield in the cell ..:) there are more then one text field's, because of that u are getting overlapped with one other, i think u are using default "master- detail" application, and modifying it..:)
oky for that u need to modify like below
in master controller
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if(cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
UITextField *textField = [[UITextField alloc]initWithFrame:CGRectMake(2, 3, 300, 30)];
[textField addTarget:self action:#selector(textFieldDone:) forControlEvents:UIControlEventEditingDidEnd]; //hear u are adding once initially
textField.tag = 100;
[cell addSubview:textField];
}
NSString *object = _objects[indexPath.row];//_objects is mutable array holding the sentences or names
UITextField *textField = (UITextField *)[cell viewWithTag:100];//after that u are reusing the textfields
textField.text = object;
textField.tag = indexPath.row;
return cell;
}
now you are creating the cell thats wy u dont want the prototype cell remove it from story board
in the above u removed the custom cell becz u are creating the cell in the code it self
now in the method
- (void) textFieldDone:(UITextField *)inTextFIeld
{
int index = inTextFIeld.tag;
[_objects replaceObjectAtIndex:index withObject:[inTextFIeld text]];
[self.masterTableVIew reloadData];//made connection to ur tableview
}
Let's say that I have
- (UITableViewCell*)tableView:(UITableView*) cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
static NSString *cellID = #"Cell Identifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
else
{
return cell;
}
UILabel * nameLabel = [[UILabel alloc] initWithFrame: CGRectMake( 0, 15, box.size.width, 19.0f)];
nameLabel.text = name;
[nameLabel setTextColor: [UIColor colorWithRed: 79.0f/255.0f green:79.0f/255.0f blue:79.0f/255.0f alpha:1.0f]];
[nameLabel setFont: [UIFont fontWithName: #"HelveticaNeue-Bold" size: 18.0f]];
[nameLabel setBackgroundColor: [UIColor clearColor]];
nameLabel.textAlignment = NSTextAlignmentCenter;
[cell addSubview: nameLabel];
}
What is that going to do?
If cell is not nil, and let's say you are at row 5, will it return the cell for row 5 with the exact text labels, etc?
Basically, my question is, if you have custom cells with labels, imageviews, etc. How do you use cellForRowAtIndexPath with dequeueReusableCellWithIdentifier?
You attempt to dequeue a cell. If the attempt failed (cell is nil), then you create a cell and configure it it's views (not the data inside the view). Afterwards, you populate the views with any data or settings that change cell-to-cell. Also, you should add any custom views to the cell's contentView, not the cell itself.
#define NAME_LABEL_TAG 1234
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellID = #"Cell Identifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UILabel * nameLabel = [[UILabel alloc] initWithFrame: CGRectMake( 0, 15, box.size.width, 19.0f)];
nameLabel.tag = NAME_LABEL_TAG;
[nameLabel setTextColor: [UIColor colorWithRed: 79.0f/255.0f green:79.0f/255.0f blue:79.0f/255.0f alpha:1.0f]];
[nameLabel setFont: [UIFont fontWithName: #"HelveticaNeue-Bold" size: 18.0f]];
[nameLabel setBackgroundColor: [UIColor clearColor]];
nameLabel.textAlignment = NSTextAlignmentCenter;
[cell.contentView addSubview: nameLabel];
}
// Populate views with data and retrieve data for "name" variable
UILabel *nameLabel = (UILabel *)[cell.contentView viewWithTag:NAME_LABEL_TAG];
nameLabel.text = name;
// Return fully configured and populated cell
return cell;
}
If you have a complex cell, it's often easier to create it in Interface Builder and subclass UITableViewCell so you can have custom properties that refer to your Labels, Buttons, etc.
Yes, dequeueing a cell that you have already added those labels to will still have them and their text just as you left it when you created that particular cell.
Create a UITableViewCell subclass, let's call it MyTableViewCell that has properties holding the labels/imageViews/etc that it will need. Once you have either dequeued or alloc init'ed one of your MyTableViewCell, you can then set the text/images/etc on these properties. Like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"identifier";
MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[MyTableViewCell alloc] initWithStyle:UITableViewStylePlain reuseIdentifier:CellIdentifier];
}
cell.nameLabel.text = name;
cell.imageView.image = anImage;
return cell;
}
One major issue with your method is the conditionals surrounding dequeueing and creating. In your method you only set up the cell the label when it is alloc init'ed (you instantly return a dequeued cell without formatting it). However, you want this set up to occur for both dequeued and manually instantiated cells. Notice how this happens in my method, the return statement is at the very bottom. This will ensure that both created and reused cells have the appropriate data.
EDIT: One important thing I left out, you will instantiate the properties of your cell in its initWithStyle: reuseIdentifier: method and add them as subviews to the cell. This is so when you go to set the text of the label (or whatever) in your cellForRowAtIndexPath method, it has already been created. Basically the cell manages creating its own views and the UITableView delegate only has to worry about filling those views with data.
UITableView at first ask you for number of expected cells. Then it's load through - tableView:cellForRowAtIndexPath: method cells which is will be displayed + some to smooth scrolling, more objects it doesn't create. Created objects (by user) stored in table view and you can access not used through dequeueReusableCellWithIdentifier: method. TableView ask user for modifying currently created cells when it's scrolling. If here is free object - take it from dequeueReusableCellWithIdentifier: another - create new one.
in order to not have to allocate every single table cell, a table only allocates what is needed. If only 8 cells fit on a page then a table view will only allocate 8 cells or 9 i don't remember if it has a padding. When you scroll a tableview and the cell goes off the page the cell is queued up to be re-used, instead of re-allocating a new cell the table view takes an existing one this process is called dequeue-ing. When you make/allocate your cells you give it an identifier, this identifier is used to retrieve a cell that is marked with that string.
You can either use this
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath
This is available after iOS 6
Or you can also register your class somewhere in ViewDidLoad and use ResuseIdentifier, so you don't have to write the ResuseIdentifier part in CellForRowAtIndexpath
- (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0)
Hope this helps you...