Cubiertas para teclados iPhone UITextField


120

Tengo una aplicación donde, en Interface Builder , configuré una UIViewque tiene un campo de texto cerca de la parte inferior de la vista. Cuando ejecuto la aplicación e intento ingresar texto en ese campo, el teclado se desliza hacia arriba del campo para que no pueda ver lo que estoy escribiendo hasta que vuelva a ocultar el teclado.

¿Alguien más se ha encontrado con este problema y ha encontrado una buena manera de resolverlo sin hacer que la vista principal se pueda desplazar o mover el campo de texto más arriba en la pantalla?


Respuestas:


290

La solución habitual es deslizar el campo (y todo lo que está sobre él) hacia arriba con una animación, y luego hacia abajo cuando haya terminado. Es posible que deba colocar el campo de texto y algunos de los otros elementos en otra vista y deslizar la vista como una unidad. (Yo llamo a estas cosas "placas" como en "placas tectónicas", pero solo soy yo). Pero aquí está la idea general si no necesitas ponerte elegante.

- (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 = 80; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

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

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

12
Me encanta que no hayas publicado un enlace al código, sino el código en sí.
Ben Coffman

2
Gran parte del código. No tuve que editarlo para que funcione ni nada. Gracias ~
James

55
Es útil para cubrir toda la pantalla de la siguiente manera: const int movementDistance = textField.frame.origin.y / 2; // ajustar según sea necesario
conecon

3
Debo mencionar que "si está escribiendo una aplicación para iOS 4 o posterior, debería utilizar los métodos basados ​​en bloques para animar su contenido". Referenciado desde: developer.apple.com/library/ios/#documentation/windowsviews/…
Mathieu

1
Encontré esto y analiza el mismo problema developer.apple.com/Library/ios/documentation/StringsTextFonts/…
unom

38

Esto funcionó de maravilla para mí deslizando uitextfields

En particular, tiene la ventaja de calcular la distancia de animación de la diapositiva dependiendo de la posición del campo de texto.


Esto es genial. No tiene que elegir una constante de 'distancia de movimiento' para cada campo de texto; se calcula para usted.
jlstrecker

La mejor solución con diferencia. Mecanismo fenomenal!
Marchy

1
Funciona muy bien en el iPad también. Acabo de actualizar PORTRAIT_KEYBOARD_HEIGHT = 264 y LANDSCAPE_KEYBOARD_HEIGHT = 352. Gran enlace. Gracias.
Khon Lieu

¡El enlace de arriba me alegró el día! ¡Tan simple de implementar, funciona perfectamente hasta ahora!
JimmyJammed

1
Esta es la mejor explicación sobre este tema. Otros tutoriales usan vistas de tabla, vistas de desplazamiento, etc. Esto realmente funciona sin entrar en ninguna otra complejidad, simple y llanamente. Gracias por compartir esta fuente.
Renexandro

28

IQKeyboardManager hace esto por usted SIN LÍNEA DE CÓDIGO , solo necesita arrastrar y soltar el archivo fuente relacionado al proyecto. IQKeyboardManager también es compatible con la orientación del dispositivo , la gestión automática de UIToolbar , keyboardDistanceFromTextField y mucho más de lo que piensa.

ingrese la descripción de la imagen aquí

Aquí está el diagrama de flujo de control: Diagrama de flujo de control

Paso 1: - Añadido notificaciones globales de UITextField, UITextViewy UIKeyboarden una clase Singleton. Lo llamé IQKeyboardManager .

Paso 2: - Si lo encuentra UIKeyboardWillShowNotification, UITextFieldTextDidBeginEditingNotificationo UITextViewTextDidBeginEditingNotificationlas notificaciones, a continuación, tratar de obtener topMostViewControllerla instancia de la UIWindow.rootViewControllerjerarquía. Con el fin de destapar adecuadamente UITextField/ UITextViewen él, topMostViewController.view's marco necesita ser ajustado.

Paso 3: - Distancia de movimiento esperada calculada topMostViewController.viewcon respecto a la primera respuesta UITextField/ UITextView.

Paso 4: - Movido topMostViewController.view.framehacia arriba / abajo de acuerdo a la distancia medida que se espera.

Paso 5: - Si se encuentra UIKeyboardWillHideNotification, UITextFieldTextDidEndEditingNotificationo UITextViewTextDidEndEditingNotificationnotificación, intente nuevamente obtener la topMostViewControllerinstancia de la UIWindow.rootViewControllerjerarquía.

Paso 6: - La distancia perturbada calculada de la topMostViewController.viewcual debe restaurarse a su posición original.

Paso 7: - Restaurado topMostViewController.view.framesegún la distancia perturbada.

Paso 8: - Instancia de instancia IQKeyboardManager singleton instanciada en la carga de la aplicación, por lo que cada UITextField/ UITextViewen la aplicación se ajustará automáticamente de acuerdo con la distancia de movimiento esperada.

Eso es todo


La respuesta aceptada no estaba funcionando para mí. Pero este sí.
alumno

@ZaEeMZaFaR IQKeyboardManager también está optimizado para iPad. ¿Puede abrir un problema en el repositorio de github de la biblioteca y cargar un proyecto de demostración que demuestre el problema con el iPad?
Mohd Iftekhar Qurashi

Gracias por responder. Lo haré si el problema persiste, antes de comentar estaba pensando que IQKeyboardManager no es para dispositivos universales.
ZaEeM ZaFaR

Al investigar más, funciona bien con iPad en el simulador pero no en el dispositivo iPad real. Funciona perfectamente bien en iPhone también (Dispositivo + Simulador). Cual podría ser el problema ?
ZaEeM ZaFaR

Como dije anteriormente, debe plantear un problema en el repositorio de github con el proyecto de demostración con los detalles de su versión de iOS y dispositivo, para que podamos analizar el problema.
Mohd Iftekhar Qurashi

7

Para ampliar la respuesta de Amagrammer, aquí hay una clase de muestra:

LoginViewController.h

@interface LoginViewController : UIViewController <UITextFieldDelegate> {

}

@property (nonatomic, retain) IBOutlet UITextField    *emailTextField;
@property (nonatomic, retain) IBOutlet UITextField    *passwordTextField;

Observe que estamos implementando el "UITextFieldDelegate"

LoginViewController.m

@implementation LoginViewController
@synthesize emailTextField=_emailTextField;
@synthesize passwordTextField=_passwordTextField;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        //Register to receive an update when the app goes into the backround
        //It will call our "appEnteredBackground method
        [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(appEnteredBackground)
                                                 name:UIApplicationDidEnterBackgroundNotification
                                               object:nil];
    }
    return self;
}


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

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

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

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


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

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    [textField resignFirstResponder];
    return YES;
}
//This is called when the app goes into the background.
//We must reset the responder because animations will not be saved
- (void)appEnteredBackground{
    [self.emailTextField resignFirstResponder];
    [self.passwordTextField resignFirstResponder];
}

