¿Cómo puedo hacer que un UITextField suba cuando el teclado está presente al comenzar a editar?

Resuelto philfreo asked hace 54 años • 99 respuestas

Con el SDK de iOS:

Tengo un UIViewwith UITextFields que abre un teclado. Lo necesito para poder:

  1. Permitir el desplazamiento del contenido de UIScrollViewpara ver los otros campos de texto una vez que se abre el teclado

  2. "Saltar" automáticamente (desplazándose hacia arriba) o acortando

Sé que necesito un UIScrollView. Intenté cambiar la clase de my UIViewa UIScrollView, pero todavía no puedo desplazar los cuadros de texto hacia arriba o hacia abajo.

¿Necesito tanto a UIViewcomo a UIScrollView? ¿Uno va dentro del otro?

¿Qué se debe implementar para desplazarse automáticamente al campo de texto activo?

Idealmente, la mayor parte posible de la configuración de los componentes se realizará en Interface Builder. Me gustaría escribir código solo para lo que lo necesita.

Nota: el UIView(o UIScrollView) con el que estoy trabajando aparece mediante una barra de pestañas ( UITabBar), que debe funcionar normalmente.


Estoy agregando la barra de desplazamiento solo para cuando aparece el teclado. Aunque no es necesario, creo que proporciona una mejor interfaz porque el usuario puede desplazarse y cambiar cuadros de texto, por ejemplo.

Lo tengo funcionando donde cambio el tamaño del marco UIScrollViewcuando el teclado sube y baja. Simplemente estoy usando:

-(void)textFieldDidBeginEditing:(UITextField *)textField {
    //Keyboard becomes visible
    scrollView.frame = CGRectMake(scrollView.frame.origin.x,
                                  scrollView.frame.origin.y,
    scrollView.frame.size.width,
    scrollView.frame.size.height - 215 + 50);   // Resize
}

-(void)textFieldDidEndEditing:(UITextField *)textField {
    // Keyboard will hide
    scrollView.frame = CGRectMake(scrollView.frame.origin.x,
                                  scrollView.frame.origin.y,
                                  scrollView.frame.size.width,
                                  scrollView.frame.size.height + 215 - 50); // Resize
}

Sin embargo, esto no "sube" ni centra automáticamente los campos de texto inferiores en el área visible, que es lo que realmente me gustaría.

philfreo avatar Jan 01 '70 08:01 philfreo
Aceptado
  1. Sólo necesitarás un ScrollViewsi el contenido que tienes ahora no cabe en la pantalla del iPhone. (Si está agregando ScrollViewcomo supervisión de los componentes solo para hacer el TextFielddesplazamiento hacia arriba cuando aparece el teclado, entonces no es necesario).

  2. La forma estándar de evitar TextFieldque el teclado cubra los mensajes de correo electrónico es mover la vista hacia arriba o hacia abajo cada vez que se muestra el teclado.

Aquí hay un código de muestra:

#define kOFFSET_FOR_KEYBOARD 80.0

-(void)keyboardWillShow {
    // Animate the current view out of the way
    if (self.view.frame.origin.y >= 0)
    {
        [self setViewMovedUp:YES];
    }
    else if (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

-(void)keyboardWillHide {
    if (self.view.frame.origin.y >= 0)
    {
        [self setViewMovedUp:YES];
    }
    else if (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
    if ([sender isEqual:mailTf])
    {
        //move the main view, so that the keyboard does not hide it.
        if  (self.view.frame.origin.y >= 0)
        {
            [self setViewMovedUp:YES];
        }
    }
}

//method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.3]; // if you want to slide up the view

    CGRect rect = self.view.frame;
    if (movedUp)
    {
        // 1. move the view's origin up so that the text field that will be hidden come above the keyboard 
        // 2. increase the size of the view so that the area behind the keyboard is covered up.
        rect.origin.y -= kOFFSET_FOR_KEYBOARD;
        rect.size.height += kOFFSET_FOR_KEYBOARD;
    }
    else
    {
        // revert back to the normal state.
        rect.origin.y += kOFFSET_FOR_KEYBOARD;
        rect.size.height -= kOFFSET_FOR_KEYBOARD;
    }
    self.view.frame = rect;

    [UIView commitAnimations];
}


- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillShow)
                                             name:UIKeyboardWillShowNotification
                                           object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillHide)
                                             name:UIKeyboardWillHideNotification
                                           object:nil];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    // unregister for keyboard notifications while not visible.
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                             name:UIKeyboardWillShowNotification
                                           object:nil];

    [[NSNotificationCenter defaultCenter] removeObserver:self
                                             name:UIKeyboardWillHideNotification
                                           object:nil];
}
RPDP avatar Jul 14 '2009 18:07 RPDP

