I am creating custom control using UITableView. This control is just a table view with multiple row in one section. And let's called it MyCustomControl.
In every cell on the table view, I added a UITextField control.
----------------
| ------------ |
|| Text Field || -> CELL
| ------------ |
----------------
This cell is just default UITableViewCell, and I added the text field using [cell addSubview:textField];
I give access to every text field component on table view with method like this:
- (UITextField*) textFieldAtIndex:(NSInteger)index
{
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:index inSection:0];
UITableViewCell* cell = [self tableView:[self containerTableView] cellForRowAtIndexPath:indexPath];
UITextField* textField = [[cell subviews] lastObject];
return textField; // It should returning the reference of textField right??
}
Once upon a time, I use this MyCustomControl in a view controller somewhere in my project.
- (void) viewDidLoad
{
[super viewDidLoad];
CGPoint origin = CGPointMake(100.f, 100.f);
CGFloat width = 200.f;
// Create custom control with text field generated as many as given parameter
MyCustomControl* textGroup = [[MyCustomControl alloc] initWithOrigin:origin width:width textFieldCount:2];
// Here is the problem, I try to set current view controllers's text fields
// with the text field generated by MyCustomControl.
// Text field returned are new text field with same properties instead of reference.
self.txtUsername = [textGroup textFieldAtIndex:0]; // Text Field are copied!
self.txtPassword = [textGroup textFieldAtIndex:1]; // Not return a pointer!
[self.txtUsername setDelegate:self]; // Fail
[self.txtPassword setDelegate:self]; // Fail
[[self view] addSubview:textGroup];
[[self view] addSubview:[self txtUsername]]; // New text field appeared on view
}
I expected to have full control of text field in MyCustomControl accessed from method textFieldAtIndex: but instead of having reference to that text field I got a new copy of text field in my view controller. And I can't set the delegate for that text field neither all other stuffs like it's text.
How can I get the reference from text field in this table view cell?
Check my answer to this, they wanted a text field but the process is the same, much cleaner than some of the other solutions here:
How to get UITableView Label Text string - Custom Cell
Thats assuming you know the index path.
Make your custom cell class and IBOutlet of your textfield to it
Hide this textfield wherever you dont require in cellForRowAtIndexPath
Textfield has touchDown action method set it when you are creating new one (same as button has touchUpInside by default - you can see this options by selecting your control and click on connection inspector)
From Below approach you can get reference of your textfield
- (IBAction)actionTextField:(id)sender {
UIView *view = sender.superview;
while (view && ![view isKindOfClass:[UITableViewCell self]]) view = view.superview;
UITableViewCell *cell = (UITableViewCell *)view;
indexPathForDeletion = [self.tableViewListViewVC indexPathForCell:cell];
NSLog(#"cell is in section %d, row %d", indexPathForDeletion.section, indexPathForDeletion.row);
}
The cells of tableview in your custom control Class is not created at the time you create instance of your custom class, so it suggest that you are assigning the textfields before their creation that is the problem i guess.
To get a reference of textfield from each cell ,you can give tag value to each textfield at the time you creating cell and can access them by the tag value whenever you want in your current controller.
Related
How to get the Dynamic tableview cell data.
I have a table view like this. All the cells are creating dynamically.
I want to get all the text (flavor and %)data when submit button is pressed.
The problem was I creating text in dynamically. So I cannot individually identify the text box. How could get the data from dynamic text box?
Set tag to your uitextfield in your custom cell
For eg.
cell.lblFlavour.tag = indexPath.row;
cell.lblPercent.tag = indexPath.row;
Now, access your uitextfield on the basis of your indexPath
Use tag property to identify each UI component uniquely. Assign the same tag property where you are creating textFields and button to each row then after change tag value for next row and get them using code.
To set tag:
self.yourTextfield.tag = 100;
self.yourbutton.tag = 100;
// set action method to uibutton
[cell.yourButton addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
To get the UI components using tag property:
-(IBAction)buttonPressed:(id)sender{
// here get all ui component using tag
UIButton *button = (UIButton *)sender;
NSLog(#"%d", [button tag]);
}
or you can use method didSelectRowAtIndexPath to get values see code:
- (void) tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSLog(#"getting index path %#",indexPath);
NSLog(#"getting index path %#",cell.yourTextField.text);
}
You can set tag to your textfield and by that tag you can access them.
For example,
UITextField *textField = [[UITextField alloc]initWithFrame:CGRectMake(0, 0, 200, 30)];
textField.tag = 100; //set tag
UITextField *textField1 = (UITextField*)[self.view viewWithTag:100]; //retreive by tag
So, like this you can manage your dynamically made textfields.
Update :
then set two tag 100 and 101 respectively. Now you have to got cell first and from that cell you can got your textfields so on button's click first got all cell, then you can got textfield from that cell like :
UITextField *textField = (UITextField*)[cell viewWithTag:100];
So, you have to got cell first in your button's click.
The main idea of UITableView is store data into data source, not into cell. When you tap "+" button on the navigation bar, you should add new item into array and then call [tableView reloadData]. So a UITableView just represent data from array. And when submit button will pressed you just enumerate an array of data source and get all the text from each row.
When you tap text into text field inside the cell, you could use delegates or blocks to store data into array.
try below code :
When you click on submit button
for (int i=0; i < [[tableView visibleCells] count]; i++)
{
//yourArrayname is the number of rows in the UITableView /Or UITableView visible cells
NSIndexPath *indexPath = [NSIndexPath indexPathForRow: i inSection: 0];
CustomTableViewCell *cell = [tblExperience cellForRowAtIndexPath:indexPath];
for (UIView *view in cell.contentView.subviews){
if ([view isKindOfClass:[UITextField class]]){
UITextField* CurrentTextField = (UITextField *)view;
NSLog(#"val %d %#",i,CurrentTextField.text);
[finalArray addObject:CurrentTextField.text];
}
}
}
Assign dynamic tags to your textfields, implement textfield delegates and save the data in an array whenever textfield ends editing. Something like this
-(void)textFieldDidEndEditing:(UITextField *)textField {
[_array insertObject:textField.text atIndex:textField.tag];
}
I need to show editable fields similar like Employee.
EmpName EmpTitle EmpDOB EmpHobby
for suppose 20 employee. suppose all fields are editable.
I have created UIView and added all UITextField for EmpName ETC.
I am adding that UIView to cell.contentView
[cell.contentView addSubview:[self displayEmpView]];
This gives me perfect design, to show for 20 Employee repeated design.
Now my problem is how can i access UITextField Values. UITextField are on UIView which is added as content view. I need to save back change values.
Is this possible? or is there any better approach.
You need to implement delegate methods for UITextField which will respond to all text events. Make your class conform to UITextFieldDelegate
You can loop through all subviews in your cell.contentView and find the subviews which are of kind UITextField , and set yourTextfield.delegate=self;
To save the data from all the rows:
-(IBAction)save{
for(int i=0;i<rowCont;i++){
UITableViewCell *cell = [mytableview cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
for(UITextField *txtField in cell.contentView.subviews){
NSString *text = [txtField text];
//save the value of text
}
}
This could be the ugly way of doing it, but to make things more easier and cleaner, you could maintain a dictionary and append textField values to the dictionary each time you finish entering the values into textfield.
So the delegate method textFieldDidEndEditing would get called when you move onto another textField, so you could do
-(void) textFieldDidEndEditing: (UITextField * ) textField
{
//Store the textField.text in the dictionary here
}
SO at the end you will have a dictionary with all the 20 values, and you can simply save this dictionary on button click to wherevr you want.
You can do it by
[cell.contentView addSubview:[self displayEmpView]];
displayEmView.tg = indexPath.row+1000;
UITextFiele1.tag = indexPath.row+1001;
and so on...
when you need to use do this:
UIView * displayEmpView = [cell viewWithTag:indexPath.row+1000];
UITexField *txtFieldABC = [displayEmpView viewWithTag:indexPath.row+1001];
But i prefer not using it, let create you custom UITableViewCell and make an outLet for all of your UI components.
I've a grouped UITableView with several custom cells where I placed text fields. I have defined a property pointing to the text field that is currently active:
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
self.activeTextField = textField;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
self.activeTextField = nil;
}
I need to get the row and section of the cell containing such text field. I tried this:
UITableViewCell *parentCell = (UITableViewCell *)[self.activeTextField superview];
NSIndexPath* cellPath = [self.tableView indexPathForCell:parentCell];
NSInteger cellRow = [cellPath row];
But I always get a 0. It looks like parentCell is not an UITableViewCell actually (but it should be). How can I get those values?
Thanks
If you are adding the text view to cell's container view then you have to call self.activeTextField.superview.superview. If not, self.activeTextField.superview should work.
In order to find out if your text field is added to container view's cell:
If you add the text field from xib/storyboard to a custom cell then it is in contentView
If you do [cell addSubview:] then is in cell's view
If you do [cell.cotentView addSubView:] then is in container view.
Totally unsure if it works but you might try:
UITableViewCell *cell = [self.tableView cellForRowAtPoint:[self.activeTextField center]];
In my application I am creating UITextField within UITableViewCell so that user can fill some information. This is my code:
if (![cell.contentView viewWithTag:10])
{
UITextField *textField=[[UITextField alloc]initWithFrame:CGRectMake(0, 0, 200, 30)];
[textField setTag:10];
[textField setBorderStyle:UITextBorderStyleRoundedRect];
[textField setBackgroundColor:[UIColor redColor]];
[cell.contentView addSubview:textField];
}
I am doing this so that I create that UITextField only once for each cell so that the textFields don't overlap... but a problem happened for example when the user writes in the textfields:
row 0 -> 0
row 1 -> 1
row 2 -> 2
row 3 -> 3
row 4 -> 4
And so on, if you have number of rows more than 10 and you started to scroll I can notice that the cells exchange there indices randomly so i can get something like:
row 0 -> 3
row 1 -> 1
row 2 -> 2
row 3 -> 0
row 4 -> 4
in the UITextField.text
how to make something or a trick like fixed position for each cell?
This can look as a 'bad' decision, but I would strongly recommend you Not to use UITableView with UITextFields unless you have like 40 textfields, because of the following reasons:
It lags the way you described, because NSIndexPath is calculated on
the go and the text that user filled in the textfields would jump
around the table when you scroll.
You will have a mess when implementing logics to get out text from
the textfields.
You will have to implement a big piece of code to implement "scroll
table up and down" animation to let user navigate through textfields,
i.e in textFieldDidBeginEditing
If you still want to use tableview, i would recommend you to set some key to each of your UITextFields, and create a datasource (like NSDictionary) that binds this key to a certain text value that you can get from it any time.
Then you will probably have an ugly piece of code like this:
- (IBAction)textFieldValueChanged:(UITextField *)sender
{
MyTableCell *cell = (MyTableCell*)sender.superview.superview;
[self.formDataSource setObject:cell.formTextField.text forKey:cell.contentKey];
}
Then when you need you can enumerate through formDataSource allValues property.
Here's an abbreviated sample of how I do this in one of my view controllers:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell
= [tableView dequeueReusableCellWithIdentifier:AttributeCellIdentifier];
NSString *propertyName = #"...";
NSString *className = #"...";
UILabel *label = (UILabel*)[cell.contentView viewWithTag:1];
UITextField* textField = (UITextField*)[cell.contentView viewWithTag:2];
textField.text = [self stringForAttribute:propertyName];
label.text = propertyName;
return cell;
}
tableView:cellForRowAtIndexPath: is part of the UITableViewDataSource protocol. It gets called whenever the table view needs to scroll new cells into view. Since it is recycling cells that have scrolled out of view automatically, you need to reset the state of the cell in this method based on the values in your model.
You may also find it useful to know you can configure your custom cell in Interface Builder, in your Storyboard. Just be sure to set an appropriate reuse identifier so that your controller will always be able to supply the right type of cell.
This question already has answers here:
first responder is not being set correctly
(2 answers)
Closed 9 years ago.
After a UITextField is edited and return is pressed, I loop through and check for the next field. I get the index path of that field, then save it. Next I reload table data and when the table is being built, if the indexPath is equal to the indexPath of what would be the next text field, then I set it as first responder.
I grab the correct textField at the correct indexPath and everything, however first responder doesn't get set. Instead, focus just disappears.
My code:
//inside UITextFieldShouldReturn
for (int i = uiTextField.fieldTag + 1; i < group.fields.count; i++) {
ObjectEditField *field = [group.fields objectAtIndex:i];
if (field.propName && field.updateObjectOnEdit == YES && [field isKindOfClass:[ObjectEditTextField class]]) {
// set the active field
activeField = field;
// obtain a pointer to the textfield object and set it to be the first responder
ObjectEditTextField *textField = (ObjectEditTextField *)field;
if (![textField.field isFirstResponder]) {
UITableViewCell *cell = (UITableViewCell *)[[[uiTextField superview] superview] superview];
firstResponderIndexPath = [_fieldsTableView indexPathForCell:cell];
Then once I have the firstResponderIndexPath I call reloadData on the table view, and inside tableView:cellForRowAtIndexPath: I have:
if (firstResponderIndexPath.row + 1 == indexPath.row)
{
ObjectEditTextField *textField = (ObjectEditTextField *)field;
NSLog(#"display: %#", textField.field.text);
[textField.field becomeFirstResponder];
}
The output for the NSLog is correct and the field it should be. However the first responder doesn't get set correctly.
You can only set the first responder if the view is part of the view hierarchy of the window. "tableView:cellForRowAtIndexPath:" gets called before it is added. So you'll have to set the first responder after the cells have been added, e.g. after you call reloadData in your UITableViewController subclass:
[self.tableView reloadData];
MyTableCell *cell = (id)[self.tableView cellForRowAtIndexPath: firstResponderIndexPath];
[cell.myTextFieldProperty becomeFirstResponder];
Has the cell been added to the window at the point where you are calling becomeFirstResponder? If it is not, nothing will happen. From the docs:
becomeFirstResponder
You may call this method to make a responder object such as a view the first responder. However, you should only call it on that view if it is part of a view hierarchy. If the view’s window property holds a UIWindow object, it has been installed in a view hierarchy; if it returns nil, the view is detached from any hierarchy.
Try calling it in tableView:willDisplayCell:forRowAtIndexPath: instead. You could also set a boolean property on the desired cell and have it call becomeFirstResponder on its field in didMoveToSuperview.