Перемещение UIView вверх, когда клавиатура появляется в iOS


У меня есть UIView, это не внутри UIScrollView. Я хотел бы переместить свой вид, когда появится клавиатура. Прежде чем я попытался использовать это решение:Как я могу сделать UITextField двигаться вверх, когда клавиатура присутствует?.

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

17 67

17 ответов:

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

//Declare a delegate, assign your textField to the delegate and then include these methods

-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
    return YES;
}


- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];

    [self.view endEditing:YES];
    return YES;
}


- (void)keyboardDidShow:(NSNotification *)notification
{
    // Assign new frame to your view 
    [self.view setFrame:CGRectMake(0,-110,320,460)]; //here taken -110 for example i.e. your view will be scrolled to -110. change its value according to your requirement.

}

-(void)keyboardDidHide:(NSNotification *)notification
{
    [self.view setFrame:CGRectMake(0,0,320,460)];
}
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

#pragma mark - keyboard movements
- (void)keyboardWillShow:(NSNotification *)notification
{
    CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    [UIView animateWithDuration:0.3 animations:^{
        CGRect f = self.view.frame;
        f.origin.y = -keyboardSize.height;
        self.view.frame = f;
    }];
}

-(void)keyboardWillHide:(NSNotification *)notification
{
    [UIView animateWithDuration:0.3 animations:^{
        CGRect f = self.view.frame;
        f.origin.y = 0.0f;
        self.view.frame = f;
    }];
}

Я написал небольшую категорию на UIView что управляет временно прокручивать вещи вокруг без необходимости обернуть все это в UIScrollView. Мое использование глагола "scroll" здесь, возможно, не идеально, потому что это может заставить вас думать, что есть представление прокрутки, и нет-мы просто анимируем позицию UIView (или UIView подкласс).

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

UIView + FormScroll.h:

#import <Foundation/Foundation.h>

@interface UIView (FormScroll) 

-(void)scrollToY:(float)y;
-(void)scrollToView:(UIView *)view;
-(void)scrollElement:(UIView *)view toPoint:(float)y;

@end

UIView + FormScroll.м:

#import "UIView+FormScroll.h"


@implementation UIView (FormScroll)


-(void)scrollToY:(float)y
{

    [UIView beginAnimations:@"registerScroll" context:NULL];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    [UIView setAnimationDuration:0.4];
    self.transform = CGAffineTransformMakeTranslation(0, y);
    [UIView commitAnimations];

}

-(void)scrollToView:(UIView *)view
{
    CGRect theFrame = view.frame;
    float y = theFrame.origin.y - 15;
    y -= (y/1.7);
    [self scrollToY:-y];
}


-(void)scrollElement:(UIView *)view toPoint:(float)y
{
    CGRect theFrame = view.frame;
    float orig_y = theFrame.origin.y;
    float diff = y - orig_y;
    if (diff < 0) {
        [self scrollToY:diff];
    }
    else {
        [self scrollToY:0];
    }

}

@end

импортируйте это в свой UIViewController, а затем вы можете сделать

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    [self.view scrollToView:textField];
}

-(void) textFieldDidEndEditing:(UITextField *)textField
{
    [self.view scrollToY:0];
    [textField resignFirstResponder];
}

...или еще что-нибудь. Эта категория дает вам три довольно хороших способа настроить положение вида.

привязка представления к клавиатуре также является опцией (см. GIF в нижней части ответа)

Swift 4

используйте расширение: (не был полностью протестирован)

extension UIView{
    func bindToKeyboard(){
        NotificationCenter.default.addObserver(self, selector: #selector(UIView.keyboardWillChange(notification:)), name: Notification.Name.UIKeyboardWillChangeFrame, object: nil)
    }

    func unbindToKeyboard(){
        NotificationCenter.default.removeObserver(self, name: Notification.Name.UIKeyboardWillChangeFrame, object: nil)
    }
    @objc
    func keyboardWillChange(notification: Notification) {
        let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
        let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt
        let curFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
        let targetFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        let deltaY = targetFrame.origin.y - curFrame.origin.y

        UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: {
            self.frame.origin.y+=deltaY

        },completion: nil)

    }
}

