Capas de teclado para iPhone UITextField

120

Eu tenho um aplicativo em que, no Interface Builder , configurei um UIViewque tenha um campo de texto próximo à parte inferior da exibição. Quando executo o aplicativo e tento inserir texto nesse campo, o teclado desliza para cima do campo, para que eu não possa ver o que estou digitando até ocultar o teclado novamente.

Alguém mais encontrou esse problema e encontrou uma boa maneira de resolvê-lo sem tornar a exibição pai rolável ou mover o campo de texto mais para cima na tela?

Cruinh
fonte

Respostas:

290

A solução usual é deslizar o campo (e tudo o que está acima dele) com uma animação e depois voltar ao final. Pode ser necessário colocar o campo de texto e alguns dos outros itens em outra exibição e deslizar a exibição como uma unidade. (Eu chamo essas coisas de "placas" como em "placas tectônicas", mas sou apenas eu). Mas aqui está a idéia geral, se você não precisa ser extravagante.

- (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];
}
Amagrammer
fonte
12
Adorei que você não postou um link para o código, mas o próprio código.
Ben Coffman
2
Grande parte do código. Não precisava editá-lo para funcionar ou algo assim. Obrigado ~
James
5
É útil cobrir toda a tela como abaixo: const int movementDistance = textField.frame.origin.y / 2; // ajusta conforme necessário
conecon
3
Devo mencionar que "Se você está escrevendo um aplicativo para iOS 4 ou posterior, deve usar os métodos baseados em blocos para animar seu conteúdo". Referenciado a partir de: developer.apple.com/library/ios/#documentation/windowsviews/…
Mathieu
1
Encontrei isso e discute o mesmo problema developer.apple.com/Library/ios/documentation/StringsTextFonts/…
unom
38

Isso funcionou maravilhas para mim deslizando uitextfields

Em particular, ele tem o benefício de calcular a distância da animação do slide, dependendo da posição do campo de texto.

considerado
fonte
Isso é ótimo. Você não precisa escolher uma constante de 'distância de movimento' para cada campo de texto - é calculado para você.
Jlstrecker
Melhor solução de longe. Mecanismo fenomenal!
Marchy
1
Funciona muito bem no iPad também. Acabei de atualizar PORTRAIT_KEYBOARD_HEIGHT = 264 e LANDSCAPE_KEYBOARD_HEIGHT = 352. Ótimo link. Obrigado.
Khon Lieu
O link acima acabou de fazer o meu dia! Tão simples de implementar, funciona perfeitamente até agora!
JimmyJammed
1
Essa é a melhor explicação para esse tópico. Outros tutoriais usam exibições de tabela, exibições de rolagem etc. Isso realmente funciona sem entrar em outras complexidades, simples e simples. Obrigado por compartilhar esta fonte.
Renexandro
28

O IQKeyboardManager faz isso para você sem NO LINE OF CODE , apenas é necessário arrastar e soltar o arquivo de origem relacionado ao projeto. O IQKeyboardManager também suporta Orientação a dispositivos , Gerenciamento automático da barra de ferramentas do UIT , keyboardDistanceFromTextField e muito mais do que você pensa.

insira a descrição da imagem aqui

Aqui está o fluxograma de controle: Fluxograma de controle

Passo 1: - Adicionado notificações globais de UITextField, UITextViewe UIKeyboardem uma única classe. Eu chamei de IQKeyboardManager .

Etapa 2: - Se encontrado UIKeyboardWillShowNotification, UITextFieldTextDidBeginEditingNotificationou UITextViewTextDidBeginEditingNotificationnotificações, tente obter a topMostViewControllerinstância da UIWindow.rootViewControllerhierarquia. A fim de adequadamente uncover UITextField/ UITextViewsobre ele, topMostViewController.view's quadro precisa ser ajustado.

Etapa 3: - Distância calculada de movimento esperada topMostViewController.viewem relação à primeira resposta UITextField/ UITextView.

Passo 4: - Movido para topMostViewController.view.framecima / baixo de acordo com a distância de movimento esperada.

Etapa 5: - Se encontrado UIKeyboardWillHideNotification, UITextFieldTextDidEndEditingNotificationou UITextViewTextDidEndEditingNotificationnotificação, tente novamente obter topMostViewControllerinstância da UIWindow.rootViewControllerhierarquia.

Etapa 6: - Distância perturbada calculada da topMostViewController.viewqual precisa ser restaurada para sua posição original.

Passo 7: - Restaurado de topMostViewController.view.frameacordo com a distância perturbada.

