Ok, I'm really confused by this behavior and quite frankly I just don't get it. The short part is that I've got a tableView with a bunch of custom cells that contain UITextFields. I've added a toolbar (as the inputAccessoryView) that shows up above the keyboard to allow for quick movement up and down (previous and next, respectively) via a UISegmentedControl in that toolbar. This all works fine UNTIL in the action handler for the UISegmentedControl somehow gets a nil back from cellForRowAtIndexPath: which is a valid index path for the tableView. Why would this return nil???
Here's the code of the handler method and friends:
(Ok, I don't want to put everything here, so here's a summary of some of the methods not shown - these seem to work OK:
I set a breakpoint at the line in moveToPreviousTextField: where it resignsFirstResponder if the previous cell is nil, mostly because the program should never get here - there should be a cell. Given that, I did a little poking and here's the results:
So why does cellForRowAtIndexPath return nil for [1,0] and [0,0] - I just don't get it at all and need some new place to look for an answer. Please help!
Here's the code of the handler method and friends:
Code:
-(void)prevNextAction:(UISegmentedControl *)control
{
NSLog(@"[%@ %@] self.currentTextField=%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd),self.currentTextField);
if (control.selectedSegmentIndex == PREVIOUS_INDEX) {
[self moveToPreviousTextField:self.currentTextField];
} else {
[self moveToNextTextField:self.currentTextField];
}
}
-(void)moveToNextTextField:(UITextField *)textField
{
NSLog(@"[%@ %@]", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
if (!self.currentTextField) {
NSLog(@"currentTextField is nil?");
}
if (textField != self.currentTextField) {
NSLog(@"textField != currentTextField???");
abort();
}
NSIndexPath *myIndexPath = [self indexPathForTextField:textField];
NSIndexPath *nextTextFieldIndexPath = [self nextTextFieldIndexPathForIndexPath:myIndexPath];
if (nextTextFieldIndexPath) {
MCTextFieldCell *nextCell = (MCTextFieldCell *)[self.tableView cellForRowAtIndexPath:nextTextFieldIndexPath];
self.currentTextField = nextCell.textField;
[self.currentTextField becomeFirstResponder];
} else {
[textField resignFirstResponder];
self.currentTextField = nil;
}
}
-(void)moveToPreviousTextField:(UITextField *)textField
{
NSIndexPath *myIndexPath = [self indexPathForTextField:textField];
NSIndexPath *prevTextFieldIndexPath = [self previousTextFieldIndexPathForIndexPath:myIndexPath];
if (prevTextFieldIndexPath) {
MCTextFieldCell *prevCell = (MCTextFieldCell *)[self.tableView cellForRowAtIndexPath:prevTextFieldIndexPath];
if (prevCell) {
self.currentTextField = prevCell.textField;
[self.currentTextField becomeFirstResponder];
} else {
[textField resignFirstResponder];
self.currentTextField = nil;
}
} else {
[textField resignFirstResponder];
self.currentTextField = nil;
}
}
(Ok, I don't want to put everything here, so here's a summary of some of the methods not shown - these seem to work OK:
- indexPathForTextField: returns the indexPath for the cell that contains the current textField. It does this by finding the cell that holds the textField (which is textField.superview.superview) and asking the tableView for its indexPath via indexPathForCell:
- nextTextFieldIndexPathForIndexPath:indexPath indexes a dictionary that maps a given indexPath to the next index path
- previousTextFieldIndexPathForIndexPath:indexPath indexes a dictionary that maps a given indexPath to the previous indexPath)
I set a breakpoint at the line in moveToPreviousTextField: where it resignsFirstResponder if the previous cell is nil, mostly because the program should never get here - there should be a cell. Given that, I did a little poking and here's the results:
Code:
(lldb) po myIndexPath
(NSIndexPath *) $17 = 0x06b888e0 <NSIndexPath 0x6b888e0> 2 indexes [2, 0]
(lldb) po prevTextFieldIndexPath
(NSIndexPath *) $18 = 0x0a54dc40 <NSIndexPath 0xa54dc40> 2 indexes [1, 0]
(lldb) po _currentTextField
(UITextField *) $19 = 0x06b76030 <UITextField: 0x6b76030; frame = (83 12; 202 21); text = '0.00'; clipsToBounds = YES; opaque = NO; autoresize = W+H; layer = <CALayer: 0x6b764e0>>
(lldb) po [[self tableView] cellForRowAtIndexPath:myIndexPath]
(id) $20 = 0x06b763d0 <MCTextFieldCell: 0x6b763d0; baseClass = UITableViewCell; frame = (0 305; 320 47); autoresize = W; layer = <CALayer: 0x6b78fe0>>
(lldb) po [[self tableView] cellForRowAtIndexPath:prevTextFieldIndexPath]
(id) $21 = 0x00000000 <nil>
(lldb) po [[self tableView] cellForRowAtIndexPath:(NSIndexPath *)[NSIndexPath indexPathForRow:1 inSection:2]]
(id) $22 = 0x06b7ac80 <MCTextFieldCell: 0x6b7ac80; baseClass = UITableViewCell; frame = (0 352; 320 46); autoresize = W; layer = <CALayer: 0x6b7a5d0>>
(lldb) po [[self tableView] cellForRowAtIndexPath:(NSIndexPath *)[NSIndexPath indexPathForRow:0 inSection:2]]
(id) $23 = 0x06b763d0 <MCTextFieldCell: 0x6b763d0; baseClass = UITableViewCell; frame = (0 305; 320 47); autoresize = W; layer = <CALayer: 0x6b78fe0>>
(lldb) po [[self tableView] cellForRowAtIndexPath:(NSIndexPath *)[NSIndexPath indexPathForRow:0 inSection:1]]
(id) $24 = 0x00000000 <nil>
(lldb) po [[self tableView] cellForRowAtIndexPath:(NSIndexPath *)[NSIndexPath indexPathForRow:0 inSection:0]]
(id) $25 = 0x00000000 <nil>
So why does cellForRowAtIndexPath return nil for [1,0] and [0,0] - I just don't get it at all and need some new place to look for an answer. Please help!