Как я могу заставить UITextField двигаться вверх при наличии клавиатуры - при начале редактирования? - PullRequest
1644 голосов
/ 14 июля 2009

С iOS SDK:

У меня есть UIView с UITextField s, которые вызывают клавиатуру. Мне нужно, чтобы я мог:

  1. Разрешить прокрутку содержимого UIScrollView, чтобы увидеть другие текстовые поля после вызова клавиатуры

  2. Автоматически «прыгать» (путем прокрутки вверх) или сокращения

Я знаю, что мне нужен UIScrollView. Я попытался изменить класс моего UIView на UIScrollView, но я все еще не могу прокрутить текстовые поля вверх или вниз.

Мне нужны и UIView, и UIScrollView? Один входит в другой?

Что необходимо реализовать для автоматической прокрутки к активному текстовому полю?

В идеале, максимально возможная настройка компонентов будет выполнена в Интерфейсном Разработчике. Я хотел бы написать код только для того, что ему нужно.

Примечание: UIView (или UIScrollView), с которым я работаю, вызывается панелью вкладок (UITabBar), которая должна функционировать как обычно.


Редактировать: я добавляю полосу прокрутки только для того, когда клавиатура появляется. Хотя это и не нужно, я чувствую, что он обеспечивает лучший интерфейс, потому что тогда пользователь может, например, прокручивать и изменять текстовые поля.

У меня это работает, когда я меняю размер кадра UIScrollView, когда клавиатура поднимается и опускается. Я просто использую:

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

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

Однако это автоматически не «сдвигает» и не центрирует нижние текстовые поля в видимой области, что мне бы очень хотелось.

Ответы [ 91 ]

7 голосов
/ 20 июля 2012

Попробуйте это:

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
    if ([sender isEqual:self.m_Sp_Contact])
    {
        [self.m_Scroller setContentOffset:CGPointMake(0, 105)animated:YES];          
    }
}
7 голосов
/ 16 февраля 2016

Swift 2.0:

Добавьте UIScrollView и добавьте textFields поверх него. Сделайте ссылки на раскадровку VC.

@IBOutlet weak var username: UITextField!
@IBOutlet weak var password: UITextField!
@IBOutlet weak var scrollView: UIScrollView!

Добавьте эти методы: UITextFieldDelegate & UIScrollViewDelegate.

//MARK:- TEXTFIELD METHODS
    func textFieldShouldReturn(textField: UITextField) -> Bool {

        if(username.returnKeyType == UIReturnKeyType.Default) {
            password.becomeFirstResponder()
        }
        textField.resignFirstResponder()
        return true
    }
    func textFieldDidBeginEditing(textField: UITextField) {

        dispatch_async(dispatch_get_main_queue()) {

            let scrollPoint:CGPoint = CGPointMake(0,textField.frame.origin.y/4)
            self.scrollView!.setContentOffset(scrollPoint, animated: true);
        }
    }
    func textFieldShouldEndEditing(textField: UITextField) -> Bool {

        dispatch_async(dispatch_get_main_queue()) {
          UIView.animateWithDuration(0, animations: { self.scrollView!.setContentOffset(CGPointZero,animated: true) })
        }
        return true
    }
    override func touchesBegan(touches: Set<UITouch>,
        withEvent event: UIEvent?) {
            self.view.endEditing(true)
    }
    func scrollViewWillBeginDragging(scrollView: UIScrollView) {
        self.scrollView.scrollEnabled =  true

        dispatch_async(dispatch_get_main_queue()) {
            UIView.animateWithDuration(0, animations: { self.scrollView!.setContentOffset(CGPointZero,animated: true)

            })
        }
    }
7 голосов
/ 11 января 2012

Вам необходимо добавить прокрутку программно с определенным размером кадра. Вы должны добавить UIScrollViewDelegate в файл .h. Вы должны включить scrollview, для чего вам нужно написать следующее в viewDidLoad ().

scrollview.scrollEnabled=YES;
scrollview.delegate=self;

scrollview.frame = CGRectMake(x,y,width,height);
//---set the content size of the scroll view--- 

[scrollview setContentSize:CGSizeMake(height,width)];

Таким образом, вы можете добавить свои значения x, y, width и height. Я думаю, что это поможет вам.

6 голосов
/ 17 декабря 2012

Вот бесплатная библиотека для работы с клавиатурой Keyboard-Handling-in-iPhone-Applications . Вам нужно написать только одну строку кода:

[AutoScroller addAutoScrollTo:scrollView];

Замечательно работать с клавиатурой в формах

6 голосов
/ 17 марта 2016

Я думаю, что лучший подход - это использовать протоколно-ориентированное программирование, если вы используете Swift.

Прежде всего вы должны создать протокол KeyboardCapable, который дает любому UIViewController, который ему соответствует, возможность регистрировать и отменять регистрацию наблюдателей с клавиатуры:

