Чехлы для клавиатуры iPhone UITextField

120

У меня есть приложение, в котором в Interface Builder я настраиваюUIView с текстовым полем в нижней части представления. Когда я запускаю приложение и пытаюсь ввести текст в это поле, клавиатура скользит вверх по полю, поэтому я не вижу, что печатаю, пока снова не скрою клавиатуру.

Кто-нибудь еще сталкивался с этой проблемой и нашел хороший способ решить ее, не делая прокручиваемое родительское представление или не перемещая текстовое поле дальше по экрану?

Cruinh
источник

Ответы:

290

Обычное решение - сдвинуть поле (и все, что над ним) вверх с анимацией, а затем вернуться вниз, когда вы закончите. Возможно, вам потребуется поместить текстовое поле и некоторые другие элементы в другое представление и сдвинуть представление как единое целое. (Я называю это «плитами», как «тектонические плиты», но это только я). Но вот общая идея, если вам не нужно фантазировать.

- (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
источник
12
Мне нравится, что вы разместили не ссылку на код, а сам код.
Бен Коффман,
2
Отличный код. Не нужно было его редактировать, чтобы он работал или что-то в этом роде. Спасибо ~
Джеймс
5
Полезно покрыть весь экран, как показано ниже: const int motionDistance = textField.frame.origin.y / 2; //
дорабатываем
3
Я должен упомянуть, что «Если вы пишете приложение для iOS 4 или новее, вам следует вместо этого использовать блочные методы для анимации вашего контента». Ссылка на: developer.apple.com/library/ios/#documentation/windowsviews/…
Матье,
1
Нашел это, и в нем обсуждается та же проблема developer.apple.com/Library/ios/documentation/StringsTextFonts/…
unom
38

Это творило чудеса для меня, сдвигая поля uitext

В частности, он позволяет рассчитывать расстояние анимации слайдов в зависимости от положения текстового поля.

cidered
источник
Это круто. Вам не нужно выбирать некоторую константу «расстояния перемещения» для каждого текстового поля - она ​​рассчитывается для вас.
jlstrecker 07
Лучшее решение на сегодняшний день. Феноменальный механизм!
Marchy
1
Прекрасно работает и на iPad. Я только что обновил PORTRAIT_KEYBOARD_HEIGHT = 264 и LANDSCAPE_KEYBOARD_HEIGHT = 352. Отличная ссылка. Спасибо.
Khon Lieu
Вышеупомянутая ссылка просто сделала мой день! Настолько просто в реализации, работает до сих пор безупречно!
JimmyJammed
1
Это лучшее объяснение по этой теме. В других руководствах используются представления таблиц, представления прокрутки и т. Д. Это действительно работает, не вдаваясь в другие сложности, просто и понятно. Спасибо, что поделились этим источником.
Ренександро
28

IQKeyboardManager сделает это за вас, БЕЗ СТРОЧКИ КОДА , нужно только перетащить соответствующий исходный файл в проект. IQKeyboardManager также поддерживает ориентацию устройства , автоматическое управление UIToolbar , keyboardDistanceFromTextField и многое другое, чем вы думаете.

введите описание изображения здесь

Вот блок-схема управления: Схема управления

Шаг1: - Добавлены глобальные уведомления о UITextField, UITextViewи UIKeyboardв одноплодной классе. Я назвал его IQKeyboardManager .

Шаг2: - Если будет установлено UIKeyboardWillShowNotification, UITextFieldTextDidBeginEditingNotificationили UITextViewTextDidBeginEditingNotificationуведомлений, а затем попытаться получить topMostViewControllerэкземпляр из UIWindow.rootViewControllerиерархии. Чтобы правильно раскрыть UITextField/ UITextViewна нем, topMostViewController.viewнеобходимо отрегулировать раму.

Шаг 3: - Расчетное ожидаемое расстояние перемещения topMostViewController.viewотносительно первого откликнувшегося UITextField/ UITextView.

Шаг 4: - Перемещение topMostViewController.view.frameвверх / вниз в соответствии с ожидаемым расстоянием перемещения.

Шаг 5: - Если обнаружено UIKeyboardWillHideNotification, UITextFieldTextDidEndEditingNotificationили UITextViewTextDidEndEditingNotificationуведомление, то снова попробуйте получить topMostViewControllerэкземпляр из UIWindow.rootViewControllerиерархии.

Шаг 6: - Расчетное отклоненное расстояние, на topMostViewController.viewкотором необходимо восстановить исходное положение.

Шаг 7: - Восстановлено topMostViewController.view.frameсогласно нарушенному расстоянию.

Шаг 8: - Создан экземпляр класса singleton IQKeyboardManager при загрузке приложения, поэтому каждый UITextField/ UITextViewв приложении будет автоматически настраиваться в соответствии с ожидаемым расстоянием перемещения.

Вот и все

Мохд Ифтехар Кураши
источник
Принятый ответ не работал у меня. Но этот делает.
учащийся
@ZaEeMZaFaR IQKeyboardManager также оптимизирован для iPad. Не могли бы вы открыть проблему с репозиторием библиотеки github и загрузить демонстрационный проект, демонстрирующий проблему с iPad?
Мохд Ифтехар Кураши
Спасибо за ответ. Я сделаю это, если проблема не исчезнет, ​​прежде чем вы комментируете, я думал, что IQKeyboardManager не для универсального устройства.
ZaEeM ZaFaR
Изучив больше, он отлично работает с iPad на симуляторе, но не на реальном устройстве iPad. Прекрасно работает и на iPhone (устройство + симулятор). В чем может быть проблема ?
ZaEeM ZaFaR
Как я уже говорил ранее, вы должны поднять проблему в репозитории github с демонстрационным проектом с информацией о вашей iOS и версии устройства, чтобы мы могли разобраться в проблеме.
Мохд Ифтехар Кураши
7

Чтобы расширить ответ Amagrammer, вот образец класса:

LoginViewController.h

@interface LoginViewController : UIViewController <UITextFieldDelegate> {

}

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

Обратите внимание, что мы реализуем «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
источник
+1 за упоминание UIApplicationDidEnterBackgroundNotification, иначе он будет двигаться вниз, если вы нажмете кнопку «Домой» и снова войдете в приложение, что сделает его некрасивым и глючным.
Adil Soomro
7

Как насчет официального решения: перемещение содержимого под клавиатурой?

Регулировка содержимого обычно включает временное изменение размера одного или нескольких представлений и их расположение так, чтобы текстовый объект оставался видимым. Самый простой способ управлять текстовыми объектами с клавиатуры - это встроить их в объект UIScrollView (или один из его подклассов, например UITableView). Когда отображается клавиатура, все, что вам нужно сделать, это сбросить область содержимого прокрутки и прокрутить нужный текстовый объект в нужное положение. Таким образом, в ответ на UIKeyboardDidShowNotification ваш метод-обработчик сделает следующее:

  1. Получите размер клавиатуры.
  2. Отрегулируйте нижнюю вставку содержимого окна прокрутки по высоте клавиатуры.
  3. Прокрутите целевое текстовое поле в поле зрения.
// 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;
}
Марс
источник
Это официальное решение теперь заключено в элемент управления, см. Здесь: - stackoverflow.com/a/17707094/1582217
Mohd Iftekhar Qurashi
Идея хороша, но свойство contentInset здесь не помогает, coz contentInset просто предоставит вам эффект заполнения: stackoverflow.com/a/10049782/260665
Raj Pawan Gumdal
@Raj, Это может быть тот случай, когда я могу посмотреть на IQKeyboardManager, но до сих пор никто не открывает никаких проблем в официальном репозитории github IQKeyboardManager относительно проблемы со свойством contentInset, поэтому я предполагаю, что это работает.
Мохд Ифтехар Кураши
7

