iOS6 y fuera de pantalla UITextView en UITableView

Tengo un UITableViewController que tiene un UITextField en cada celda. Cuando el usuario golpea en un campo, automáticamente establece el siguiente campo para ser el primero en responder y desplaza la tabla para mostrar ese campo, así:

- (BOOL) textFieldShouldReturn:(UITextField *)textField { UITextField *nextTextField=nil; NSInteger row; NSInteger section; if(textField == self.txtFieldA) { nextTextField = self.txtFieldB; row=1; section=0; } else if(textField == self.txtFieldB) { nextTextField = self.txtFieldC; row=2; section=0; } else { [textField resignFirstResponder]; } if(nextTextField) { NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; [nextTextField becomeFirstResponder]; [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES]; } } 

El problema se produce al seleccionar un campo de text (digamos txtFieldA), desplazar la tabla hacia abajo para que txtFieldB no esté visible, luego presionar enter. Funciona como se esperaba en iOS5 (txtFieldB se convierte en el nuevo primer respondedor). Sin embargo, en iOS6 no funciona. Se desplaza al punto correcto de la tabla, pero el txtFieldB no se convierte en el primer respondedor (y no estoy seguro de qué es; cuando presiono "Atrás" en mi controller de navigation, el keyboard permanece visible).

Cuando presiono "Return", txtFieldB's didBeginEditing no se dispara. Si retraso [nextTextField becomeFirstResponder] por un segundo más o less (hasta que la animation de desplazamiento finalice y haga visible nextTextField) funcionará como se esperaba, pero me parece hacky y less fácil hacerlo, así que preferiría entender qué está pasando mal que implementar algo como ese.

En mi solución a este problema, subclass UITableViewCell para hacer una celda de campo de text. En esa celda de campo de text, -setSelected:animated: el valor de -setSelected:animated:

 - (void)setSelected:(BOOL)selected animated:(BOOL)animated { [super setSelected:selected animated:animated]; if (selected) { [self.textField becomeFirstResponder]; } } 

De vuelta en mi controller de vista, cuando quiero avanzar al siguiente campo, simplemente selecciono la fila en la que está.

 - (IBAction)advanceToNextTextField { NSIndexPath *indexPath = [self nextIndexPathFromIndexPath:self.indexPathOfActiveField]; [self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone]; [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionNone animated:YES]; } 

Esto facilita el event handling la tecla de retorno.

 - (BOOL)textFieldShouldReturn:(UITextField *)textField { switch (textField.returnKeyType) { case UIReturnKeyNext: [self advanceToNextTextField]; break; case UIReturnKeyDone: [self resignFirstResponder]; break; default: break; } return YES; } 

Espero que ayude.

También tengo un proyecto existente que funcionó bien antes de iOS 6. Mi aplicación universal utiliza un UITableView para crear un formulario con los botones "siguiente" y "anterior" en una barra de herramientas de keyboard (similar a Safari). La tabla muestra instancias de una subclass UITableViewCell que contiene un campo de text. La funcionalidad siguiente / anterior también se envuelve, por lo que "siguiente" para el último campo devuelve el foco a la parte superior del formulario.

Desafortunadamente, la solución de Jeffrey Thomas no está funcionando para mí. Si una celda está fuera de la pantalla, el método de selección y desplazamiento en el método advanceToNextField funciona bien para la celda misma. En el método setSelected , la jerarquía de vista para el campo de text es correcta: UITextField -> UITableViewCellContentView -> UITableViewCell subclass -> UITableView .

Sin embargo, la llamada becomeFirstResponder todavía no puede activar el modo de edición del campo, dispara textFieldDidBeginEditing , etc. El keyboard sigue siendo visible pero no afecta a la celda ahora visible.

Por ahora, la única forma en que he podido sortear este problema es desplazarme primero sin animation :

 [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionNone animated:NO]; [self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone]; 

Al igual que la técnica de demora de Ned en la operación, esto garantiza que el campo de text esté visible cuando la aplicación le pida que responda. De lo contrario, parece que becomeFirstResponder se dispara durante la animation y antes de que el campo de text sea (completamente) visible. Al parecer, algunos cambios en iOS 6 son más estrictos en los posts de primera respuesta y visibilidad.

Lamentablemente, esto pierde la agradable animation fluida al moverse de un campo a otro. Suponiendo que esto no sea simplemente un error en iOS 6.0, planeo UITableView usar UITableView como un formulario en el futuro.

Imaginé una solución a este problema, con la ayuda de mi compañero de trabajo Charles (¡gracias!). También probé la solución de Jeffrey Thomas (y sus variaciones) sin suerte: el campo de text no se convierte en el primero en responder si su celda de tabla está fuera de la pantalla, como también lo describe Ogel. UITableViewDelegate methods UITableViewDelegate que me avisarían cuando la vista de tabla termine de animar y demorar llamar al método setSelected: del campo de text hasta entonces, sin suerte. Luego me señaló a

 - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView 

que también es disparada por la vista de tabla ( UITableViewDelegate ajusta a UIScrollViewDelegate ), cuando se realiza el desplazamiento en respuesta al método de vista de tabla

 - (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated 

Entonces, la key es calcular la ruta del índice de la fila que pretende seleccionar en su método siguiente / anterior (como en el método - (IBAction)advanceToNextTextField Jeffrey Thomas - (IBAction)advanceToNextTextField ), pero en lugar de seleccionarlo, solo desplácese a él y establezca un ivar para ello:

 [_table scrollToRowAtIndexPath:nextIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES]; _selectIndexPathOnAnimationEnd = nextIndexPath; 

Luego, en el método de delegado de vista de desplazamiento, select la fila en esa ruta de índice después de que la vista de tabla haya terminado de desplazarse a ella:

 - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView { if(_selectIndexPathOnAnimationEnd != nil) { [_table selectRowAtIndexPath:_selectIndexPathOnAnimationEnd animated:NO scrollPosition:UITableViewScrollPositionMiddle]; _selectIndexPathOnAnimationEnd = nil; } } 

En este punto, el campo de text puede convertirse en el primer respondedor.