Swift 2 + 3

использовать расширение:

extension UIView{
    func bindToKeyboard(){
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(UIView.keyboardWillChange(_:)), name: UIKeyboardWillChangeFrameNotification, object: nil)
    }


    func keyboardWillChange(notification: NSNotification) {

        let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
        let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt
        let curFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).CGRectValue()
        let targetFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue()
        let deltaY = targetFrame.origin.y - curFrame.origin.y


        UIView.animateKeyframesWithDuration(duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: {
                self.frame.origin.y+=deltaY

        },completion: nil)

    }  
}

использование:

// view did load...
textField.bindToKeyboard()

...

// view unload
textField.unbindToKeyboard()

результат:
enter image description here

важно
Не забудьте удалить наблюдателя, когда вид разгрузка

нашел theDuncs ответ очень полезно и ниже вы можете найти мою собственную (рефакторинг) версию:


Основные Изменения

  1. теперь получаю размер клавиатуры динамически, а не жесткого кодирования значений
  2. извлек анимацию UIView в свой собственный метод предотвратить дублирование кода
  3. допустимая длительность, которая должна быть передана в метод, а не быть жестко закодировано

- (void)viewWillAppear:(BOOL)animated {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}

- (void)viewWillDisappear:(BOOL)animated {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

- (void)keyboardWillShow:(NSNotification *)notification {
    CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    float newVerticalPosition = -keyboardSize.height;

    [self moveFrameToVerticalPosition:newVerticalPosition forDuration:0.3f];
}


- (void)keyboardWillHide:(NSNotification *)notification {
    [self moveFrameToVerticalPosition:0.0f forDuration:0.3f];
}


- (void)moveFrameToVerticalPosition:(float)position forDuration:(float)duration {
    CGRect frame = self.view.frame;
    frame.origin.y = position;

    [UIView animateWithDuration:duration animations:^{
        self.view.frame = frame;
    }];
}

на основе решения Даниэля кром. Это версия в swift 3.0. Отлично работает с AutoLayout, и перемещает весь вид при появлении клавиатуры.

extension UIView {

    func bindToKeyboard(){
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
    }

    func unbindFromKeyboard(){
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
    }

    @objc
    func keyboardWillChange(notification: NSNotification) {

        guard let userInfo = notification.userInfo else { return }

        let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as! Double
        let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as! UInt
        let curFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
        let targetFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        let deltaY = targetFrame.origin.y - curFrame.origin.y

        UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: {
            self.frame.origin.y += deltaY
        })
    }  
}

как его использовать: Добавить на viewDidLoad как:

override func viewDidLoad() {
    super.viewDidLoad()

    view.bindToKeyboard()
}

и добавить unbindFromKeyboard функции деинициализации:

deinit {
    view.unbindFromKeyboard()
}

для всех проблем, связанных с клавиатурой просто используйте IQKeyBoardManager это полезно. https://github.com/hackiftekhar/IQKeyboardManager.

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

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