Etapa 8: - Instância da classe IQKeyboardManager singleton instanciada no carregamento do aplicativo, para que todos UITextField/ UITextViewno aplicativo se ajustem automaticamente de acordo com a distância de movimentação esperada.

Isso é tudo

Mohd Iftekhar Qurashi
fonte
A resposta aceita não estava funcionando para mim. Mas este faz.
aluno
@ZaEeMZaFaR O IQKeyboardManager também é otimizado para o iPad. Você pode abrir um problema no repositório do github da biblioteca e fazer o upload de um projeto de demonstração demonstrando o problema no iPad?
Mohd Iftekhar Qurashi
Obrigado pela resposta. Farei isso se o problema persistir, antes de comentar, eu estava pensando que o IQKeyboardManager não é para dispositivo universal.
ZaEeM ZaFaR
Ao investigar mais, está funcionando bem com o iPad no simulador, mas não no dispositivo real do iPad. Funcionando perfeitamente bem no iPhone também (Device + Simulator). Qual poderia ser o problema?
ZaEeM ZaFaR
Como eu disse anteriormente, você deve levantar um problema no repositório do github com projeto de demonstração com os detalhes da versão do iOS e do dispositivo, para que possamos analisar o problema.
Mohd Iftekhar Qurashi
7

Para expandir a resposta do Amagrammer, aqui está uma classe de amostra:

LoginViewController.h

@interface LoginViewController : UIViewController <UITextFieldDelegate> {

}

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

Observe que estamos implementando o "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];
}
stebooks
fonte
+1 para mencionar UIApplicationDidEnterBackgroundNotification, caso contrário, ele se moverá mais para baixo se você pressionar o botão Início e entrar novamente no aplicativo, fazendo com que ele fique feio e com erros.
Adil Soomro
7

Que tal a solução oficial: Movendo o conteúdo localizado sob o teclado

Ajustar seu conteúdo geralmente envolve redimensionar temporariamente uma ou mais visualizações e posicioná-las para que o objeto de texto permaneça visível. A maneira mais simples de gerenciar objetos de texto com o teclado é incorporá-los a um objeto UIScrollView (ou a uma de suas subclasses como UITableView). Quando o teclado é exibido, tudo o que você precisa fazer é redefinir a área de conteúdo da exibição de rolagem e rolar o objeto de texto desejado para a posição. Assim, em resposta a um UIKeyboardDidShowNotification, seu método manipulador faria o seguinte:

  1. Obtenha o tamanho do teclado.
  2. Ajuste o conteúdo inferior da visualização de rolagem pela altura do teclado.
  3. Role o campo de texto de destino para exibição.
// 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;
}
Marte
fonte
Esta solução oficial está agora envolto em um controle de ver aqui: - stackoverflow.com/a/17707094/1582217
Mohd Iftekhar Qurashi
Idéia é boa, mas a propriedade 'contentInset' não ajuda aqui, coz contentInset só vai fornecer-lhe o efeito padding: stackoverflow.com/a/10049782/260665
Raj Pawan Gumdal
@Raj, esse pode ser o caso no IQKeyboardManager, mas ainda ninguém abriu nenhum problema no repositório oficial do IQKeyboardManager no que diz respeito à questão da propriedade contentInset, por isso presumo que esteja funcionando.
Mohd Iftekhar Qurashi
7

Eu enfrentei o mesmo problema nas UITableView células textField. Resolvo esse problema implementando o método a seguir para escutar a notificação do teclado.

Observador para as notificações aqui:

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

Manipule essas notificações usando a função abaixo:

(void)keyboardWasShown:(NSNotification*)aNotification 
(void)keyboardWillBeHidden:(NSNotification*)aNotification 
Boobalan
fonte
1
O link está quebrado. Considere incluir uma solução independente no futuro!
Miek
5

Veja isso. Sem problemas para você.

Esta solução é muito elegante. Tudo o que você precisa fazer é adicionar seus campos de texto em a UIScrollViewe alterar sua classe para TPKeyboardAvoidingScollView, se você estiver usando storyboards. A exibição de rolagem é estendida de forma a detectar quando o teclado está visível e se move acima do teclado a uma distância razoável. É a solução perfeita porque é independente da sua UIViewController. Tudo o que é necessário é feito dentro da classe mencionada acima. Obrigado Michael Tyson e tudo.

TPKeyboardAvoiding

Aruna
fonte
@NANNAV - Por favor, forneça comentários quando você fizer sugestões para modificar uma resposta.
Security Hound
5

Abaixo está uma versão rápida da resposta do Amagrammer. Além disso, uma variação usando o evento UIKeyboardWillShowNotification, pois eu precisava saber o tamanho dos teclados antes de mudar a exibição.

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)
}
Justin Domnitz
fonte
4