También estaba teniendo muchos problemas con la UIScrollViewcomposición de varios archivos UITextFields, de los cuales uno o más quedaban ocultos por el teclado cuando se estaban editando.

Aquí hay algunas cosas a considerar si UIScrollViewno se desplaza correctamente.

1) Asegúrese de que su tamaño de contenido sea mayor que el UIScrollViewtamaño del marco. La forma de entenderlo UIScrollViewses que UIScrollViewes como una ventana de visualización del contenido definido en contentSize. Entonces, para que se UIScrollviewdesplace a cualquier lugar, contentSize debe ser mayor que UIScrollView. De lo contrario, no es necesario desplazarse ya que todo lo definido en contentSize ya está visible. Por cierto, tamaño de contenido predeterminado = CGSizeZero.

2) Ahora que comprende que UIScrollViewen realidad es una ventana a su "contenido", la forma de asegurarse de que el teclado no oscurezca su UIScrollView's"ventana" de visualización sería cambiar el tamaño UIScrollViewde manera que cuando el teclado esté presente, tenga la UIScrollViewventana. dimensionado solo al UIScrollViewframe.size.height original menos la altura del teclado. Esto asegurará que su ventana sea solo esa pequeña área visible.

3) Aquí está el problema: cuando implementé esto por primera vez, pensé que tendría que obtener el CGRectcampo de texto editado y llamar al UIScrollView'smétodo scrollRecToVisible. Implementé el UITextFieldDelegatemétodo textFieldDidBeginEditingcon la llamada al scrollRecToVisiblemétodo. En realidad, esto funcionó con un efecto secundario extraño: el desplazamiento lo colocaríaUITextField en su posición. Durante mucho tiempo no pude entender qué era. Luego comenté el textFieldDidBeginEditingmétodo Delegado y ¡¡todo funciona!!(???). Al final resultó que, creo que en UIScrollViewrealidad implícitamente trae implícitamente lo editado actualmente UITextFielda la ventana visible. Mi implementación del UITextFieldDelegatemétodo y la posterior llamada al scrollRecToVisiblefue redundante y fue la causa del extraño efecto secundario.

Estos son los pasos para desplazarse correctamente hacia su UITextFieldlugar UIScrollViewcuando aparezca el teclado.

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.

- (void)viewDidLoad 
{
    [super viewDidLoad];

    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillShow:) 
                                                 name:UIKeyboardWillShowNotification 
                                               object:self.view.window];
    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillHide:) 
                                                 name:UIKeyboardWillHideNotification 
                                               object:self.view.window];
    keyboardIsShown = NO;
    //make contentSize bigger than your scrollSize (you will need to figure out for your own use case)
    CGSize scrollContentSize = CGSizeMake(320, 345);
    self.scrollView.contentSize = scrollContentSize;
}

- (void)keyboardWillHide:(NSNotification *)n
{
    NSDictionary* userInfo = [n userInfo];

    // get the size of the keyboard
    CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;


    // resize the scrollview
    CGRect viewFrame = self.scrollView.frame;
    // I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
    viewFrame.size.height += (keyboardSize.height - kTabBarHeight);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [self.scrollView setFrame:viewFrame];
    [UIView commitAnimations];

    keyboardIsShown = NO;
}

- (void)keyboardWillShow:(NSNotification *)n
{
    // This is an ivar I'm using to ensure that we do not do the frame size adjustment on the `UIScrollView` if the keyboard is already shown.  This can happen if the user, after fixing editing a `UITextField`, scrolls the resized `UIScrollView` to another `UITextField` and attempts to edit the next `UITextField`.  If we were to resize the `UIScrollView` again, it would be disastrous.  NOTE: The keyboard notification will fire even when the keyboard is already shown.
    if (keyboardIsShown) {
        return;
    }

    NSDictionary* userInfo = [n userInfo];

    // get the size of the keyboard
    CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;

    // resize the noteView
    CGRect viewFrame = self.scrollView.frame;
    // I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
    viewFrame.size.height -= (keyboardSize.height - kTabBarHeight);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [self.scrollView setFrame:viewFrame];
    [UIView commitAnimations];
    keyboardIsShown = YES;
}
  1. Regístrese para recibir notificaciones del teclado enviewDidLoad
  2. Cancelar el registro para recibir notificaciones del teclado enviewDidUnload
  3. Asegúrese de que contentSizeesté configurado y sea mayor que su UIScrollViewenviewDidLoad
  4. Reducir el UIScrollViewcuando el teclado está presente
  5. Revertir cuando UIScrollViewel teclado desaparezca.
  6. Utilice un ivar para detectar si el teclado ya se muestra en la pantalla, ya que las notificaciones del teclado se envían cada vez que se UITextFieldtabula, incluso si el teclado ya está presente para evitar reducirloUIScrollView cuando ya está reducido .

