Go to next UITextField in different UITableViewCells - ios

I have a UITableView with x number of cells. In some cells there is x number UITextFields. I want the next-button on the keyboard to highlight the next textField. I got it working in the same cell but not when it's supposed to jump to a textField in a different cell. The first textField in each cell has tag 0 and the second tag 1 and so on. This is my code:
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
// Fetch current cell
UITableViewCell *cell = (UITableViewCell *)textField.superview.superview;
// Tag for next textField
NSInteger nextTag = textField.tag + 1;
UITextField *nextTextField;
for(UITextField *subview in cell.contentView.subviews){
if([subview isKindOfClass:[UITextField class]]){
if(subview.tag == nextTag){
nextTextField = subview;
break;
}
}
}
if(nextTextField){
// Go to next textField
[nextTextField becomeFirstResponder];
} else{
// IndexPath for current cellen
NSIndexPath *indexPath = [travellersTableView indexPathForCell:cell];
UITableViewCell *nextCell;
// While a cell exist at indexPath
while([travellersTableView numberOfSections] > indexPath.section+1){
nextCell = [self tableView:travellersTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section+1]];
if(nextCell){
for(UITextField *subview in nextCell.contentView.subviews){
if([subview isKindOfClass:[UITextField class]]){
if(subview.tag == 0){
NSLog(#"%#", subview.placeholder);
nextTextField = subview;
break;
}
}
}
// Go to next textField or cell
if(nextTextField){
[nextTextField becomeFirstResponder]; NSLog(#"Next!");
break;
} else{
indexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section+1];
}
}
}
if(!nextTextField){ [textField resignFirstResponder]; }
}
return NO;
}
The NSLog's behave as expected. I have different placeholders in the textFields and the next one is echoed. Next! is also echoed so it's trying to becomeFirstResponder.

I found the issue. I didn't fetch nextCell from my tableView correctly. This is how it should look like:
nextCell = [travellersTableView cellForRowAtIndexPath:indexPath];

Always start tags from at least 1. Tags equal to zero causes serious problems. An object with tag 0 is often not initialized. Start your tags from 1 and check again.

Related

UITableViewController Static and Grouped scrollToRowAtIndexPath not scrolling to correct cell