У меня такая же проблема с UITableView ячейками textField. Я решил эту проблему, реализовав следующий метод прослушивания уведомления с клавиатуры.

Наблюдатель за уведомлениями здесь:

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

Обработайте это уведомление с помощью функции ниже:

(void)keyboardWasShown:(NSNotification*)aNotification 
(void)keyboardWillBeHidden:(NSNotification*)aNotification 
Boobalan
источник
1
Ссылка не работает. Пожалуйста, подумайте о включении независимого решения в будущем!
miek
5

Проверь это. Никаких хлопот для вас.

Это очень изящное решение. Все, что вам нужно сделать, это добавить свои текстовые поля в UIScrollViewи изменить его класс на TPKeyboardAvoidingScollView, если вы используете раскадровки. Вид прокрутки расширен таким образом, что он обнаруживает, когда клавиатура видна, и перемещается над клавиатурой на разумном расстоянии. Это идеальное решение, потому что оно не зависит от вашего UIViewController. Все необходимое делается в рамках вышеупомянутого класса. Спасибо Майклу Тайсону и всем остальным.

TPKeyboardAvoiding

Аруна
источник
@NANNAV - Пожалуйста, комментируйте, когда вы вносите предложения по изменению ответа.
Security Hound
5

Ниже представлена ​​быстрая версия ответа Амаграммера. Кроме того, вариант с использованием события UIKeyboardWillShowNotification, поскольку мне нужно было знать размер клавиатуры, прежде чем убирать представление с дороги.

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