[[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector (keyboardDidHide:)
                                                 name: UIKeyboardDidHideNotification object:nil];

-(void) keyboardDidShow: (NSNotification *)notif
    {
        CGSize keyboardSize = [[[notif userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
        UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.height+[self getTableView].tableFooterView.frame.size.height, 0.0);

        [self getTableView].contentInset = contentInsets;
        [self getTableView].scrollIndicatorInsets = contentInsets;

        CGRect rect = self.frame; rect.size.height -= keyboardSize.height;
        if (!CGRectContainsPoint(rect, self.frame.origin))
        {
            CGPoint scrollPoint = CGPointMake(0.0, self.frame.origin.y - (keyboardSize.height - self.frame.size.height));
            [[self getTableView] setContentOffset:scrollPoint animated:YES];
        }
    }

-(void) keyboardDidHide: (NSNotification *)notif
{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    [self getTableView].contentInset = contentInsets;
    [self getTableView].scrollIndicatorInsets = contentInsets;
}

на основе ответ theDunc но написан на Swift с автозапуском.

@IBOutlet weak var bottomConstraint: NSLayoutConstraint! // connect the bottom of the view you want to move to the bottom layout guide

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    NSNotificationCenter.defaultCenter().addObserver(self,
                                                     selector: #selector(ConversationViewController.keyboardWillShow(_:)),
                                                     name: UIKeyboardWillShowNotification,
                                                     object: nil)

    NSNotificationCenter.defaultCenter().addObserver(self,
                                                     selector: #selector(ConversationViewController.keyboardWillHide(_:)),
                                                     name: UIKeyboardWillHideNotification,
                                                     object: nil)
}

override func viewWillDisappear(animated: Bool) {
    NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
    super.viewWillDisappear(animated)
}

// MARK: - Keyboard events

func keyboardWillShow(notification: NSNotification) {
    if let userInfo = notification.userInfo,
        keyboardFrame = userInfo[UIKeyboardFrameBeginUserInfoKey]
    {
        let keyboardSize = keyboardFrame.CGRectValue().size
        self.bottomConstraint.constant = keyboardSize.height
        UIView.animateWithDuration(0.3) {
            self.view.layoutIfNeeded()
        }
    }
}

func keyboardWillHide(notification: NSNotification) {
    self.bottomConstraint.constant = 0
    UIView.animateWithDuration(0.3) {
        self.view.layoutIfNeeded()
    }
}

я реализовал пользовательский контроллер, который динамически вычисляет размер клавиш, прокрутки, текстовые поля, когда он появляется и исчезает, даже при вращении устройства. Работает со всеми устройствами iOS. Просто наследовать контроллер, чтобы иметь то, что вам нужно. Вы можете найти его со всеми инструкциями по следующей ссылке:https://github.com/mikthebig/ios-textfield-scroll

простое решение без добавления уведомления наблюдателя

-(void)setViewMovedUp:(BOOL)movedUp
{
    [UIView beginAnimations:nil context:NULL];

    [UIView setAnimationDuration:0.3]; // if you want to slide up the view

    CGRect rect = self.view.frame;

    if (movedUp)
    {
        // 1. move the view's origin up so that the text field that will be hidden come above the keyboard
        // 2. increase the size of the view so that the area behind the keyboard is covered up.
        rect.origin.y -= kOFFSET_FOR_KEYBOARD;
        rect.size.height += kOFFSET_FOR_KEYBOARD;
    }
    else
    {
        // revert back to the normal state.
        rect.origin.y += kOFFSET_FOR_KEYBOARD;
        rect.size.height -= kOFFSET_FOR_KEYBOARD;
    }
    self.view.frame = rect;

    [UIView commitAnimations];
}


-(void)textFieldDidEndEditing:(UITextField *)sender
{
     if  (self.view.frame.origin.y >= 0)
        {
            [self setViewMovedUp:NO];
        }
}

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
        //move the main view, so that the keyboard does not hide it.
        if  (self.view.frame.origin.y >= 0)
        {
            [self setViewMovedUp:YES];
        }
}

здесь

#define kOFFSET_FOR_KEYBOARD 80.0

Swift 4

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange), name: .UIKeyboardWillChangeFrame, object: nil)

@objc func keyboardWillChange(notification: NSNotification) {

        let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
        let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt
        let curFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
        let targetFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        let deltaY = targetFrame.origin.y - curFrame.origin.y

        UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: {
            self.YourView.frame.origin.y+=deltaY
        },completion: nil)
    }

на всякий случай, если кто-то ищет решение в Свифт, бросьте это в свой код:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: .UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: .UIKeyboardWillHide, object: nil)
}

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

    NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
    NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
}


@objc func keyboardWillShow(notification: Notification) {
    if let userInfo = notification.userInfo {
        if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
            UIView.animate(withDuration: 0.3) {
                var alteredFrame = self.view.frame
                alteredFrame.origin.y = -keyboardSize.height
                self.view.frame = alteredFrame
            }
        }
    }
}

