Как я могу заставить 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 ]

0 голосов
/ 01 марта 2014

Я нашел @DK_ решением, которое я начал использовать. Тем не менее, существует предположение, что scrollView охватывает весь вид. Это был не тот случай для меня. Я только хотел scrollView на тот случай, если клавиатура закрыла нижнее текстовое поле на экране входа в систему. Таким образом, мое представление контента было такого же размера, как и мое представление прокрутки, которое было меньше основного представления.

Это также не учитывало пейзажи, из-за которых у меня начались проблемы. После игры с ним в течение нескольких дней, это мой keyboardWasShown: метод.

- (void)keyboardWasShown:(NSNotification*)aNotification
{
    // A lot of the inspiration for this code came from http://stackoverflow.com/a/4837510/594602
    CGFloat height = 0;
    NSDictionary* info = [aNotification userInfo];

    CGRect kbFrameRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    CGRect kbBoundsRect = [self.view convertRect:kbFrameRect fromView:nil]; // Convert frame from window to view coordinates.

    CGRect scrollRect = scrollView.frame;
    CGRect intersect = CGRectIntersection(kbBoundsRect, scrollRect);

    if (!CGRectIsNull(intersect))
    {
        height = intersect.size.height;
        UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, height, 0.0);
        scrollView.contentInset = contentInsets;
        scrollView.scrollIndicatorInsets = contentInsets;
    }

    // Figure out what the view rectangle is for the scrollView
    CGPoint contentOffset = scrollView.contentOffset;
    CGRect visibleRect = CGRectOffset(scrollRect, contentOffset.x, contentOffset.y);    // I'm not 100% sure if this is needed/right. My scrollView was always at the top in testing.
    visibleRect.size.height -= height;
    CGRect activeRect = activeField.frame;

    if (!CGRectContainsRect(visibleRect, activeRect))
    {
        [self.scrollView scrollRectToVisible:activeField.frame animated:YES];
    }
}

У меня также были некоторые трудности при работе с автоматическим макетом. Если у меня нет правильной раскладки, я не получаю ожидаемую прокрутку. Одна вещь, которая облегчила жизнь, состояла в том, чтобы поместить все элементы, которые должны были быть прокручены, в один вид, и поместил это как единственный элемент в виде прокрутки. Я назвал это единственное представление «представлением контента».

Я думаю, что ключевым моментом было то, что представление контента имело заданную ширину и высоту. Это позволило представлению прокрутки точно знать, сколько контента имело дело с. Это немного отходит от обычных макетов. Обычно представления пытаются занять как можно больше места. С содержимым представления прокрутки вы пытаетесь заставить представление максимально ограничиться. Представление содержимого позволяет вам положить этому конец. Поэтому я присвоил высоте 248 и использовал стандартную ширину экрана 320 в качестве ширины.

Схемы, которые в конечном итоге работали для меня, были такими:

  • Прокрутка от просмотра к суперпредставлению: в основном я дал ограничения сверху, слева и справа.
    • Horizontal Space - View - Scroll View (0)
    • Vertical Space - View - Scroll View (0)
    • Horizontal Space - Scroll View - View (0)
  • Высота просмотра прокрутки: я устанавливаю вид прокрутки с постоянной высотой. Я не знаю, было ли это действительно необходимо, но оно получило границы самого представления прокрутки.
    • Height - (248) - Scroll View
  • Представление содержимого для представления прокрутки: я дал константы со всех сторон, сверху, слева, снизу и справа.
    • Vertical Space - View - Scroll View (0)
    • Vertical Space - Scroll View - View (0)
    • Horizontal Space - View - Scroll View (0)
    • Horizontal Space - Scroll View - View (0)
  • Размеры представления содержимого.
    • Height - (248) - View
    • Width - (320) - View
0 голосов
/ 18 ноября 2016

Попробуйте, это работает отлично:

if Firstnametxt.text == "" || Passwordtxt.text == "" || emailtxt.text == ""
    {
        if Firstnametxt.text == ""
        {
            Firstnametxt!.shake(10, withDelta: 5, speed: 0.05, shakeDirection: ShakeDirection.Horizontal)
            Firstnametxt.becomeFirstResponder()
        }
        else if Passwordtxt.text == ""
        {
            Passwordtxt!.shake(10, withDelta: 5, speed: 0.05, shakeDirection: ShakeDirection.Horizontal)
            Passwordtxt.becomeFirstResponder()
        }
        else if emailtxt.text == ""

        {

            emailtxt!.shake(10, withDelta: 5, speed: 0.05, shakeDirection: ShakeDirection.Horizontal)
            emailtxt.becomeFirstResponder()
        }

    }
    else
    {
        let isValidEmail:Bool = self.isValidEmail(emailtxt.text!)
        if isValidEmail == true
        {
                        }
        else
        {
            emailtxt!.shake(10, withDelta: 5, speed: 0.05, shakeDirection: ShakeDirection.Horizontal)
            emailtxt.becomeFirstResponder()

        }

    }
