Клавіатура iPhone охоплює UITextField


120

У мене є додаток, де я в Інтерфейсі Builder встановив UIViewте, що має текстове поле внизу подання. Коли я запускаю додаток і намагаюся ввести текст у це поле, клавіатура ковзає вгору над полем, щоб я не бачив, що я набираю, поки не заховую клавіатуру знову.

Хто-небудь ще зіткнувся з цією проблемою і знайшов хороший спосіб її вирішити, не роблячи батьківський вигляд прокручуваним або переміщуючи текстове поле далі вгору по екрану?


Відповіді:


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

12
Подобається, що ви не опублікували посилання на код, а сам код.
Бен Коффман

2
Великий біт коду. Не потрібно було його редагувати, щоб він працював чи нічого. Спасибі ~
Джеймс

5
Корисно охопити весь екран, як показано нижче: const int motionDistance = textField.frame.origin.y / 2; // налаштувати за потребою
conecon

3
Я мушу зазначити, що "Якщо ви пишете заявку на iOS 4 або пізнішої версії, вам слід скористатися блоковими методами для анімації вашого вмісту". Посилання від: developer.apple.com/library/ios/#documentation/windowsviews/…
Матьє

1
Виявив це, і він обговорює ту саму проблему developer.apple.com/Library/ios/documentation/StringsTextFonts/…
14:58

38

Це творило чудеса для мене ковзних уитекстфілів

Зокрема, вона має перевагу обчислення відстані анімації слайдів залежно від положення текстового поля.


Це чудово. Не потрібно вибирати деяку константу "відстані руху" для кожного текстового поля - це розраховано для вас.
jlstrecker

Найкраще рішення на сьогоднішній день. Феноменальний механізм!
Березень

1
Чудово працює і на iPad. Щойно я оновив PORTRAIT_KEYBOARD_HEIGHT = 264 та LANDSCAPE_KEYBOARD_HEIGHT = 352. Чудове посилання. Дякую.
Khon Lieu

Наведене посилання щойно зробило мій день! Настільки простий у здійсненні, працює бездоганно поки що!
JimmyJammed

1
Це найкраще пояснення з цієї теми там. В інших навчальних посібниках використовуються «Перегляди таблиці», «Прокрутки» тощо. Це насправді працює без будь-яких інших складностей, звичайних та простих. Дякуємо, що поділилися цим джерелом.
Renexandro

28

IQKeyboardManager зробить це для вас, БЕЗ ЛІНІЇ КОДУ , потрібно лише перетягнути відповідний вихідний файл до проекту. IQKeyboardManager також підтримує орієнтацію на пристрої , автоматичне управління панеллю UITool , клавіатураDistanceFromTextField та багато іншого, ніж ви думаєте.

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

Ось схема управління потоком: Діаграма керування потоком

Шаг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вниз відповідно до порушеної відстані.

Step8: - конкретизований одноточечного IQKeyboardManager екземпляра класу на додатку навантаження, тому кожен UITextField/ UITextViewв додатку буде автоматично підлаштовуватися в залежності від очікуваного відстані переміщення.

Це все


Прийнята відповідь не працювала для мене. Але це робить.
учень

@ZaEeMZaFaR IQKeyboardManager також оптимізований для iPad. Чи можете ви, будь ласка, відкрити випуск про бібліотеку github repo та завантажити демонстраційний проект, що демонструє проблему з iPad?
Мохд Іфтехар Кураші

Дякуємо за відповідь Я зроблю це, якщо проблема не зникає, перш ніж коментувати, я думав, що IQKeyboardManager не для універсального пристрою.
ZaEeM ZaFaR

Досліджуючи більше, його якість працює з iPad на тренажері, але не на фактичному пристрої iPad. Працює чудово і на iPhone (Device + Simulator). Що може бути проблемою?
ZaEeM ZaFaR

Як я вже говорив, вам слід підняти проблему на github repo з демонстраційним проектом із деталями версії 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];
}

+1 для згадки UIApplicationDidEnterBackgroundNotification, інакше він рухатиметься вниз, якщо одним натиснути кнопку "Головна" і знову ввійде в додаток, що спричинить його некрасиво і неприємно.
Аділ Соомро

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
Мохд Іфтікар Qurashi

Ідея хороша, але властивість «contentInset» тут не допоможе, СОГ contentInset буде просто надати вам ефект утеплювача: stackoverflow.com/a/10049782/260665
Радж Паван 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 

1
Посилання розірвано. Будь ласка, розгляньте можливість включення самостійного рішення в майбутньому!
miek

5

Заціни. Жодних клопотів для вас.

Це рішення дуже акуратне. Все, що вам потрібно зробити, - це додати свої текстові поля в a UIScrollViewі змінити його клас на TPKeyboardAvoidingScollView, якщо ви використовуєте мовленнєві дошки. Погляд прокрутки розширюється таким чином, що він би визначав, коли клавіатура видно, і переміститься над клавіатурою на розумній відстані. Це ідеальне рішення, оскільки воно не залежить від вашого UIViewController. Кожна необхідна справа робиться в межах вищезгаданого класу. Дякую Майклу Тайсону та ін.

TPKeyboardAvoiding


@NANNAV - Будь ласка, надайте коментарі, коли ви вносите пропозиції щодо зміни відповіді.
Охорона безпеки

5

Нижче наведена швидка версія відповіді Amagrammer. Також варіант із використанням події 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 / editing-textfields-without-obscuring.html ). Він показує, як перемістити існуючий UIViewна а UIScrollViewта прокрутити його автоматично, коли з’явиться клавіатура.

Я трохи оновив його, щоб обчислити правильну висоту, UIScrollViewколи є елементи керування (наприклад, а UITabBar) нижче UIScrollBar. Див. Повідомлення про оновлення uiview .


2

Ось рішення за допомогою Xcode5, iOS7:

Використовуйте блоки UITextfieldDelegate та анімацію.

Це майже весь код для 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 був простим виходом.

Після перетягування рамки у свій проект включіть файл заголовка. Завантажте та відкрийте приклад проекту, а потім перетягніть об’єкт «Клавіатурний помічник» із розділу об’єктів у xib до розділу об’єктів у конструкторі інтерфейсу вашого проекту.

Перетягніть усі свої погляди, щоб бути дітьми "Клавіатури".


0

Перетягування рамки, яку я використовую в своїх проектах. Підтримує автоматичне звільнення під час натискання кнопки за межами першого відповіді або під час прокрутки.

GTKeyboardHelper


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як a UITextFieldDelegateі фактичне textFielddelegate .

(Завдяки Ammagrammer, це лише коротша відповідь, використовуючи блоки для анімації)


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 потрібно зателефонувати до центруOn: (CGRect) наступними методами:

textFieldDidBeginEditing: (UITextField *) з, як параметр, кадром текстового поля, на яке ви хочете централізувати .

І ви повинні викликати це у своєму обробнику подій, де ви закриєте клавіатуру,

textFieldDidEndEditing: (UITextField *) може бути одним із способів зробити це, поставивши INITIAL_VIEW як параметр centerOn: (CGRect) .


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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.