+1 por mencionar UIApplicationDidEnterBackgroundNotification, de lo contrario, se moverá más hacia abajo si uno presiona el botón Inicio y vuelve a entrar en la aplicación, lo que hace que sea feo y tenga errores.
Adil Soomro

7

¿Qué tal la solución oficial: mover el contenido que se encuentra debajo del teclado?

Ajustar su contenido generalmente implica cambiar temporalmente el tamaño de una o más vistas y posicionarlas para que el objeto de texto permanezca visible. La forma más sencilla de administrar objetos de texto con el teclado es incrustarlos dentro de un objeto UIScrollView (o una de sus subclases como UITableView). Cuando se muestra el teclado, todo lo que tiene que hacer es restablecer el área de contenido de la vista de desplazamiento y desplazar el objeto de texto deseado a su posición. Por lo tanto, en respuesta a un UIKeyboardDidShowNotification, su método de controlador haría lo siguiente:

  1. Obtenga el tamaño del teclado.
  2. Ajuste el contenido inferior de su vista de desplazamiento por la altura del teclado.
  3. Desplace el campo de texto de destino a la vista.
// Call this method somewhere in your view controller setup code.
- (void)registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
            selector:@selector(keyboardWasShown:)
            name:UIKeyboardDidShowNotification object:nil];

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

}

// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
    scrollView.contentInset = contentInsets;
    scrollView.scrollIndicatorInsets = contentInsets;

    // If active text field is hidden by keyboard, scroll it so it's visible
    // Your app might not need or want this behavior.
    CGRect aRect = self.view.frame;
    aRect.size.height -= kbSize.height;
    if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
        [self.scrollView scrollRectToVisible:activeField.frame animated:YES];
    }
}

// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    scrollView.contentInset = contentInsets;
    scrollView.scrollIndicatorInsets = contentInsets;
}

Esta solución oficial ahora está envuelta en un control, ver aquí: - stackoverflow.com/a/17707094/1582217
Mohd Iftekhar Qurashi

La idea es buena, pero la propiedad 'contentInset' no ayuda aquí, porque contentInset solo le proporcionará el efecto de relleno: stackoverflow.com/a/10049782/260665
Raj Pawan Gumdal

@Raj, ese puede ser el caso que puedo ver en IQKeyboardManager, pero aún así nadie abre ningún problema en el repositorio oficial de github de IQKeyboardManager con respecto al problema de propiedad contentInset, así que supongo que está funcionando.
Mohd Iftekhar Qurashi

7

Tengo el mismo problema en UITableView las celdas textField. Resuelvo este problema implementando el siguiente método para escuchar la notificación del teclado.

Observador de las notificaciones aquí:

[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil];

Maneje esas notificaciones utilizando la siguiente función:

(void)keyboardWasShown:(NSNotification*)aNotification 
(void)keyboardWillBeHidden:(NSNotification*)aNotification 

1
El enlace está roto. ¡Considere incluir una solución independiente en el futuro!
miek

5

Mira esto. Sin problemas para ti.

Esta solución es muy ordenada. Todo lo que tiene que hacer es agregar sus campos de texto en ay UIScrollViewcambiar su clase a TPKeyboardAvoidingScollView, si está usando guiones gráficos. La vista de desplazamiento se extiende de tal manera que detectaría cuando el teclado es visible y se moverá por encima del teclado a una distancia razonable. Es la solución perfecta porque es independiente de su UIViewController. Todo lo necesario se hace dentro de la clase mencionada anteriormente. Gracias Michael Tyson y todos.

TPKeyboardEvitar


@NANNAV: proporcione comentarios cuando haga sugerencias para modificar una respuesta.
Sabueso de seguridad

5

A continuación se muestra una versión rápida de la respuesta de Amagrammer. Además, una variación usando el evento UIKeyboardWillShowNotification ya que necesitaba saber el tamaño del teclado antes de quitar la vista del camino.

var keyboardHeight:CGFloat = 0

override func viewDidLoad() {
    super.viewDidLoad()
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillChange:", name: UIKeyboardWillShowNotification, object: nil)
}

func textFieldDidBeginEditing(textField: UITextField) {
    //keyboardWillChange (below) is used instead of textFieldDidBeginEditing because textFieldDidBeginEditing
    //is called before the UIKeyboardWillShowNotification necessary to determine the keyboard height.
}

func textFieldDidEndEditing(textField: UITextField) {
    animateTextField(false)
}

func animateTextField(textFieldUp:Bool) {
    let movementDistance:CGFloat = keyboardHeight
    let movementDuration = 0.3

    let movement:CGFloat = (textFieldUp ? -movementDistance : movementDistance)

    UIView.beginAnimations("anim", context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)
    UIView.setAnimationDuration(movementDuration)
    self.view.frame = CGRectOffset(self.view.frame, 0, movement)
    UIView.commitAnimations()
}