I have a UITableViewController in which I am using Static Cells that are grouped. Each cell has a UITextField for users to enter info, and at the bottom is a submit button. Upon tapping the button, I verify that they have entered info for each of the fields and if not, I scroll up to that cell/field so that they can complete it.
However, for some reason it is not scrolling to the correct cell when using scrollToRowAtIndexPath:
#pragma mark - Save
- (IBAction)didTapSaveGuestDetails:(ZSSBottomOverlayButton *)sender {
[self checkForFormSubmissionErrors];
}
- (void)checkForFormSubmissionErrors {
// First Name
if (self.nameFirst.text.length == 0) {
[self showAlertWithMessage:NSLocalizedString(#"* Please enter first name", nil) fieldToHighlight:self.nameFirst];
return;
}
[self.view endEditing:YES];
[self submitUpdatedGuestInfo];
}
- (void)submitUpdatedGuestInfo {
}
#pragma mark - Alert
- (void)showAlertWithMessage:(NSString *)message fieldToHighlight:(UIView *)field {
UIColor *redColor = [OTAColors colorWithRed:234 green:85 blue:58];
[RKDropdownAlert title:message backgroundColor:redColor textColor:[UIColor whiteColor] time:2.0];
if (field) {
[self highlightField:field];
}
}
- (void)highlightField:(UIView *)field {
[self.tableView endEditing:YES];
// Color field
if ([field isKindOfClass:[ZSSLargeTextField class]]) {
ZSSLargeTextField *textField = (ZSSLargeTextField *)field;
[textField showWarning];
}
CGPoint pointInTable = [field convertPoint:field.bounds.origin toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:pointInTable];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
This exact implementation works great when using prototype cells, but NOT static.
However, if I use the following it does scroll to the correct cell:
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
So, it seems the issue could be in my convertPoint:toView: method?
Any idea what could be preventing it from scrolling to the correct cell?

When textFieldDidEndEditing gets called, the app freezes

I have a custom UITableView with UITextFields inside. In cellForRow... I made the textFields delegate to self. (In my main VC class.) The way I get the text from the textField is at textFieldDidEndEditing, and I add it to a mutableArray.
I then have different cell ids that get added when a button gets selected:
- (IBAction)addRow:(id)sender
{
NSInteger row = [self.rowArray cound];
[self.rowArray insertObject:#"anotherCell" atIndex:row];
NSIndexPath *indexPath = [NSindexPath indexPathForRow:row inSection:0];
[self.myTableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
(There is a textField in that cellID and I set the delegate to self.)
In textFieldDidEndEditing, I made an NSLog of textField.text, and when that method gets called from a textField that was there initially, it works as expected.
But when textFieldDidEndEditing gets called from the textField that's in the cell of anotherCell (the added cell), then the whole simulator freezes.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellID = [self.rowArray objectAtIndex:[indexPath row]];
customCell *cell = [tableView dequeuereusablecellwithidentifier:cellID forIndexPath:indexPath];
cell.name.delegate = self; // From cell that is initially there
cell.phoneNumber.delegate = self; // From the added cell
return cell;
}
(If this is confusing, or if you need more code, just let me know in the comments. Thanks)
Edit
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if (textField.tag <= 9)
{
NSLog(#"%#", textField.text); // This works
}
UIView *superview = textField.superview;
while (![superview isMemberOfClass:[UITableViewCell class]]) {
superview = superview.superview;
}
CustomCellClass *cell = (CustomCellClass *)superview;
NSIndexPath *indexPath = [self.myTableView indexPathForCell:cell];
if (textField.tag >= 12)
{
if ([self.inputArray count] > indexPath.row) // So I won't get the error message of [__NSArrayM objectAtIndex:]: index 1 beyond bounds for empty array'
{
for (NSUInteger i = [self.inputArray count]; i < indexPath.row; i++) {
[self.inputArray insertObject:#"" atIndex:i];
NSLog(#"%lu", (unsigned long)i);
}
}
NSLog(#"%#", self.inputArray);
}
}
Your code is stuck in an infinite loop here:
while (![superview isMemberOfClass:[UITableViewCell class]]) {
superview = superview.superview;
}
because isMemberOfClass will return true only if the superview class is UITableViewCell, but NOT if it is a subclass of UITableViewCell. If you change isMemberOfClass to isKindOfClass, it should work. Check the Apple docs here.

UItextField becomeFirstResponder returning No

I have a UITableView of custom UITableViewCells each with two UITextFields in them. When I select a UITextField in the opening view i.e. without scrolling select a UITextField in the opening view. It becomesFirstResponder and I can enter text, as I hit enter all UITextFields in the opening view can becomeFirstResponder but when I get to the end of the UITableViewCells of that opening view and try to load the next UITableViewCell that is currently out of view and set its UITextField to becomeFirstResponder it doesn't work.
I have set up a if statment inside textFieldShouldReturn and it confirms that the textField is returning NO to becomeFirstResponder.
I would like to know how to fix this; I have looked for solutions but have yet to find anything that fits my situation. Below are the UITextFieldDelegates that I am using as I think in here I am supposed to do something like call or load the next UITableViewCell, but I do not know how.
#pragma mark - Textfield Delegates
-(BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
[textField resignFirstResponder];
if ([textField isEqual:cell.widthTexField]) {
nWidth = [[sortedItemsArray objectAtIndex:textField.tag] valueForKey:#"w_MM"];
} else if ([textField isEqual:cell.heightTextField]) {
nHeight = [[sortedItemsArray objectAtIndex:textField.tag] valueForKey:#"h_MM"];
}
return YES;
}
-(BOOL)textFieldShouldBeginEditing:(UITextField*)textfield {
int height = self.finishingTableView.frame.size.height;
NSLog(#"%i", height);
self.finishingTableView.frame= CGRectMake(self.finishingTableView.frame.origin.x, self.finishingTableView.frame.origin.y, self.finishingTableView.frame.size.width, 307);
// select correct row
if (textfield.tag > 999999) {
int adjustTag = textfield.tag-1000000; // remove a million so that you have the text fields correct position in the table. (this is only for height textfields)
NSIndexPath *indexPath =[NSIndexPath indexPathForRow:adjustTag inSection:0];
[finishingTableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[self tableView:finishingTableView didSelectRowAtIndexPath:indexPath];
[self.finishingTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionNone animated:YES];
return YES;
} else {
NSIndexPath *indexPath =[NSIndexPath indexPathForRow:textfield.tag inSection:0];
[finishingTableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[self tableView:finishingTableView didSelectRowAtIndexPath:indexPath];
[self.finishingTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionNone animated:YES];
return YES;
}
return YES;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
NSLog(#"%i", textField.tag+1);
[[self.view viewWithTag:textField.tag+1] becomeFirstResponder];
BOOL success = [[self.view viewWithTag:textField.tag+1] becomeFirstResponder];
if (success) {
NSLog(#"HypnosisView became the first responder");
} else {
NSLog(#"Could not become first responder");
}
// this means there has been a change in the UItextfield
NSLog(#"%#", selectedItemDictionary);
if (textField.tag < 999999) {
tempFinishingObjectDictionary = [selectedItemDictionary mutableCopy];
if (![textField.text isEqualToString:[selectedItemDictionary objectForKey:#"w_MM"]]) {
tempUpdatedRow = #"T";
// remove kevalues
[tempFinishingObjectDictionary removeObjectForKey:#"updatedRow"];
[tempFinishingObjectDictionary removeObjectForKey:#"new_w_MM"];
// update keyvalues
[tempFinishingObjectDictionary setValue:tempUpdatedRow forKey:#"updatedRow"];
[tempFinishingObjectDictionary setValue:textField.text forKey:#"new_w_MM"];
}
} else {
if (![textField.text isEqualToString:[selectedItemDictionary objectForKey:#"h_MM"]]) {
tempUpdatedRow = #"T";
// remove kevalues
[tempFinishingObjectDictionary removeObjectForKey:#"updatedRow"];
[tempFinishingObjectDictionary removeObjectForKey:#"new_h_MM"];
// update keyvalues
[tempFinishingObjectDictionary setValue:tempUpdatedRow forKey:#"updatedRow"];
[tempFinishingObjectDictionary setValue:textField.text forKey:#"new_h_MM"];
}
}
NSLog(#"%#", tempFinishingObjectDictionary);
[coreDataController editSelectedFinishing:htmlProjID UpdatedNSD:tempFinishingObjectDictionary SelectedRow:selectedItemIndexPathRow];
[SVProgressHUD dismiss];
NSLog(#"%#", selectedItemDictionary);
return YES;
}
In your textFieldShouldReturn when you go to the next cell you should check if two cells out is off the screen (and therefore not existent). If it is iff the screen you should scroll the table by one cell
[tableview setContentOffset:CGPointMake(tableview.contentOffset.x,tableview.contentOffset.y + cellHeight) animated:YES];
You will have to adjust this scrolling to your liking as I don't know how your application flows.

Skip to next UItextField

I have some UITextfields in a UItableView. the textfields have been subclassed in a UITableViewCell that I have created. I would lke to know how to progress down to the next cell in its respective row after the return key is pressed on the keyboard.
So if the user was to start on width, they would continue down the width colum, or if they wanted to enter height each time they hit enter they would continue down that column.
currently whats happening is if i use the width colum, it works fine but if i select a height UItextField after hitting enter it jumps over to the next width cell.
This is my code for detecting the return key.
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[[self.view viewWithTag:textField.tag+1] becomeFirstResponder];
return YES;
}
and this is what my tableview of cells looks like.
For more information this is how I add the custom celltextfields to UItextfield.
NSString *widthString = [currentFinishingDictionary objectForKey:#"w"];
if ((NSNull *) widthString != [NSNull null]) {
cell.widthTexField.text = widthString;
cell.widthTexField.delegate = self;
cell.widthTexField.tag = indexPath.row;
} else {
cell.widthTexField.text = #" ";
cell.widthTexField.delegate = self;
cell.widthTexField.tag = indexPath.row;
}
NSString *heightString = [currentFinishingDictionary objectForKey:#"h"];
if ((NSNull *) heightString != [NSNull null]) {
cell.heightTextField.text = heightString;
cell.heightTextField.delegate = self;
cell.heightTextField.tag = indexPath.row;
} else {
cell.heightTextField.text = #" ";
cell.heightTextField.delegate = self;
cell.heightTextField.tag = indexPath.row;
}
I think the issue might be in the way the tags are assigned to the UITextFields.
Please check if the tag values are actually what you have assumed.
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
[yourtextfield resignFirstResponder];
// save state, etc if necessary
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
MyCell * cell = (MyCell *)[tableView cellForRowAtIndexPath:indexPath];
self.selectedField = cell.tf; // cell.tf is a UITextField property defined on MyCell
[yourtextfield becomeFirstResponder];
}
Make sure that you have set tag to UITextField
You should try in this way:
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
//[[self.view viewWithTag:textField.tag+1] becomeFirstResponder];
// Get here you cell seleted Refrecne
id obj = [cell.contentView viewWithTag:textField.tag+1];
if([obj isKindOfClass:[UITextField class]]) {
UITextField *nextField = (UITextField*)obj
[nextField becomeFirstResponder];
}
return YES;
}
Or you can use ELCTextFieldCell
It contents UITextField and UILabel in one cell. You can customizr as per your need.
you can use
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
UITableViewCell *cell = [self.myTableview cellForRowAtIndexPath:[NSIndexPath indexPathForRow: textField.tag+1 inSection:0]];
UITextField *tf = (UITextField*)[cell viewByTag:textField.tag+1]
[tf becomeFirstResponder];
return YES;
}
Did you try using collectionView instead of tableview, collection views design can be made similar to the image you added and you can add one textfield in one cell and then make first responder for width text field by doing this
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
if ( [tableView.dataSource tableView:tableView numberOfRowsInSection:0] > tag + 2)
CustomCell *cell = [self.myTableview cellForRowAtIndexPath:[NSIndexPath indexPathForRow: textField.tag+2 inSection:0]];
[cell.textField becomeFirstResponder];
}
If you wanna still persist with this design then you can do this
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
int xPosition = (int)textField.frame.origin.x;
CustomCell *cell = [self.myTableview cellForRowAtIndexPath:[NSIndexPath indexPathForRow: textField.tag+1 inSection:0]];
if((int)cell.textField1.x == xPosition)
{
[cell.textField1.x becomeFirstResponder];
}
else
{
[cell.textField2.x becomeFirstResponder];
}
}
Code is not compiled. Only logic is written.Hope it helps :)
Change your assigning tag values as below that may help you.
NSString *widthString = [currentFinishingDictionary objectForKey:#"w"];
if ((NSNull *) widthString != [NSNull null]) {
cell.widthTexField.text = widthString;
cell.widthTexField.delegate = self;
cell.widthTexField.tag = indexPath.row;
} else {
cell.widthTexField.text = #" ";
cell.widthTexField.delegate = self;
cell.widthTexField.tag = indexPath.row;
}
NSString *heightString = [currentFinishingDictionary objectForKey:#"h"];
if ((NSNull *) heightString != [NSNull null]) {
cell.heightTextField.text = heightString;
cell.heightTextField.delegate = self;
cell.heightTextField.tag = indexPath.row + 2000;
} else {
cell.heightTextField.text = #" ";
cell.heightTextField.delegate = self;
cell.heightTextField.tag = indexPath.row + 2000;
}
Now your textfields tag looks like as below and may work as you desired.
0 2000
1 2001
2 2002
3 2003
4 2004
.. ........
Now your method work properly but need to think other idea as well because this will wrong when you have 2000 cells.
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
// i.e. textField.tag == 6 then next 6 + 1 == 7
// i.e. textField.tag == 20006 then next 2006 + 1 == 2007
[[self.view viewWithTag:textField.tag+1] becomeFirstResponder];
return YES;
}

UITextField doesn't get cursor when UITableView scrolles to not visible cell

I have a custom grouped TableView with 5 sections and in total with 50 custom cells of type:
Label | XXX
where XXX can be UITextField, UITextView, UIButton, UILabel. Cells have mixed order in tableView.
To navigate through textFields & textViews i added Next/Prev buttons on my keyboard.
The problem is the when i select txtField and click Next or Prev button, the table scrolls to the needed cell position, but txtField doesn't get the cursor. In order to get the cell with textField/textView i use:
nextCell = [self tableView:_myTableView
cellForRowAtIndexPath:[NSIndexPath indexPathForRow:curRow inSection:curSec]];
I need to search through all cells in tableView, not only through the visible ones.
As i tested, the problem is in this line of code, because if i call:
nextCell = [_myTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:curRow inSection:curSec]];
and the cell is visible, then the txtField gets cursor. However, if i need to jump from cell (section = 1, row = 6) to cell (section =0, row = 3) which is not visible, i get nil.
My full code of button function:
-(void) previousTextField:(id)sender
{
int curTagInd = _editingTextElement.tag;
NSIndexPath *curInd = [self getRowIndexOf:_editingTextElement];
int curSec = curInd.section;
int curRow = curInd.row;
id nextView = nil;
UITableViewCell *nextCell = nil;
do {
curRow--;
if (curRow >=0) {
nextCell = [self tableView:_myTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:curRow inSection:curSec]];//[_myTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:--curRow inSection:curInd.section]];
curTagInd--;
if ([nextCell isKindOfClass:[CustomCell class]] || [nextCell isKindOfClass:[CustomLongTextCell class]]) {
do {
nextView = [nextCell viewWithTag:curTagInd];
if ([nextView isEnabled]) {
nextCell = nil;
_editingIndexPath = [NSIndexPath indexPathForRow:curRow inSection:curSec];
break;
}else{
break;
}
} while (nextView != nil && ((![nextView isKindOfClass:[UITextField class]] && ![nextView isKindOfClass:[UITextView class]]) || ![nextView isEnabled]));
}
}
else if(curSec > 0){ //got to previous section
curSec--;
curRow = [self getNumberOfRowsInSection:curSec];
curTagInd = [self getNumberOfRowsInSection:curSec]+1;
}
else{
nextCell = nil;
}
} while (nextCell != nil);
if (nextView != nil) {
[self.myTableView scrollToRowAtIndexPath:_editingIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
UITextField *textField = (UITextField *)nextView;
[textField becomeFirstResponder];
}
}
Using this code i get inside method textFieldShouldBeginEditing:, but not in textFieldDidBeginEditing:
SOLUTION:
The problem was that the table began scrolling only after the function previousTextField: was finished, and thus, [textField becomeFirstResponder]; was called before the table was scrolled to the needed position. Due to the fact that needed cell is still not visible on the screen, delegate methods didn't call.
To avoid this, i added timer after table-scrolling line:
NSTimer *t = [NSTimer scheduledTimerWithTimeInterval: 0.2
target: self
selector:#selector(doneScrolling)
userInfo: nil repeats:NO];
and defined doneScrolling like:
-(void)doneScrolling
{
NSLog(#"done scrolling");
[self tableView:self.myTableView didSelectRowAtIndexPath:self.editingIndexPath];
}
where:
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:NO];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UIView *nextView = [cell viewWithTag:indexPath.row+1];
if ([nextView isKindOfClass:[UITextView class]] || [nextView isKindOfClass:[UITextField class]]) {
if ([nextView isEnabled]) {
UITextField *textField = (UITextField *)nextView;
[textField becomeFirstResponder];
}
}
}

Resources