import Foundation
import UIKit

protocol KeyboardCapable: KeyboardAnimatable {
    func keyboardWillShow(notification: NSNotification)
    func keyboardWillHide(notification: NSNotification)
}

extension KeyboardCapable where Self: UIViewController {
    func registerKeyboardNotifications() {
        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil)
    }

    func unregisterKeyboardNotifications() {
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
    }
}

Вы заметили постороннее ключевое слово KeyboardAnimatable в приведенном выше фрагменте кода. Это просто название следующего протокола, который нам нужно создать:

import Foundation
import UIKit

protocol KeyboardAnimatable {

}

extension KeyboardAnimatable where Self: UIViewController {
    func performKeyboardShowFullViewAnimation(withKeyboardHeight height: CGFloat, andDuration duration: NSTimeInterval) {
        UIView.animateWithDuration(duration, animations: { () -> Void in
            self.view.frame = CGRectMake(view.frame.origin.x, -height, view.bounds.width, view.bounds.height)
            }, completion: nil)
    }

    func performKeyboardHideFullViewAnimation(withDuration duration: NSTimeInterval) {
        UIView.animateWithDuration(duration, animations: { () -> Void in
            self.view.frame = CGRectMake(view.frame.origin.x, 0.0, view.bounds.width, view.bounds.height)
            }, completion: nil)
    }
}

Этот протокол KeyboardAnimatable предоставляет всем UIViewController, соответствующим ему два метода, которые будут анимировать весь вид вверх и вниз соответственно.

Хорошо, поэтому, если KeyboardCapable соответствует KeyboardAnimatable, все UIViewController, соответствующие KeyboardCapable, также соответствуют KeyboardAnimatable. Это круто.

Давайте посмотрим, как UIViewController соответствует KeyboardCapable и реагирует на события клавиатуры:

import Foundation
import UIKit

class TransferConfirmViewController: UIViewController, KeyboardCapable {
    //MARK: - LIFE CYCLE       
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)

        registerKeyboardNotifications()
    }

    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)

        unregisterKeyboardNotifications()
    }

    //MARK: - NOTIFICATIONS
    //MARK: Keyboard
    func keyboardWillShow(notification: NSNotification) {
        let keyboardHeight = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue().height
        let animationDuration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
        performKeyboardShowFullViewAnimation(withKeyboardHeight: keyboardHeight, andDuration: animationDuration)
    }

    func keyboardWillHide(notification: NSNotification) {
        let animationDuration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
        performKeyboardHideFullViewAnimation(withDuration: animationDuration)
    }
}

Теперь ваш UIViewController будет реагировать на события клавиатуры и, как следствие, анимироваться.

Примечание. Если вы хотите использовать пользовательскую анимацию вместо просмотра или вытягивания изображения, вы должны определить пользовательские методы в протоколе KeyboardAnimatable или выполнять их в функциях KeyboardCapable. Это зависит от вас.

6 голосов
/ 06 марта 2017

Просто добавьте это в ваш файл pod -> pod 'IQKeyboardManager'

Это все, обрабатывает всю клавиатуру, прокрутки и все прочее!

Вам не нужно ничего кодировать, не может найти лучшего решения!

Имеет расширение, которое обрабатывает отображение текстовых полей, смещение экрана, стрелки вперед и назад при наличии нескольких текстовых полей.

Имеется также кнопка «Сделано на заказ», которую можно удалить.

Ссылка -> https://github.com/hackiftekhar/IQKeyboardManager

5 голосов
/ 30 января 2012

Гораздо более элегантное решение - использовать подкласс UIView (хотя это не всегда уместно) и пересчитать все ваши подпредставления при смене кадра у родителя (и быть умным: пересчитывать их только при новом размере кадра изменилось, т. е. используйте CGRectEqualToRect для сравнения нового кадра при переопределении setFrame и ДО того, как вы вызовете [super setFrame:frame_]). Единственный улов - это то, что UIViewController, который вы намереваетесь использовать, вероятно, должен прослушивать события клавиатуры (или, вы можете сделать это в самом UIView, для удобной инкапсуляции). Но только UIKeyboardWillShowNotification и UIKeyboardWillHideNotification. Это так, чтобы все выглядело гладко (если вы подождете, пока CG позвонит, вы получите момент прерывистости).

Это имеет преимущество в создании подкласса UIView, который в любом случае делает правильные вещи.

Наивной реализацией было бы переопределение drawRect: (не), лучше было бы просто использовать layoutSubviews (а затем в UIViewController, или что-то еще, что вы можете вызвать [view setNeedsLayout] в ОДНОМ методе, который вызывается либо для показа, либо для скрытия).

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

Не задавайте что-то подобное жестко, если нет другого решения. ОС дает вам достаточно информации, если вы все сделали правильно, что вам просто нужно разумно перерисовать (в зависимости от вашего нового размера frame). Это намного чище и то, как вы должны делать что-то. (Хотя может быть и лучший подход.)