Houve um grande passo a passo na edição de campos de texto sem obscurecer (link morto agora, aqui está um link do Wayback: https://web.archive.org/web/20091123074029/http://acts-as-geek.blogspot.com/2009/ 11 / editing-textfields-without-obscuring.html ). Ele mostra como mover um existente UIViewpara um UIScrollViewe rolar automaticamente quando o teclado aparece.

Atualizei-o um pouco para calcular a altura correta para UIScrollViewquando houver controles (como a UITabBar) abaixo do UIScrollBar. Consulte a publicação da atualização do uiview .

GargantuChet
fonte
2

Aqui está uma solução usando o Xcode5, iOS7:

Use os blocos UITextfieldDelegate e de animação.

Isso é quase todo o código do ViewController, mas eu queria incluir o código delegado para aqueles que ainda não estão familiarizados com o padrão delegado (como eu). Também incluí o código para ocultar o teclado quando você toca fora da visualização de texto.

Você pode mover as visualizações (botões, campos de texto, etc.) o mais alto possível, apenas colocando-as no lugar novamente (+100 e depois -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];
}
Ethan Parker
fonte
1

Eu acho que uma maneira seria mover toda a posição de suas visualizações de (x, y) para (x, y-keybaardHeight) quando o campo de texto é clicado e colocá-lo de volta quando o teclado é descartado, pode parecer um pouco estranho, pois a visualização apenas aparece (talvez não seja ruim se você o animar).

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    CGRect frame=self.view.frame;
    frame.origin=CGPointMake(x...//set point here
    self.view.frame=frame;
}
Daniel
fonte
Não, não é. Se o usuário tocar no primeiro campo de texto, ele ficará acima da área visível.
Lol12 /
0

Além da solução do Amagrammer, se você estiver usando o cocos2d no modo retrato, altere esta linha:

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

para isso:

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

Se você estiver usando o cocos2d no modo paisagem, faça as alterações acima e troque os upvalores em textFieldDidBeginEditing:etextFieldDidEndEditing:

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

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

Eu tive o mesmo problema e achei o GTKeyboardHelper um caminho fácil.

Depois de arrastar e soltar a estrutura em seu projeto, inclua o arquivo de cabeçalho. Faça o download e abra o projeto de exemplo e arraste o objeto "Keyboard Helper" da seção de objetos no xib para a seção de objetos no construtor de interface do seu projeto.

Arraste e solte todas as suas visualizações para ser filho do "Keyboard Helper".

Andrei Nagy
fonte
0

Arraste e solte a estrutura que eu uso nos meus projetos. Oferece suporte à dispensa automática quando você toca fora de uma primeira resposta ou quando você rola.

GTKeyboardHelper

mackross
fonte
0

Basta deslizar a visualização para cima e para baixo, conforme necessário:

- (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);
    }];
}

Não se esqueça de definir selfcomo UITextFieldDelegatee como o campo de texto real delegate.

(Graças ao Ammagrammer, esta é apenas uma resposta mais curta usando blocos para animações)

dulgan
fonte
0

Eu tenho outra coisa, se você quiser. O ponto aqui é que você deseja definir o centro da sua UIView no campo de texto que você está editando.

Antes disso, você deve salvar seu INITIAL_CENTER , como um CGPoint , de self.view.center e seu INITIAL_VIEW como um CGRect de self.view.frame em uma propriedade const.

Você pode criar um 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];
}

Em seguida, no seu UITextFieldDelegate , é necessário chamar centerOn: (CGRect) nos seguintes métodos:

textFieldDidBeginEditing: (UITextField *) com, como parâmetro, o quadro do campo de texto que você deseja centralizar.

E você deve chamá-lo no seu manipulador de eventos, onde você fecha o teclado,

textFieldDidEndEditing: (UITextField *) pode ser uma das maneiras de fazer isso, colocando o INITIAL_VIEW como um parâmetro de centerOn: (CGRect) .

Jissay
fonte
0

Acredito que nas versões mais recentes do iOS (6.1 ou superior), a visão subjacente, pelo menos para o UITableView, diminui automaticamente quando o teclado é exibido. Portanto, você só precisa tornar o campo de texto visível nessa exibição. Em init:

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

então:

- (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];
}
Lawrence Kesteloot
fonte
0

https://github.com/ZulwiyozaPutra/Shift-Keyboard-Example Espero que esta solução tenha ajudado. Eles são todos Swift 3 escritos.

//
//  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)
    }
    
}

Zulwiyoza Putra
fonte