Было отличное пошаговое руководство по редактированию текстовых полей без затемнения (ссылка сейчас мертва, вот обратная ссылка: https://web.archive.org/web/20091123074029/http://acts-as-geek.blogspot.com/2009/ 11 / edit-textfields-without-obscuring.html ). Он показывает, как переместить существующее UIViewв a UIScrollViewи автоматически прокручивать его при появлении клавиатуры.

Я немного обновил его, чтобы рассчитать правильную высоту, UIScrollViewкогда есть элементы управления (например, a UITabBar) под UIScrollBar. См. Сообщение об обновлении uiview .

GargantuChet
источник
2

Вот решение с использованием Xcode5, iOS7:

Используйте блоки UITextfieldDelegate и animation.

Это почти весь код для ViewController, но я хотел включить код делегата для тех, кто еще несколько незнаком с шаблоном делегата (например, я). Я также включил код, чтобы скрыть клавиатуру, когда вы нажимаете вне текстового представления.

Вы можете перемещать представления (кнопки, текстовые поля и т. Д.) Настолько высоко, насколько хотите, просто не забудьте вернуть их на место (+100, затем -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

Я предполагаю, что одним из способов было бы переместить всю позицию вашего представления с (x, y) на (x, y-keybaardHeight) при щелчке по текстовому полю и вернуть его, когда клавиатура отклонена, может выглядеть немного странно, поскольку представление просто всплывает (может, неплохо было бы его анимировать).

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    CGRect frame=self.view.frame;
    frame.origin=CGPointMake(x...//set point here
    self.view.frame=frame;
}
Даниил
источник
Нет, это не так. Если пользователь коснется первого текстового поля, оно окажется над видимой областью.
lolol
0

В дополнение к решению Amagrammer, если вы используете cocos2d в портретном режиме, измените эту строку:

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

к этому:

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

Если вы используете cocos2d в ландшафтном режиме, внесите указанные выше изменения и переключите upзначения в textFieldDidBeginEditing:иtextFieldDidEndEditing:

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

- (void)textFieldDidEndEditing:(UITextField *)textField {
    [self animateTextField:textField up:YES];
}
Железный Майк
источник
0

У меня была та же проблема, и GTKeyboardHelper показался мне простым выходом.

После перетаскивания фреймворка в проект включите файл заголовка. Загрузите и откройте пример проекта, затем перетащите объект «Keyboard Helper» из раздела объектов в xib в раздел объектов в построителе интерфейса вашего проекта.

Перетащите все ваши представления, чтобы они были дочерними для «Помощника по клавиатуре».

Андрей Надь
источник
0

Фреймворк перетаскивания, который я использую в своих проектах. Поддерживает автоматическое закрытие при нажатии за пределами первого респондента или при прокрутке.

GTKeyboardHelper

mackross
источник
0

Просто сдвиньте вид вверх и вниз по мере необходимости:

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

Не забудьте установить selfкак UITextFieldDelegateи как фактическое текстовое полеdelegate .

(Спасибо Ammagrammer, это просто более короткий ответ с использованием блоков для анимации)

dulgan
источник
0

У меня есть кое-что еще, если хочешь. Дело в том, что вы хотите установить центр вашего UIView в текстовом поле, которое вы редактируете.

Перед этим вы должны сохранить свой INITIAL_CENTER как CGPoint из self.view.center и ваш INITIAL_VIEW как CGRect из self.view.frame в свойстве const.

Вы можете создать такой метод:

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

Затем на вашем UITextFieldDelegate вам нужно позвонить в centerOn: (CGRect) следующими методами:

textFieldDidBeginEditing: (UITextField *) с в качестве параметра рамкой текстового поля, в котором вы хотите .

И вы должны вызвать его в своем обработчике событий, где вы закрываете клавиатуру,

textFieldDidEndEditing: (UITextField *) может быть одним из способов сделать это, поместив INITIAL_VIEW в качестве параметра centerOn: (CGRect) .

Jissay
источник
0

Я считаю, что в более новых версиях iOS (6.1+, возможно, даже раньше) базовый вид, по крайней мере для UITableView, автоматически сжимается при появлении клавиатуры. Таким образом, вам нужно только сделать текстовое поле видимым в этом представлении. В init:

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

затем:

- (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 Надеюсь, это решение помогло. Все они написаны на 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)
    }
    
}

Зульвиёза Путра
источник