func keyboardWillChange(notification:NSNotification) {
    let keyboardRect:CGRect = ((notification.userInfo![UIKeyboardFrameEndUserInfoKey])?.CGRectValue)!
    keyboardHeight = keyboardRect.height
    animateTextField(true)
}

4

Hubo un gran recorrido en la edición de campos de texto sin oscurecer (enlace muerto ahora, aquí hay un enlace Wayback: https://web.archive.org/web/20091123074029/http://acts-as-geek.blogspot.com/2009/ 11 / edición-textfields-without-obscuring.html ). Muestra cómo mover un existente UIViewa un UIScrollView, y desplazarlo automáticamente cuando aparece el teclado.

Lo actualicé un poco para calcular la altura correcta para UIScrollViewcuando hay controles (como a UITabBar) debajo de UIScrollBar. Ver publicación uiview de actualización .


2

Aquí hay una solución usando Xcode5, iOS7:

Use los bloques UITextfieldDelegate y de animación.

Este es casi todo el código para ViewController, pero quería incluir el código de delegado para aquellos que aún no están familiarizados con el patrón de delegado (como yo). También incluí código para ocultar el teclado cuando toca fuera de la vista de texto.

Puede mover las vistas (botones, campos de texto, etc.) tan alto como desee, solo asegúrese de volver a colocarlas en su lugar (+100 y luego -100).

@interface ViewController () <UITextFieldDelegate>
@property (strong, nonatomic) IBOutlet UITextField *MyTextField;

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.MyTextField.delegate = self;

}

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
      NSLog(@"text began editing");

      CGPoint MyPoint = self.MyTextField.center;

      [UIView animateWithDuration:0.3
                    animations:^{

                    self.MyTextField.center = CGPointMake(MyPoint.x, MyPoint.y - 100);
                                }];
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
     NSLog(@"text ENDED editing");

     CGPoint MyPoint = self.MyTextField.center;

     [UIView animateWithDuration:0.3
                 animations:^{

     self.MyTextField.center = CGPointMake(MyPoint.x, MyPoint.y + 100);
                             }];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
     [self.view endEditing:YES];
}

1

Supongo que una forma sería mover la posición de todas sus vistas de (x, y) a (x, y-keybaardHeight) cuando se hace clic en el campo de texto y volver a colocarlo cuando se cierra el teclado, puede parecer un poco extraño ya que la vista solo aparece (tal vez no sería malo si lo animas).

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    CGRect frame=self.view.frame;
    frame.origin=CGPointMake(x...//set point here
    self.view.frame=frame;
}

No, no es. Si el usuario toca el primer campo de texto, se pondrá por encima del área visible.
lolol

0

Además de la solución de Amagrammer, si está utilizando cocos2d en modo vertical, cambie esta línea:

self.view.frame = CGRectOffset(self.view.frame, 0, movement);

a esto:

[CCDirector sharedDirector].openGLView.frame = CGRectOffset([CCDirector sharedDirector].openGLView.frame, movement, 0);

Si está utilizando cocos2d en modo horizontal, realice el cambio anterior y cambie los upvalores en textFieldDidBeginEditing:ytextFieldDidEndEditing:

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

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

0

Tuve el mismo problema y descubrí que GTKeyboardHelper era una salida fácil.

Después de arrastrar y soltar el marco en su proyecto, incluya el archivo de encabezado. Descargue y abra el proyecto de ejemplo, luego arrastre el objeto "Keyboard Helper" desde la sección de objetos en el xib a la sección de objetos en el generador de interfaz de su proyecto.

Arrastre y suelte todas sus vistas para ser hijos de "Keyboard Helper".


0

Arrastrar y soltar el marco que utilizo en mis proyectos. Admite el despido automático cuando toca fuera de un primer respondedor o cuando se desplaza.

GTKeyboardHelper


0

Simplemente deslice la vista hacia arriba y hacia abajo según sea necesario:

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

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    [self.currentTextField resignFirstResponder];
    return YES;
}

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

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

    [UIView animateWithDuration:movementDuration animations:^{
        self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    }];
}