Una cosa a tener en cuenta es que se UIKeyboardWillShowNotificationactivará incluso cuando el teclado ya esté en la pantalla cuando presione otra tecla UITextField. Me encargué de esto usando un ivar para evitar cambiar el tamaño UIScrollViewcuando el teclado ya está en la pantalla. ¡Cambiar el tamaño sin querer UIScrollViewcuando el teclado ya está ahí sería desastroso!

Espero que este código les ahorre muchos dolores de cabeza a algunos de ustedes.

Shiun avatar Apr 24 '2010 08:04 Shiun

En realidad, es mejor utilizar la implementación de Apple, como se proporciona en los documentos . Sin embargo, el código que proporcionan es defectuoso. Reemplace la parte que se encuentra keyboardWasShown:justo debajo de los comentarios por la siguiente:

NSDictionary* info = [aNotification userInfo];
CGRect keyPadFrame=[[UIApplication sharedApplication].keyWindow convertRect:[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue] fromView:self.view];
CGSize kbSize =keyPadFrame.size;
CGRect activeRect=[self.view convertRect:activeField.frame fromView:activeField.superview];
CGRect aRect = self.view.bounds;
aRect.size.height -= (kbSize.height);

CGPoint origin =  activeRect.origin;
origin.y -= backScrollView.contentOffset.y;
if (!CGRectContainsPoint(aRect, origin)) {
    CGPoint scrollPoint = CGPointMake(0.0,CGRectGetMaxY(activeRect)-(aRect.size.height));
    [backScrollView setContentOffset:scrollPoint animated:YES];
}

Los problemas con el código de Apple son los siguientes: (1) Siempre calculan si el punto está dentro del marco de la vista, pero es un ScrollView, por lo que es posible que ya se haya desplazado y debes tener en cuenta ese desplazamiento:

origin.y -= scrollView.contentOffset.y

(2) Cambian el contentOffset por la altura del teclado, pero queremos lo contrario (queremos cambiar contentOffsetpor la altura que es visible en la pantalla, no lo que no lo es):

activeField.frame.origin.y-(aRect.size.height)
DK_ avatar Jan 29 '2011 14:01 DK_

In textFieldDidBeginEdittingy in textFieldDidEndEditingllaman a la función [self animateTextField:textField up:YES]así:

-(void)textFieldDidBeginEditing:(UITextField *)textField 
{ 
    [self animateTextField:textField up:YES]; 
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField:textField up:NO];
}

-(void)animateTextField:(UITextField*)textField up:(BOOL)up
{
    const int movementDistance = -130; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? movementDistance : -movementDistance); 

    [UIView beginAnimations: @"animateTextField" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}

Espero que este código te ayude.

veloz 5

func animateTextField(textField: UITextField, up: Bool) {
    
    let movementDistance: CGFloat = -130
    let movementDuration: Double = 0.3
    
    var movement:CGFloat = 0
    if up {
        movement = movementDistance
    } else {
        movement = -movementDistance
    }
    
    UIView.animate(withDuration: movementDuration, delay: 0, options: [.beginFromCurrentState]) {
        self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
    }
}

func textFieldDidBeginEditing(_ textField: UITextField) {
    animateTextField(textField: textField, up: true)
}

func textFieldDidEndEditing(_ textField: UITextField) {
    animateTextField(textField: textField, up: false)
}
Volodymyr Kulyk avatar Aug 02 '2011 06:08 Volodymyr Kulyk

Simplemente usando TextFields:

1a) Usando Interface Builder: Seleccionar todos los campos de texto => Editar => Incrustar en => ScrollView

1b) Incrustar manualmente TextFields en UIScrollView llamado scrollView

2) establecerUITextFieldDelegate

3) Configure cada textField.delegate = self;(o haga conexiones en Interface Builder)

4) Copiar/Pegar:

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    CGPoint scrollPoint = CGPointMake(0, textField.frame.origin.y);
    [scrollView setContentOffset:scrollPoint animated:YES];
}

- (void)textFieldDidEndEditing:(UITextField *)textField {
    [scrollView setContentOffset:CGPointZero animated:YES];
}
 avatar Dec 01 '2012 14:12