@objc func keyboardWillHide(notification: Notification) {
    UIView.animate(withDuration: 0.3) {
        var alteredFrame = self.view.frame
        alteredFrame.origin.y = 0.0
        self.view.frame = alteredFrame
    }
}

вот так. Я использовал этот код с UIView, хотя. Вы должны быть в состоянии сделать эти настройки для scrollview.

    func addKeyboardNotifications() {
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(keyboardWillShow(notification:)),
                                               name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(keyboardWillHide(notification:)),
                                               name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    }

    func keyboardWillShow(notification: NSNotification) {

        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
            let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
// if using constraints            
// bottomViewBottomSpaceConstraint.constant = keyboardSize.height
self.view.frame.origin.y -= keyboardSize.height
            UIView.animate(withDuration: duration) {
                self.view.layoutIfNeeded()
            }
        }
    }
    func keyboardWillHide(notification: NSNotification) {

        let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
//if using constraint
//        bottomViewBottomSpaceConstraint.constant = 0
self.view.frame.origin.y = 0
        UIView.animate(withDuration: duration) {
            self.view.layoutIfNeeded()
        }
    }

Не забудьте удалить уведомления в правильном месте.

func removeKeyboardNotifications() {
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

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

использование:

 self.keyboardHandler = [EDKeyboardHandler new];

  [self.keyboardHandler listenWithBlock:^(KeyboardInfo *model)
  {
    //adjust view positions according to keyboard position here
  }];

и KeyboardInfo модель имеет следующие свойства:

typedef enum : NSUInteger {
    KeyboardStatusDidShow,
    KeyboardStatusWillShow,
    KeyboardStatusDidHide,
    KeyboardStatusWillHide,
} KeyboardStatus;

@interface KeyboardInfo:NSObject

@property (nonatomic,readonly) NSTimeInterval animationDuration;
@property (nonatomic,readonly) CGRect keyboardFrame;
@property (nonatomic,readonly) NSInteger animationCurve;
@property (nonatomic,readonly) KeyboardStatus status;

@end

Регистрация GitHub проект для деталей и интеграции cocoaPods.

объявите делегат, назначьте ему текстовое поле, а затем включите эти методы.

предполагая, что у вас есть форма входа с текстовыми полями электронной почты и пароля, этот код идеально подходит:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    [self.emailTextField resignFirstResponder];
    [self.passwordTextField resignFirstResponder];

}

- (BOOL)textFieldShouldReturn:(UITextField *)textField {

    if (self.emailTextField == textField) {
        [self.passwordTextField becomeFirstResponder];
    } else {
        [self.emailTextField resignFirstResponder];
        [self.passwordTextField resignFirstResponder];
    }
    return NO;
}
- (void)viewWillAppear:(BOOL)animated {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}

- (void)viewWillDisappear:(BOOL)animated {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

#pragma mark - keyboard movements
- (void)keyboardWillShow:(NSNotification *)notification
{
    CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    [UIView animateWithDuration:0.3 animations:^{
        CGRect f = self.view.frame;
        f.origin.y = -0.5f * keyboardSize.height;
        self.view.frame = f;
    }];
}

-(void)keyboardWillHide:(NSNotification *)notification
{
    [UIView animateWithDuration:0.3 animations:^{
        CGRect f = self.view.frame;
        f.origin.y = 0.0f;
        self.view.frame = f;
    }];
}

Это можно сделать легко и автоматически если это textfield находится в ячейке таблицы (даже когда table.scrollable = NO).

Примечание это: положение и размер таблицы должны быть разумными. например:

  • если положение y таблицы составляет 100, считая от нижней части представления, то клавиатура высотой 300 будет перекрывать всю таблицу.
  • если таблицы height = 10 и textfield в нем должна быть прокручена до 100, когда клавиатура появляется в чтобы быть видимым, тогда это текстовое поле будет вне привязки таблицы.