0 голосов
/ 02 ноября 2016

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

P.S. Я не использую scrollview, просто перемещаю вверх, но он должен работать аналогично

func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
    if !keyboardIsShown{
        self.infoViewTopConstraint.constant -= keyboardSize.height
        self.infoViewBottomConstraint.constant += keyboardSize.height
        self.view.setNeedsLayout()
        self.view.layoutIfNeeded()
        keyboardIsShown = true
    }
}

func keyboardWillHide(notification: NSNotification) {
if keyboardIsShown {
    self.infoViewTopConstraint.constant += keyboardSize.height
    self.infoViewBottomConstraint.constant -= keyboardSize.height
    self.view.setNeedsLayout()
    self.view.layoutIfNeeded()
    keyboardIsShown = false
}
0 голосов
/ 17 января 2014

Я не видел этой возможности здесь, поэтому я добавляю ее, так как я попробовал методы в ответах, но спустя часы обнаружил, что в XCode 5 в iOS6 / 7 есть более простой способ намного : Используйте NSLayoutConstraints.

См .: Ограничение Autolayout - Клавиатура

Вот мой код:

.m файл:

// Called when the UIKeyboardWillShowNotification is sent.
- (void)keyboardWillBeShown:(NSNotification*)aNotification
{
    NSLog(@"keyboardWillBeShown:");
    [self.PhoneNumberLabelOutlet setHidden:TRUE];
    CGFloat heightOfLabel = self.PhoneNumberLabelOutlet.frame.size.height;
    for( NSLayoutConstraint* thisConstraint in self.topElementsVerticalDistanceFromTopLayoutConstraint ) {
        thisConstraint.constant -= heightOfLabel;
    }

    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    CGFloat oldConstant = [self.SignInYConstraint constant];
    self.SignInYConstraint.constant = oldConstant + kbSize.height;
    [self.view setNeedsUpdateConstraints];

    NSTimeInterval duration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    [UIView animateWithDuration:duration animations:^{
        [self.view layoutIfNeeded];
    }];

}

.h файл:

#import <UIKit/UIKit.h>

@interface SignInViewController : UIViewController {

    UITextField* _activeField;
}




- (void)signInCallback:(NSObject*)object;


@property (weak, nonatomic) IBOutlet UILabel *PhoneNumberLabelOutlet;

@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *ActivityIndicatorOutlet;

@property (weak, nonatomic) IBOutlet UITextField *UserIDTextfieldOutlet;

@property (weak, nonatomic) IBOutlet UITextField *PasswordTextfieldOutlet;

@property (weak, nonatomic) IBOutlet UIButton *SignInButton;

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

@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *topElementsVerticalDistanceFromTopLayoutConstraint;

@end
0 голосов
/ 22 октября 2016

В iOS перемещение клавиатуры вверх и возврат к текстовым полям в приложении немного сбивает с толку и требует реализации нескольких методов для того же. также вам нужно сделать делегат в textfield и позаботиться об этом. Код для него будет повторяться в каждом классе, где существуют текстовые поля.

Я предпочитаю использовать этот элемент управления Github.

IQKeyboard

В котором Нам не нужно ничего делать. - просто перетащите элемент управления в свой проект и соберите. - Он сделает все для вашего приложения.

Спасибо

Может быть, это будет полезно.

0 голосов
/ 24 мая 2018

Мы можем дать пользователю код для Swift 4.1

    let keyBoardSize = 80.0

    func keyboardWillShow() {

    if view.frame.origin.y >= 0 {
    viewMovedUp = true
     }
     else if view.frame.origin.y < 0 {
    viewMovedUp = false
   }
  }

func keyboardWillHide() {
 if view.frame.origin.y >= 0 {
    viewMovedUp = true
 }
 else if view.frame.origin.y < 0 {
    viewMovedUp = false
 }

}

func textFieldDidBeginEditing(_ textField: UITextField) {
   if sender.isEqual(mailTf) {
    //move the main view, so that the keyboard does not hide it.
    if view.frame.origin.y >= 0 {
        viewMovedUp = true
    }
  }
}

func setViewMovedUp(_ movedUp: Bool) {
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(0.3)
    // if you want to slide up the view
let rect: CGRect = view.frame
if movedUp {

    rect.origin.y -= keyBoardSize
    rect.size.height += keyBoardSize
}
else {
    // revert back to the normal state.
    rect.origin.y += keyBoardSize
    rect.size.height -= keyBoardSize
 }
 view.frame = rect
 UIView.commitAnimations()
}

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

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

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

NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
}
0 голосов
/ 28 марта 2017