No olvide establecer selfcomo UITextFieldDelegatey como el campo de texto realdelegate .

(Gracias a Ammagrammer, esta es solo una respuesta más corta usando bloques para animaciones)


0

Tengo algo más si quieres. El punto aquí es que desea establecer el centro de su UIView en el campo de texto que está editando.

Antes de eso, debe guardar su INITIAL_CENTER , como CGPoint , de self.view.center y su INITIAL_VIEW como CGRect de self.view.frame en una propiedad const.

Puedes crear un método como este:

- (void) centerOn: (CGRect) fieldFrame {

    // Set up the center by taking the original view center
    CGPoint center = CGPointMake(INITIAL_CENTER.x,
                             INITIAL_CENTER.y - ((fieldFrame.origin.y + fieldFrame.size.height/2) - INITIAL_CENTER.y));


    [UIView beginAnimations:@"centerViewOnField" context:nil];
    [UIView setAnimationDuration:0.50];

    if (CGRectEqualToRect(fieldFrame,INITIAL_VIEW)) {
        self.view.frame = INITIAL_VIEW;
        [self.view setCenter:INITIAL_CENTER];
    } else {
        [self.view setCenter:center];
    }


    [UIView commitAnimations];
}

Luego, en su UITextFieldDelegate , debe llamar a centerOn: (CGRect) en los siguientes métodos:

textFieldDidBeginEditing: (UITextField *) con, como parámetro, el marco del campo de texto en el que desea centrarse.

Y tiene que llamarlo en su controlador de eventos, donde cierra su teclado,

textFieldDidEndEditing: (UITextField *) puede ser una de las formas de hacerlo, colocando INITIAL_VIEW como un parámetro de centerOn: (CGRect) .


0

Creo que en las versiones más recientes de iOS (6.1+, posiblemente incluso antes), la vista subyacente, al menos para UITableView, se contrae automáticamente cuando aparece el teclado. Por lo tanto, solo necesita hacer que el campo de texto sea visible en esa vista. En init:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWasShown:)
                                             name:UIKeyboardDidShowNotification
                                           object:nil];

luego:

- (void)keyboardWasShown:(NSNotification*)notification
{
    // Scroll the text field into view so it's not under the keyboard.
    CGRect rect = [self.tableView convertRect:inputView.bounds fromView:inputView];
    [self.tableView scrollRectToVisible:rect animated:YES];
}

0

https://github.com/ZulwiyozaPutra/Shift-Keyboard-Example Espero que esta solución haya ayudado. Todos están escritos en Swift 3.

//
//  ViewController.swift
//  Shift Keyboard Example
//
//  Created by Zulwiyoza Putra on 11/23/16.
//  Copyright © 2016 Zulwiyoza Putra. All rights reserved.
//

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {
    
    
    //connecting textfield from storyboard
    @IBOutlet weak var textField: UITextField!
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        subscribeToKeyboardNotifications()
    }
    
    override func viewDidAppear(_ animated: Bool) {
        self.textField.delegate = self
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        unsubscribeFromKeyboardNotifications()
    }
    
    //Hide keyboard after finished editing
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }
    
    //Setup view before keyboard appeared
    func keyboardWillAppear(_ notification:Notification) {
        view.frame.origin.y = 0 - getKeyboardHeight(notification)
    }
    
    //Setup view before keyboard disappeared
    func keyboardWillDisappear(_ notification: Notification) {
        view.frame.origin.y = 0
    }
    
    //Getting keyboard height
    func getKeyboardHeight(_ notification:Notification) -> CGFloat {
        let userInfo = notification.userInfo
        let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue // of CGRect
        return keyboardSize.cgRectValue.height
    }
    
    //Subscribing to notifications to execute functions
    func subscribeToKeyboardNotifications() {
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillAppear(_:)), name: .UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillDisappear(_:)), name: .UIKeyboardWillHide, object: nil)
    }
    
    //Unsubscribing from notifications
    func unsubscribeFromKeyboardNotifications() {
        NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
        NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
    }
    
}

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.