I am working on an iOS application where a button is enabled only after all UITextFields that are filled in (i.e. have at least one character inside each one). Each UITextField is in a custom UITableViewCell inside of a UITableView. Once the user has entered a value in each UITextField, I need the button to be enabled. Inside my viewDidLoad method, I disable the button as follows:
[_doneButton setEnabled:NO];
and then I have the following method:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
for (int i = 0; i < [self.table numberOfSections]; i++) {
NSInteger rows = [self.table numberOfRowsInSection:i];
for (int row = 0; row < rows; row++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:i];
SimpleTableCell *cell = (SimpleTableCell *)[self.table cellForRowAtIndexPath:indexPath];
for (UIView *subView in cell.subviews) {
if ([subView isKindOfClass:[UITextField class]]) {
//Does not come to this point
UITextField *txtField = (UITextField *)subView;
if([[txtField text] length] > 0) {
//Enable button and bold title
[_doneButton setEnabled:YES];
[_doneButton.titleLabel setFont:[UIFont boldSystemFontOfSize:28]];
}
else {
[_doneButton setEnabled:NO];
}
}
}
}
}
return YES;
}
Unfortunately, my code does not get into the "if" clause where I check to see if the custom cell has a subView of type UITextField, and because of that, the button is never enabled. For the record, "SimpleTableCell" is the name of my custom UITableViewCell class that I've created. My code appears to be correct, but I can't figure out why it is not functioning correctly. Can anyone see what it is I'm doing wrong?
I think I would go about this in a completely different way. I would create a mutable dictionary to hold the strings in the various text fields. Give the text fields tags, and use the tags (converted to NSNumbers) as the keys in the dictionary. Implement textFieldDidEndEditing:, and in that method get the string, check that it's not an empty string, and if not, do setObject:forKey: on the dictionary. After you set the value, check the count of the dictionary's allKeys property, and if it equals the number of text fields you have, enable the button.
That's because the only logical subview of the cell is an UIView containing all the items of the cell. It's property is "contentView".
The content view of a UITableViewCell object is the default superview for content displayed by the cell. If you want to customize cells by simply adding additional views, you should add them to the content view so they will be positioned appropriately as the cell transitions into and out of editing mode.
More information about it here: https://developer.apple.com/library/ios/documentation/uikit/reference/UITableViewCell_Class/Reference/Reference.html#//apple_ref/occ/instp/UITableViewCell/contentView
Have you set the UITextField's delegate? If that method is not getting called at all, that is the problem.
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 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.
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]];
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.