Приветствие.

5 голосов
/ 09 октября 2014

Вот моя версия с использованием autolayout:

Идея состоит в том, чтобы просто встроить ваше представление, содержащее текстовые поля / текстовое представление, в UIScrollView, установить ограничение снизу к его суперпредставлению, создать выход и обновить его константу в соответствии с высотой клавиатуры с помощью уведомлений. Это основано на примере Apple здесь и технической заметке Apple по UIScrollView с использованием AutoLayout здесь .

1) Вставьте ваш View V в UIScrollView S: Если вы уже установили свои константы и подпредставления, вы можете скопировать / вставить свое представление и подпредставления в представление ViewController, затем внедрить его с помощью меню «Редактор -> встроить» и, наконец, удалить скопированные представления.)

2) Установите следующие ограничения:

  • S к началу страницы: 0
  • S к нижнему указателю макета: 0
  • S ведет к супервизору: 0
  • S трейлинг к суперпредставлению: 0

  • V верхнее пространство для просмотра: 0

  • V нижнее пространство для просмотра: 0
  • V в конце для просмотра: 0
  • V ведущий пробел к супервизору: 0

  • V равна ширине S

  • Последнее нижнее V подпредставление к суперпредставлению: 20

3) Создайте выход из последнего ограничения для вашего контроллера представления

4) Используйте следующий код:

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomSpaceToContentView;

// ...

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    // ...

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

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

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Handle keyboard

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

    self.bottomSpaceToContentView.constant = kBottomMargin + kbSize.height;
    [self.view layoutIfNeeded];
}

// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
    self.bottomSpaceToContentView.constant = kBottomMargin;
    [self.view layoutIfNeeded];
}

И тадааааа, все работает!

5 голосов
/ 30 марта 2016

Это очень просто, просто вставьте следующий код в свой класс и настройте, если вам нужно.

-(void)textFieldDidBeginEditing:(UITextField *)textField {
     //Show Keyboard
     self.view.frame = CGRectMake(self.view.frame.origin.x,
                              self.view.frame.origin.y-50,
                              self.view.frame.size.width,
                              self.view.frame.size.height);   
}

-(void)textFieldDidEndEditing:(UITextField *)textField {
     // Hide keyboard
     self.view.frame = CGRectMake(self.view.frame.origin.x,
                              self.view.frame.origin.y+50,
                              self.view.frame.size.width,
                              self.view.frame.size.height); 
}
4 голосов
/ 14 марта 2014

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

#import "UIView+avoidKeyboard.h"
#import "AppDelegate.h"

@implementation UIView (avoidKeyboard)

- (void) becomeFirstResponder {

if(self.isFirstResponder)
    return;

[super becomeFirstResponder];

if ([self isKindOfClass:[UISearchBar class]] ||
    [self isKindOfClass:[UITextField class]] ||
    [self isKindOfClass:[UITextView class]])
{
    AppDelegate *appDelegate    = [UIApplication sharedApplication].delegate;

    CGRect screenBounds         = appDelegate.window.frame;

    CGFloat keyboardHeight;
    CGFloat keyboardY;
    CGFloat viewsLowestY;
    CGPoint origin              = [self.superview convertPoint:self.frame.origin toView:appDelegate.window]; //get this views origin in terms of the main screens bounds

    if(UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])){ //the window.frame doesnt take its orientation into account so if its sideways we must use the x value of the origin instead of the y
        keyboardHeight          = 216;
        keyboardY               = screenBounds.size.height  - keyboardHeight; //find the keyboards y coord relative to how much the main window has moved up
        viewsLowestY            = origin.y + self.frame.size.height; //find the lowest point of this view
    }
    else {
        keyboardHeight          = 162;
        keyboardY               = screenBounds.size.width  - keyboardHeight;
        viewsLowestY            = origin.x + self.frame.size.height;
    }

    CGFloat difference          = viewsLowestY - keyboardY + 20; //find if this view overlaps with the keyboard with some padding

    if (difference > 0){ //move screen up if there is an overlap

        [UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{

            CGRect frame = appDelegate.window.frame;

            if(UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])){
                frame.origin.y -= difference;
            }
            else {
                frame.origin.x -= difference;
            }
            appDelegate.window.frame = frame;
        }
        completion:nil];
    }
}
}

//look at appDelegate to see when the keyboard is hidden

@end

В приложении приложите эту функцию

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardHides:) name:UIKeyboardWillHideNotification object:nil]; //add in didFinishLaunchingWithOptions

...

- (void)keyboardHides:(NSNotification *)notification
{
    [UIView animateWithDuration:0.3 animations:^{
        [window setFrame: CGRectMake(0, 0, window.frame.size.width, window.frame.size.height)];
    } completion:nil];
}
...