Для Swift Developer, используя Swift 3, вот репо https://github.com/jamesrochabrun/KeyboardWillShow

import UIKit

class ViewController: UIViewController {

    //1 Create a view that will hold your TEXTFIELD
    let textField: UITextField = {
        let tf = UITextField()
        tf.translatesAutoresizingMaskIntoConstraints = false
        tf.layer.borderColor = UIColor.darkGray.cgColor
        tf.layer.borderWidth = 3.0
        return tf
    }()
    //2 global variable that will hold the bottom constraint on changes
    var textfieldBottomAnchor: NSLayoutConstraint?

    override func viewDidLoad() {
        super.viewDidLoad()
        //3 add the view to your controller
        view.addSubview(textField)
        textField.heightAnchor.constraint(equalToConstant: 80).isActive = true
        textField.widthAnchor.constraint(equalToConstant: view.frame.width).isActive = true
        textField.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        textfieldBottomAnchor = textField.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        textfieldBottomAnchor?.isActive = true

        setUpKeyBoardObservers()
    }
    //4 Use NSnotificationCenter to monitor the keyboard updates
    func setUpKeyBoardObservers() {
        NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    }

    //5 toggle the bottom layout global variable based on the keyboard's height
    func handleKeyboardWillShow(notification: NSNotification) {

        let keyboardFrame = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? CGRect
        if let keyboardFrame = keyboardFrame {
            textfieldBottomAnchor?.constant = -keyboardFrame.height
        }
        let keyboardDuration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? Double
        if let keyboardDuration = keyboardDuration {
            UIView.animate(withDuration: keyboardDuration, animations: {
                self.view.layoutIfNeeded()
            })
        }
    }

    func handleKeyboardWillHide(notification: NSNotification) {

        textfieldBottomAnchor?.constant = 0
        let keyboardDuration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? Double
        if let keyboardDuration = keyboardDuration {
            UIView.animate(withDuration: keyboardDuration, animations: {
                self.view.layoutIfNeeded()
            })
        }
    }
    //6 remove the observers
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)

        NotificationCenter.default.removeObserver(self)
    }
}
0 голосов
/ 12 сентября 2018

Добавьте мои 5 центов:)

Я всегда предпочитаю использовать tableView для inputTextField или scrollView. В сочетании с уведомлениями вы можете легко управлять таким поведением. (Обратите внимание, если вы используете статические ячейки в tableView, такое поведение будет работать автоматически).

// MARK: - Notifications
fileprivate func registerNotificaitions() {
    NotificationCenter.default.addObserver(self, selector: #selector(AddRemoteControlViewController.keyboardWillAppear(_:)),
                                           name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(AddRemoteControlViewController.keyboardWillDisappear),
                                           name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

fileprivate func unregisterNotifications() {
    NotificationCenter.default.removeObserver(self)
}

@objc fileprivate func keyboardWillAppear(_ notification: Notification) {
    if let keyboardHeight = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height {
        view.layoutIfNeeded()
        UIView.animate(withDuration: 0.3, animations: {
            let heightInset = keyboardHeight - self.addDeviceButton.frame.height
            self.tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: heightInset, right: 0)
            self.view.layoutIfNeeded()
        }, completion: nil)
    }
}

@objc fileprivate func keyboardWillDisappear() {
    view.layoutIfNeeded()
    UIView.animate(withDuration: 0.3, animations: {
        self.tableView.contentInset = UIEdgeInsets.zero
        self.view.layoutIfNeeded()
    }, completion: nil)
}
0 голосов
/ 22 января 2015

Прежде всего, я рекомендую лучший дизайн для вашей страницы, чтобы не было необходимости прокручивать ваш вид. Если у вас много текстовых полей, вам все равно не нужно использовать ScrollView, он просто усложняет задачу. Вы можете просто добавить контейнер UIView в верхней части исходного представления контроллера, а затем поместить эти текстовые поля в это представление. Когда клавиатура отображается или исчезает, просто используйте анимацию, чтобы переместить это представление контейнера.

0 голосов
/ 14 февраля 2015

Я использую Swift и автоматическое размещение (но не могу комментировать предыдущий ответ Swift); вот как я делаю это без просмотра прокрутки:

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

Это можно вызвать, используя любой из обычных методов textFieldDidBeginEditing или keyboardWillShow.

func setFormHeight(top: CGFloat)
{
    let height = UIScreen.mainScreen().bounds.size.height

    // restore text input fields for iPhone 4/4s
    if (height < 568) {
        UIView.animateWithDuration(0.2, delay: 0.0, options: nil, animations: {
            self.topSpaceForFormConstraint.constant = top
            self.view.layoutIfNeeded()
            }, completion: nil)
    }

}
...