Переместить текстовое поле, когда клавиатура появляется быстро

217

Я использую Swift для программирования на iOS и использую этот код для перемещения UITextField, но он не работает. Я keyboardWillShowправильно вызываю функцию , но текстовое поле не двигается. Я использую autolayout.

override func viewDidLoad() {
    super.viewDidLoad()
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil);
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil);
}

deinit {
    NSNotificationCenter.defaultCenter().removeObserver(self);
}

func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        //let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)

        var frame = self.ChatField.frame
        frame.origin.y = frame.origin.y - keyboardSize.height + 167
        self.chatField.frame = frame
        println("asdasd")
    }
}
Педро Манфреди
источник
2
Пошаговое руководство с файлами проекта: codebeaulieu.com/43/…
Дэн Болье,
Возможно, deinit и viewDidLoad не сбалансированы.
Рикардо
На основе как документов Apple, так и личного опыта. Вот мое git-репо с использованием UIScrollView для перемещения TF: github.com/29satnam/MoveTextFieldWhenKeyboardAppearsSwift
Codetard

Ответы:

317

Есть несколько улучшений в существующих ответах.

Во-первых, UIKeyboardWillChangeFrameNotification , вероятно, является лучшим уведомлением, поскольку оно обрабатывает изменения, которые не просто отображают / скрывают, но изменяются из-за изменений клавиатуры (язык, использование сторонних клавиатур и т. Д.) И поворотов (но обратите внимание на комментарий ниже, указывающий, что клавиатура будет скрыта, следует также обрабатывается для поддержки аппаратного подключения клавиатуры).

Во-вторых, параметры анимации могут быть извлечены из уведомления, чтобы гарантировать, что анимации правильно вместе.

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

Свифт 3

class MyViewController: UIViewController {

// This constraint ties an element at zero points from the bottom layout guide
@IBOutlet var keyboardHeightLayoutConstraint: NSLayoutConstraint?

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self,
        selector: #selector(self.keyboardNotification(notification:)),
        name: NSNotification.Name.UIKeyboardWillChangeFrame,
        object: nil)
}

deinit {
    NotificationCenter.default.removeObserver(self)
}

@objc func keyboardNotification(notification: NSNotification) {
    if let userInfo = notification.userInfo {
        let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
        let endFrameY = endFrame.origin.y ?? 0
        let duration:TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
        let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
        let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIViewAnimationOptions.curveEaseInOut.rawValue
        let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
        if endFrameY >= UIScreen.main.bounds.size.height {
            self.keyboardHeightLayoutConstraint?.constant = 0.0
        } else {
            self.keyboardHeightLayoutConstraint?.constant = endFrame?.size.height ?? 0.0
        }
        UIView.animate(withDuration: duration,
                                   delay: TimeInterval(0),
                                   options: animationCurve,
                                   animations: { self.view.layoutIfNeeded() },
                                   completion: nil)
    }
}

(Отредактировано, чтобы учесть анимацию клавиатуры вне экрана вместо усадки, как описано в потрясающем комментарии @ Gabox ниже)

Swift 5

class MyViewController: UIViewController {

// This constraint ties an element at zero points from the bottom layout guide
@IBOutlet var keyboardHeightLayoutConstraint: NSLayoutConstraint?

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self,
        selector: #selector(self.keyboardNotification(notification:)),
        name: UIResponder.keyboardWillChangeFrameNotification,
        object: nil)
}

deinit {
    NotificationCenter.default.removeObserver(self)
}

@objc func keyboardNotification(notification: NSNotification) {
    if let userInfo = notification.userInfo {
        let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
        let endFrameY = endFrame?.origin.y ?? 0
        let duration:TimeInterval = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
        let animationCurveRawNSN = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber
        let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
        let animationCurve:UIView.AnimationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw)
        if endFrameY >= UIScreen.main.bounds.size.height {
            self.keyboardHeightLayoutConstraint?.constant = 0.0
        } else {
            self.keyboardHeightLayoutConstraint?.constant = endFrame?.size.height ?? 0.0
        }
        UIView.animate(withDuration: duration,
                                   delay: TimeInterval(0),
                                   options: animationCurve,
                                   animations: { self.view.layoutIfNeeded() },
                                   completion: nil)
    }
}
Джозеф лорд
источник
1
@JosephLord хорошо. но я обнаружил, что это не работает, когда клавиатура прячется, потому что endFrame?.size.heightне ноль. Я получил конец кадра как UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 1024}, {768, 264}}";. Запустил на iOS 8.3 iPad Симулятор, Портрет. Xcode6.3 beta4.
Хлунг
8
если клавиатура не скрывается, попробуйте использовать этот код, если endFrame? .origin.y> = UIScreen.mainScreen (). bounds.size.height {self.keyboardHeightLayoutConstraint? .constant = 0.0} else {self.keyboardHeightLayoutConstraint? .constant = endFrame.size.height}
Габриэль Гонсалвес
3
keyBoardHeightLayoutConstraint - это ограничение, определенное в InterfaceBuilder, ограничивающее нижнюю часть представления, которую вы хотите переместить / сжать, до нижнего руководства по макету или нижней части основного представления для контроллера представления. Константа изначально установлена ​​на ноль и будет скорректирована, чтобы освободить место для клавиатуры при появлении или изменении размера клавиатуры.
Джозеф Лорд
2
Обратите внимание, что .UIKeyboardWillChangeFrameне срабатывает при подключении аппаратной клавиатуры, даже если клавиатура iOS исчезает. Вы должны также наблюдать, .UIKeyboardWillHideчтобы поймать этот крайний случай.
Jamesk
1
@Sulthan работает хорошо .. Моя проблема в том, что становится немного выше, чем клавиатура. Есть ли способ, как мы можем это исправить?
Павлос
128

Если вы используете Auto Layout, я предполагаю, что вы установили ограничение Bottom Space в Superview . Если это так, вам просто нужно обновить значение ограничения. Вот как вы делаете это с небольшой анимацией.

func keyboardWasShown(notification: NSNotification) {
    let info = notification.userInfo!
    let keyboardFrame: CGRect = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue()

    UIView.animateWithDuration(0.1, animations: { () -> Void in
        self.bottomConstraint.constant = keyboardFrame.size.height + 20
    })
}

Жесткий код 20 добавляется только для того, чтобы немного вытолкнуть текстовое поле над клавиатурой. В противном случае верхнее поле клавиатуры и нижнее поле текстового поля будут соприкасаться.

Когда клавиатура отклонена, сбросьте значение ограничения до его первоначального значения.

Isuru
источник
1
Можете ли вы объяснить мне, пожалуйста, как я это определяю? Спасибо! я всегда контролирую все на раскадровке
Педро Манфреди
4
bottomConstraint - это имя, которое я дал ограничению. Я выбрал константу, перетащил и создал для нее IBOutlet и дал это имя. Вы можете создавать IBOutlets для ограничений так же, как и для других элементов пользовательского интерфейса, таких как кнопки и текстовые поля.
Исуру
2
Этот ответ работал отлично, за исключением анимации, которая произошла сразу для меня. Проверьте, как мне анимировать изменения ограничений? как правильно анимировать.
Адам Джонс
2
@ vinbhai4u Вы должны зарегистрироваться для получения UIKeyboardWillShowNotificationуведомления. Посмотрите на код в вопросе ОП.
Исуру
8
@AdamJohns Чтобы оживить изменение ограничения, обновите константу вне и animateWithDurationи вызовите self.view.layoutIfNeeded()внутри блока animate.
Макс
111

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

override func viewDidLoad() {
   super.viewDidLoad()        
   NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil);
   NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil);
}

@objc func keyboardWillShow(sender: NSNotification) {
     self.view.frame.origin.y = -150 // Move view 150 points upward 
}

@objc func keyboardWillHide(sender: NSNotification) {
     self.view.frame.origin.y = 0 // Move view to original position  
}

Свифт 5:

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(sender:)), name: UIResponder.keyboardWillShowNotification, object: nil);

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(sender:)), name: UIResponder.keyboardWillHideNotification, object: nil);
Сакиб Омер
источник
4
Мне нравится это простое решение. Но я добавил клавиатуру с логическим отображением, так как у меня есть перемещение, кроме одного текстового поля. Я перемещаю клавиатуру только один раз, пока она показывает. Спасибо.
Кен
2
вместо перемещения представления переместите textView
ericgu
1
Это сохранит минус значение y представления, если пользователь переключит язык ввода.
Джеффри Нео
вместо того, чтобы изменить self.view я сделал значение self.myConstraint, это работает, но дело в том, что представление (к которому применяется ограничение) продолжает двигаться вверх. кто-нибудь сталкивался с этой проблемой?
Саши
5
Вместо использования self.view.frame.origin.y -= 150использовать self.view.frame.origin.y = -150и вместо self.view.frame.origin.y += 150использования self.view.frame.origin.y = 0. Это предотвращает перемещение вида 150 каждый раз при касании нового поля.
Gunwin
44

Для перемещения вашего вида во время редактирования текстового поля попробуйте это, я применил это,

Вариант 1: - ** ** Обновление в Swift 5.0 и iPhone X, XR, XS и XS Max Move с использованием NotificationCenter

  • Зарегистрировать это уведомление в func viewWillAppear(_ animated: Bool)

  • Отменить регистрацию этого уведомления в func viewWillDisappear(_ animated: Bool)

Примечание: - Если вы не отмените регистрацию, это вызовет дочерний класс и станет причиной сбоя или чего-либо еще.

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    NotificationCenter.default.addObserver( self, selector: #selector(keyboardWillShow(notification:)), name:  UIResponder.keyboardWillShowNotification, object: nil )
}
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
}

@objc func keyboardWillShow( notification: Notification) {
    if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
        var newHeight: CGFloat
        let duration:TimeInterval = (notification.userInfo![UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
        let animationCurveRawNSN = notification.userInfo![UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber
        let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
        let animationCurve:UIView.AnimationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw)
        if #available(iOS 11.0, *) {
            newHeight = keyboardFrame.cgRectValue.height - self.view.safeAreaInsets.bottom
        } else {
            newHeight = keyboardFrame.cgRectValue.height
        }
        let keyboardHeight = newHeight  + 10 // **10 is bottom margin of View**  and **this newHeight will be keyboard height**
        UIView.animate(withDuration: duration,
                       delay: TimeInterval(0),
                       options: animationCurve,
                       animations: {
                        self.view.textViewBottomConstraint.constant = keyboardHeight **//Here you can manage your view constraints for animated show**
                        self.view.layoutIfNeeded() },
                       completion: nil)
    }
}

Вариант 2: - Хорошо работает

func textFieldDidBeginEditing(textField: UITextField) {
        self.animateViewMoving(up: true, moveValue: 100)
}
func textFieldDidEndEditing(textField: UITextField) {
        self.animateViewMoving(up: false, moveValue: 100)
}

func animateViewMoving (up:Bool, moveValue :CGFloat){
    var movementDuration:NSTimeInterval = 0.3
    var movement:CGFloat = ( up ? -moveValue : moveValue)
    UIView.beginAnimations( "animateView", context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)
    UIView.setAnimationDuration(movementDuration )
    self.view.frame = CGRectOffset(self.view.frame, 0,  movement)
    UIView.commitAnimations()
}

Я получил этот ответ из этого источника UITextField двигаться вверх, когда клавиатура появляется в Swift

В Свифте 4 ---

func textFieldDidBeginEditing(_ textField: UITextField) {
        animateViewMoving(up: true, moveValue: 100)
    }

    func textFieldDidEndEditing(_ textField: UITextField) {
        animateViewMoving(up: false, moveValue: 100)
    }
    func animateViewMoving (up:Bool, moveValue :CGFloat){
        let movementDuration:TimeInterval = 0.3
        let movement:CGFloat = ( up ? -moveValue : moveValue)
        UIView.beginAnimations( "animateView", context: nil)
        UIView.setAnimationBeginsFromCurrentState(true)
        UIView.setAnimationDuration(movementDuration ) 
        self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
        UIView.commitAnimations()
    }
Jogendra.Com
источник
1
@ Jogendra.Com, Спасибо за ваш тяжелый труд. Но лучше всего он работает на iPhone 5,4 и 6. Но как я могу отключить его на iPhone 6plus и iPad (более высоких)
Тиха Аунг
Если вы используете вариант 1, обязательно добавьте ограничение IBOutlet. Вы создадите ограничение, которое хотите изменить, используя Auto-layout, затем перетащите его в viewcontroller, чтобы создать IBOutlet, который я называю self.iboutletConstraint.constant в функции animate. Также это не приводит к повторной настройке выхода при сокрытии клавиатуры, я справился с этим, сбросив ограничение до его первоначального значения.
Хаммад Тарик
21

Я люблю чистый код Swift. Итак, вот самый трудный код, который я мог придумать, чтобы перемещать текстовое представление вверх / вниз с помощью клавиатуры. В настоящее время он работает в производственном приложении iOS8 / 9 Swift 2.

ОБНОВЛЕНИЕ (март 2016): я просто максимально сжал свой предыдущий код. Также есть множество популярных ответов, которые жестко задают высоту клавиатуры и параметры анимации. В этом нет необходимости, не говоря уже о том, что числа в этих ответах не всегда совпадают с фактическими значениями, которые я вижу на моем 6s + iOS9 (высота клавиатуры 226, продолжительность 0,25 и кривая анимации 7). В любом случае, почти нет лишнего кода, чтобы получить эти значения прямо из системы. Увидеть ниже.

override func viewDidLoad() {
    super.viewDidLoad()

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "animateWithKeyboard:", name: UIKeyboardWillShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "animateWithKeyboard:", name: UIKeyboardWillHideNotification, object: nil)
}

func animateWithKeyboard(notification: NSNotification) {

    // Based on both Apple's docs and personal experience, 
    // I assume userInfo and its documented keys are available.
    // If you'd like, you can remove the forced unwrapping and add your own default values.

    let userInfo = notification.userInfo!
    let keyboardHeight = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue().height
    let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as! Double
    let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as! UInt
    let moveUp = (notification.name == UIKeyboardWillShowNotification)

    // baseContraint is your Auto Layout constraint that pins the
    // text view to the bottom of the superview.

    baseConstraint.constant = moveUp ? -keyboardHeight : 0

    let options = UIViewAnimationOptions(rawValue: curve << 16)
    UIView.animateWithDuration(duration, delay: 0, options: options,
        animations: {
            self.view.layoutIfNeeded()
        },
        completion: nil
    )

}

ПРИМЕЧАНИЕ. Этот код охватывает большинство комментариев / общих случаев. Тем не менее, может потребоваться больше кода для обработки различных ориентаций и / или пользовательских клавиатур. Вот углубленная статья о работе с клавиатурой iOS. Если вам нужно разобраться с каждым сценарием, это может помочь.

scootermg
источник
Кажется, это Swift 1.1, и я думаю, что не будет компилироваться в Swift 1.2, потому что он использует asдля принудительного приведения. as!может сработать, но, как вы можете видеть в других местах на этой странице, я избегаю принудительного применения и принудительного разворачивания себя.
Джозеф Лорд
Компилируется сейчас в Swift 1.2. И я добавил комментарий к коду re: принудительное развертывание. Приветствия.
scootermg
К сожалению. Я имел в виду Swift 2.
scootermg
В зависимости от того, как вы связали свой, baseConstraintэто может быть baseConstraint.constant = moveUp ? keyboardHeight : 0вместо baseConstraint.constant = moveUp ? -keyboardHeight : 0.
limfinity
15

Изменить : я рекомендую более простое и чистое решение. Просто измените класс ограничения нижнего интервала на KeyboardLayoutConstraint . Он автоматически расширится до высоты клавиатуры.


Это улучшенная версия ответа @JosephLord.

Как протестировано на iOS 8.3 iPad Simulator, Portrait. Xcode6.3 beta4, я обнаружил, что его ответ не работает, когда клавиатура прячется, потому что UIKeyboardFrameEndUserInfoKeyесть "NSRect: {{0, 1024}, {768, 264}}";. Высота никогда 0.

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

    // You have to set this up in storyboard first!. 
    // It's a vertical spacing constraint between view and bottom of superview.
    @IBOutlet weak var bottomSpacingConstraint: NSLayoutConstraint! 

    override func viewDidLoad() {
        super.viewDidLoad()

        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardNotification:"), name:UIKeyboardWillShowNotification, object: nil);
        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardNotification:"), name:UIKeyboardWillHideNotification, object: nil);
    }

    deinit {
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }

    func keyboardNotification(notification: NSNotification) {

        let isShowing = notification.name == UIKeyboardWillShowNotification

        if let userInfo = notification.userInfo {
            let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue()
            let endFrameHeight = endFrame?.size.height ?? 0.0
            let duration:NSTimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
            let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
            let animationCurveRaw = animationCurveRawNSN?.unsignedLongValue ?? UIViewAnimationOptions.CurveEaseInOut.rawValue
            let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
            self.bottomSpacingConstraint?.constant = isShowing ? endFrameHeight : 0.0
            UIView.animateWithDuration(duration,
                delay: NSTimeInterval(0),
                options: animationCurve,
                animations: { self.view.layoutIfNeeded() },
                completion: nil)
        }
    }
Hlung
источник
Не могли бы вы объяснить ваши изменения? Я не мог заставить его работать. У меня есть UIScrollView с кнопкой внизу. Я установил класс в нижнем ограничении поля.
schw4ndi
@ schw4ndi с какими взглядами связаны твои нижние ограничения? он должен соединять нижнюю часть прокрутки с нижней частью суперпредставления этой прокрутки.
Хлунг
О, спасибо, у меня было ограничение между кнопкой и scrollView
schw4ndi
9

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

1) Добавить наблюдателя уведомлений в загрузке

override func viewDidLoad() {
        super.viewDidLoad()
        setupManager()
        // Do any additional setup after loading the view.
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    }

2) Удалить Notification Observer как

deinit {
        NotificationCenter.default.removeObserver(self)
    }

3) Добавить методы отображения / скрытия клавиатуры, такие как

 @objc func keyboardWillShow(notification: NSNotification) {
            if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
                UIView.animate(withDuration: 0.1, animations: { () -> Void in
                    self.view.frame.origin.y -= keyboardSize.height
                    self.view.layoutIfNeeded()
                })
            }
        }

@objc func keyboardWillHide(notification: NSNotification) {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
            UIView.animate(withDuration: 0.1, animations: { () -> Void in
                self.view.frame.origin.y += keyboardSize.height
                self.view.layoutIfNeeded()
            })
        }
    }

4) Добавьте делегата textfeild и добавьте методы touchesBegan .usefull для скрытия клавиатуры при касании за пределами текстового поля на экране.

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        view.endEditing(true)

    }
пансора абхай
источник
должно бытьUIKeyboardFrameEndUserInfoKey
Micro
1
NSNotification.Name.UIKeyboardWillShow Переименовано UIResponder.keyboardWillShowNotificationтакже UIKeyboardFrameBeginUserInfoKey вUIResponder.keyboardFrameBeginUserInfoKey
smj
7

Это улучшенная версия ответа @JosephLord и @ Hlung. Может применяться независимо от того, есть ли у вас панель вкладок или нет. И это прекрасно восстановит вид, который перемещен клавиатурой в исходное положение.

// You have to set this up in storyboard first!. 
// It's a vertical spacing constraint between view and bottom of superview.
@IBOutlet weak var bottomSpacingConstraint: NSLayoutConstraint! 

override func viewDidLoad() {
        super.viewDidLoad()            

        //    Receive(Get) Notification
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardNotification:", name: UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardNotification:", name: UIKeyboardWillHideNotification, object: nil)


        self.originalConstraint = self.keyboardHeightLayoutConstraint?.constant //for original coordinate.
}

func keyboardNotification(notification: NSNotification) {
        let isShowing = notification.name == UIKeyboardWillShowNotification

        var tabbarHeight: CGFloat = 0
        if self.tabBarController? != nil {
            tabbarHeight = self.tabBarController!.tabBar.frame.height
        }
        if let userInfo = notification.userInfo {
            let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue()
            let duration:NSTimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
            let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
            let animationCurveRaw = animationCurveRawNSN?.unsignedLongValue ?? UIViewAnimationOptions.CurveEaseInOut.rawValue
            let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
            self.keyboardHeightLayoutConstraint?.constant = isShowing ? (endFrame!.size.height - tabbarHeight) : self.originalConstraint!
            UIView.animateWithDuration(duration,
                delay: NSTimeInterval(0),
                options: animationCurve,
                animations: { self.view.layoutIfNeeded() },
                completion: nil)
        }
}
Джефф Гу Канг
источник
6

Самый простой способ, который не требует никакого кода:

  1. Скачать KeyboardLayoutConstraint.swift и добавьте (перетащите и оставьте) файл в свой проект, если вы еще не используете среду анимации Spring.
  2. В вашей раскадровке создайте нижнее ограничение для объекта / вида / текстового поля, выберите ограничение (дважды щелкните его) и в инспекторе удостоверений измените его класс с NSLayoutConstraint на KeyboardLayoutConstraint.
  3. Готово!

Объект будет автоматически перемещаться вверх с помощью клавиатуры, синхронно.

gammachill
источник
2
Отличное решение! Но вам нужен Safe Area.Bottom как первый элемент ограничения (у меня не работал, когда это был второй элемент). И он работал лучше всего с константой, установленной на 0, поскольку он сохраняет константу и корректирует ее, а не просто перемещает ее достаточно далеко, чтобы показать поле и клавиатуру.
Mythlandia
У вас есть версия KeyboardLayoutConstraint swift4?
jeet.chanchawat
6

Я создал протокол Swift 3 для обработки появления / исчезновения клавиатуры

import UIKit

protocol KeyboardHandler: class {

var bottomConstraint: NSLayoutConstraint! { get set }

    func keyboardWillShow(_ notification: Notification)
    func keyboardWillHide(_ notification: Notification)
    func startObservingKeyboardChanges()
    func stopObservingKeyboardChanges()
}


extension KeyboardHandler where Self: UIViewController {

    func startObservingKeyboardChanges() {

        // NotificationCenter observers
        NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillShow, object: nil, queue: nil) { [weak self] notification in
          self?.keyboardWillShow(notification)
        }

        // Deal with rotations
        NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil, queue: nil) { [weak self] notification in
          self?.keyboardWillShow(notification)
        }

        // Deal with keyboard change (emoji, numerical, etc.)
        NotificationCenter.default.addObserver(forName: NSNotification.Name.UITextInputCurrentInputModeDidChange, object: nil, queue: nil) { [weak self] notification in
          self?.keyboardWillShow(notification)
        }

        NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillHide, object: nil, queue: nil) { [weak self] notification in
          self?.keyboardWillHide(notification)
        }
    }


    func keyboardWillShow(_ notification: Notification) {

      let verticalPadding: CGFloat = 20 // Padding between the bottom of the view and the top of the keyboard

      guard let value = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue else { return }
      let keyboardHeight = value.cgRectValue.height

      // Here you could have more complex rules, like checking if the textField currently selected is actually covered by the keyboard, but that's out of this scope.
      self.bottomConstraint.constant = keyboardHeight + verticalPadding

      UIView.animate(withDuration: 0.1, animations: { () -> Void in
          self.view.layoutIfNeeded()
      })
  }


  func keyboardWillHide(_ notification: Notification) {
      self.bottomConstraint.constant = 0

      UIView.animate(withDuration: 0.1, animations: { () -> Void in
          self.view.layoutIfNeeded()
      })
  }


  func stopObservingKeyboardChanges() {
      NotificationCenter.default.removeObserver(self)
  }

}

Затем, чтобы реализовать его в UIViewController, сделайте следующее:

  • пусть viewController соответствует этому протоколу:

    class FormMailVC: UIViewControlle, KeyboardHandler {
  • начните наблюдать изменения клавиатуры в viewWillAppear:

    // MARK: - View controller life cycle
    override func viewWillAppear(_ animated: Bool) {
      super.viewWillAppear(animated)
      startObservingKeyboardChanges()
    }
  • прекратить наблюдать изменения клавиатуры в viewWillDisappear:

    override func viewWillDisappear(_ animated: Bool) {
      super.viewWillDisappear(animated)
      stopObservingKeyboardChanges()
    }
  • создайте IBOutlet для нижнего ограничения из раскадровки:

    // NSLayoutConstraints
    @IBOutlet weak var bottomConstraint: NSLayoutConstraint!

    (Я рекомендую встроить весь ваш пользовательский интерфейс в «contentView» и связать это свойство с нижним ограничением этого contentView с нижним руководством по компоновке) Ограничение просмотра содержимого

  • изменить приоритет ограничения верхнего ограничения на 250 (низкий)

Ограничение на просмотр содержимого

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

  • Убедитесь, что у вашего Autolayout достаточно ограничений, чтобы определить, как должен выглядеть contentView.

Возможно, вам придется добавить ограничение «больше чем равно» для этого: ограничение "больше чем равно"

И вот, пожалуйста! Без клавиатуры

С клавиатурой

Фредерик Адда
источник
Это сработает, если вы поставите "Relation = Equal" тоже без предупреждений.
Вальтони Боавентура
Если вы ставите равные отношения, они могут работать только в определенных ситуациях. В других случаях вы можете получить предупреждение о несогласованности автоматического макета. Это зависит от вашего собственного макета. Вот почему я сказал: «Возможно, вам придется».
Фредерик Адда
Хорошо, Фредерик, согласился. Было хорошее решение!
Валтони Боавентура
пропал без вестиimport UIKit
Мирко
6

Такой простой UIViewController расширение можно использовать

//MARK: - Observers
extension UIViewController {

    func addObserverForNotification(notificationName: String, actionBlock: (NSNotification) -> Void) {
        NSNotificationCenter.defaultCenter().addObserverForName(notificationName, object: nil, queue: NSOperationQueue.mainQueue(), usingBlock: actionBlock)
    }

    func removeObserver(observer: AnyObject, notificationName: String) {
        NSNotificationCenter.defaultCenter().removeObserver(observer, name: notificationName, object: nil)
    }
}

//MARK: - Keyboard observers
extension UIViewController {

    typealias KeyboardHeightClosure = (CGFloat) -> ()

    func addKeyboardChangeFrameObserver(willShow willShowClosure: KeyboardHeightClosure?,
        willHide willHideClosure: KeyboardHeightClosure?) {
            NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardWillChangeFrameNotification,
                object: nil, queue: NSOperationQueue.mainQueue(), usingBlock: { [weak self](notification) in
                    if let userInfo = notification.userInfo,
                        let frame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue(),
                        let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Double,
                        let c = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? UInt,
                        let kFrame = self?.view.convertRect(frame, fromView: nil),
                        let kBounds = self?.view.bounds {

                            let animationType = UIViewAnimationOptions(rawValue: c)
                            let kHeight = kFrame.size.height
                            UIView.animateWithDuration(duration, delay: 0, options: animationType, animations: {
                                if CGRectIntersectsRect(kBounds, kFrame) { // keyboard will be shown
                                    willShowClosure?(kHeight)
                                } else { // keyboard will be hidden
                                    willHideClosure?(kHeight)
                                }
                                }, completion: nil)
                    } else {
                            print("Invalid conditions for UIKeyboardWillChangeFrameNotification")
                    }
            })
    }

    func removeKeyboardObserver() {
        removeObserver(self, notificationName: UIKeyboardWillChangeFrameNotification)
    }
}

Пример использования

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

        removeKeyboardObserver()
    }

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

    addKeyboardChangeFrameObserver(willShow: { [weak self](height) in
        //Update constraints here
        self?.view.setNeedsUpdateConstraints()
        }, willHide: { [weak self](height) in
        //Reset constraints here
        self?.view.setNeedsUpdateConstraints()
    })
}

Swift 4 решение

//MARK: - Observers
extension UIViewController {

  func addObserverForNotification(_ notificationName: Notification.Name, actionBlock: @escaping (Notification) -> Void) {
    NotificationCenter.default.addObserver(forName: notificationName, object: nil, queue: OperationQueue.main, using: actionBlock)
  }

  func removeObserver(_ observer: AnyObject, notificationName: Notification.Name) {
    NotificationCenter.default.removeObserver(observer, name: notificationName, object: nil)
  }
}

//MARK: - Keyboard handling
extension UIViewController {

  typealias KeyboardHeightClosure = (CGFloat) -> ()

  func addKeyboardChangeFrameObserver(willShow willShowClosure: KeyboardHeightClosure?,
                                      willHide willHideClosure: KeyboardHeightClosure?) {
    NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillChangeFrame,
                                           object: nil, queue: OperationQueue.main, using: { [weak self](notification) in
                                            if let userInfo = notification.userInfo,
                                              let frame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
                                              let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Double,
                                              let c = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? UInt,
                                              let kFrame = self?.view.convert(frame, from: nil),
                                              let kBounds = self?.view.bounds {

                                              let animationType = UIViewAnimationOptions(rawValue: c)
                                              let kHeight = kFrame.size.height
                                              UIView.animate(withDuration: duration, delay: 0, options: animationType, animations: {
                                                if kBounds.intersects(kFrame) { // keyboard will be shown
                                                  willShowClosure?(kHeight)
                                                } else { // keyboard will be hidden
                                                  willHideClosure?(kHeight)
                                                }
                                              }, completion: nil)
                                            } else {
                                              print("Invalid conditions for UIKeyboardWillChangeFrameNotification")
                                            }
    })
  }

  func removeKeyboardObserver() {
    removeObserver(self, notificationName: NSNotification.Name.UIKeyboardWillChangeFrame)
  }
}

Swift 4.2

//MARK: - Keyboard handling
extension UIViewController {

    func addObserverForNotification(_ notificationName: Notification.Name, actionBlock: @escaping (Notification) -> Void) {
        NotificationCenter.default.addObserver(forName: notificationName, object: nil, queue: OperationQueue.main, using: actionBlock)
    }

    func removeObserver(_ observer: AnyObject, notificationName: Notification.Name) {
        NotificationCenter.default.removeObserver(observer, name: notificationName, object: nil)
    }

    typealias KeyboardHeightClosure = (CGFloat) -> ()

    func removeKeyboardObserver() {
        removeObserver(self, notificationName: UIResponder.keyboardWillChangeFrameNotification)
    }

    func addKeyboardChangeFrameObserver(willShow willShowClosure: KeyboardHeightClosure?,
                                        willHide willHideClosure: KeyboardHeightClosure?) {
        NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillChangeFrameNotification,
                                               object: nil, queue: OperationQueue.main, using: { [weak self](notification) in
                                                if let userInfo = notification.userInfo,
                                                    let frame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
                                                    let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double,
                                                    let c = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt,
                                                    let kFrame = self?.view.convert(frame, from: nil),
                                                    let kBounds = self?.view.bounds {

                                                    let animationType = UIView.AnimationOptions(rawValue: c)
                                                    let kHeight = kFrame.size.height
                                                    UIView.animate(withDuration: duration, delay: 0, options: animationType, animations: {
                                                        if kBounds.intersects(kFrame) { // keyboard will be shown
                                                            willShowClosure?(kHeight)
                                                        } else { // keyboard will be hidden
                                                            willHideClosure?(kHeight)
                                                        }
                                                    }, completion: nil)
                                                } else {
                                                    print("Invalid conditions for UIKeyboardWillChangeFrameNotification")
                                                }
        })
    }
}
ale_stro
источник
2
гораздо больше "Swift", чем другие решения / прекрасно работает / многократно используется в каждом контроллере, не переписывая все -> определенно лучший здесь :)
Tib
Я пытался, но не случайно, нужен ли UIScrollView или как?
erdemgc
@erdemgc вы видели пример использования? Все, что вам нужно, это просто UIViewControlller + addKeyboardChangeFrameObserver, а затем не забудьте удалить его
ale_stro
removeKeyboardObserver()Метод здесь не удаляет наблюдателя. Если вы не вызываете это, вы увидите Invalid conditions for UIKeyboardWillChangeFrameNotificationв консоли метод add. Если вы вызовете это, вы увидите ту же ошибку, означающую, что наблюдатель не удален. В документации говорится: «Чтобы отменить регистрацию наблюдений, вы передаете объект, возвращенный этим методом removeObserver(_:)». Вместо этого вы сохраняете объект, возвращенный этим методом, а затем передаете его, когда хотите удалить наблюдателя.
Хай-Ань Хоанг,
На самом деле, после загрузки прокрутки для привязки присваивается значение, и вы не можете определить, будет ли клавиатура скрыта, если рамка клавиатуры пересекается с границей.
Джеймс Ким
5

Вы можете использовать эту библиотеку и всего одну строку кода в appDidFinishedLaunching, и все готово ..

func application(application: UIApplication,didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

    IQKeyboardManager.sharedManager().enable = true
    return true
}

IQKeyboardManager - настроить отображение при появлении ссылки на клавиатуре - https://github.com/hackiftekhar/IQKeyboardManager

Абдул Карим
источник
4
struct MoveKeyboard {
    static let KEYBOARD_ANIMATION_DURATION : CGFloat = 0.3
    static let MINIMUM_SCROLL_FRACTION : CGFloat = 0.2;
    static let MAXIMUM_SCROLL_FRACTION : CGFloat = 0.8;
    static let PORTRAIT_KEYBOARD_HEIGHT : CGFloat = 216;
    static let LANDSCAPE_KEYBOARD_HEIGHT : CGFloat = 162;
}


  func textFieldDidBeginEditing(textField: UITextField) {
    let textFieldRect : CGRect = self.view.window!.convertRect(textField.bounds, fromView: textField)
    let viewRect : CGRect = self.view.window!.convertRect(self.view.bounds, fromView: self.view)

    let midline : CGFloat = textFieldRect.origin.y + 0.5 * textFieldRect.size.height
    let numerator : CGFloat = midline - viewRect.origin.y - MoveKeyboard.MINIMUM_SCROLL_FRACTION * viewRect.size.height
    let denominator : CGFloat = (MoveKeyboard.MAXIMUM_SCROLL_FRACTION - MoveKeyboard.MINIMUM_SCROLL_FRACTION) * viewRect.size.height
    var heightFraction : CGFloat = numerator / denominator

    if heightFraction < 0.0 {
        heightFraction = 0.0
    } else if heightFraction > 1.0 {
        heightFraction = 1.0
    }

    let orientation : UIInterfaceOrientation = UIApplication.sharedApplication().statusBarOrientation
    if (orientation == UIInterfaceOrientation.Portrait || orientation == UIInterfaceOrientation.PortraitUpsideDown) {
        animateDistance = floor(MoveKeyboard.PORTRAIT_KEYBOARD_HEIGHT * heightFraction)
    } else {
        animateDistance = floor(MoveKeyboard.LANDSCAPE_KEYBOARD_HEIGHT * heightFraction)
    }

    var viewFrame : CGRect = self.view.frame
    viewFrame.origin.y -= animateDistance

    UIView.beginAnimations(nil, context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)
    UIView.setAnimationDuration(NSTimeInterval(MoveKeyboard.KEYBOARD_ANIMATION_DURATION))

    self.view.frame = viewFrame

    UIView.commitAnimations()
}


func textFieldDidEndEditing(textField: UITextField) {
    var viewFrame : CGRect = self.view.frame
    viewFrame.origin.y += animateDistance

    UIView.beginAnimations(nil, context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)

    UIView.setAnimationDuration(NSTimeInterval(MoveKeyboard.KEYBOARD_ANIMATION_DURATION))

    self.view.frame = viewFrame

    UIView.commitAnimations()

}

И наконец, так как мы используем методы делегатов

func textFieldShouldReturn(textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }

отказ от использования объектива-c http://www.cocoawithlove.com/2008/10/sliding-uitextfields-around-to-avoid.html

Соломон Айула
источник
Это решение сработало для меня, хотя мне пришлось проделать дополнительную работу: объявить var animateDistance: CGFloat!плюс, я должен был обрабатывать UIKeyboardWillHideNotification, когда пользователь нажимает кнопку скрытия клавиатуры.
Rhuantavan
4

Еще одно решение, которое не зависит от автоматического размещения, ограничений или каких-либо выходов. Что вам нужно, это ваши поля в прокрутки.

override func viewDidLoad() {
    super.viewDidLoad()

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "makeSpaceForKeyboard:", name: UIKeyboardWillShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "makeSpaceForKeyboard:", name: UIKeyboardWillHideNotification, object: nil)
}

func makeSpaceForKeyboard(notification: NSNotification) {
    let info = notification.userInfo!
    let keyboardHeight:CGFloat = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue().size.height
    let duration:Double = info[UIKeyboardAnimationDurationUserInfoKey] as! Double

    if notification.name == UIKeyboardWillShowNotification {
        UIView.animateWithDuration(duration, animations: { () -> Void in
            var frame = self.view.frame
            frame.size.height = frame.size.height - keyboardHeight
            self.view.frame = frame
        })
    } else {
        UIView.animateWithDuration(duration, animations: { () -> Void in
            var frame = self.view.frame
            frame.size.height = frame.size.height + keyboardHeight
            self.view.frame = frame
        })
    }

}
Simpa
источник
1
Это показывает черный экран после UIKeyboardWillShowNotificationзвонка.
Сачин Кумарам
4

Вот моя версия решения для Swift 2.2:

Сначала зарегистрируйтесь для показа клавиатуры / скрытия уведомлений

NSNotificationCenter.defaultCenter().addObserver(self,
                                                 selector: #selector(MessageThreadVC.keyboardWillShow(_:)),
                                                 name: UIKeyboardWillShowNotification,
                                                 object: nil)
NSNotificationCenter.defaultCenter().addObserver(self,
                                                 selector: #selector(MessageThreadVC.keyboardWillHide(_:)),
                                                 name: UIKeyboardWillHideNotification,
                                                 object: nil)

Затем в методах, отвечающих за эти уведомления, переместите основной вид вверх или вниз.

func keyboardWillShow(sender: NSNotification) {
if let keyboardSize = (sender.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue() {
  self.view.frame.origin.y = -keyboardSize.height
  }
}

func keyboardWillHide(sender: NSNotification) {
self.view.frame.origin.y = 0
}

Хитрость в части "keyboardWillShow", которая принимает звонки каждый раз, когда "Панель подсказок QuickType" раскрывается или сворачивается. Затем мы всегда устанавливаем координату y основного вида, которая равна отрицательному значению общей высоты клавиатуры (с частью «QuickType bar» или без нее).

В конце не забудьте убрать наблюдателей

deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
Павле Миятович
источник
3

Ниже приведено простое решение, согласно которому текстовое поле имеет ограничение, связывающее его с нижним руководством по макету. Он просто добавляет высоту клавиатуры к константе ограничения.

// This constraint ties the text field to the bottom layout guide
@IBOutlet var textFieldToBottomLayoutGuideConstraint: NSLayoutConstraint!

override func viewDidLoad() {
    super.viewDidLoad()

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name:UIKeyboardWillShowNotification, object: nil);
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name:UIKeyboardWillHideNotification, object: nil);
}

func keyboardWillShow(sender: NSNotification) {
    if let keyboardSize = (sender.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.textFieldToBottomLayoutGuideConstraint?.constant += keyboardSize.height
    }
}

func keyboardWillHide(sender: NSNotification) {
    if let keyboardSize = (sender.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.textFieldToBottomLayoutGuideConstraint?.constant -= keyboardSize.height
    }
}
echessa
источник
3

Ну, я думаю, что я мог бы опоздать, но я нашел другую простую версию ответа Сакиба. Я использую Autolayout с ограничениями. У меня есть небольшое представление внутри другого основного вида с полями имени пользователя и пароля. Вместо того, чтобы изменять координату y представления, я сохраняю исходное значение ограничения в переменной и изменяю константу ограничения на какое-то значение, и снова после того, как клавиатура отклоняется, я устанавливаю ограничение на исходное. Таким образом, это позволяет избежать проблемы, с которой сталкивается ответ Сакиба (вид продолжает двигаться вверх и не останавливается). Ниже мой код ...

override func viewDidLoad() {
    super.viewDidLoad()
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil);
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil);
    self.originalConstraint = self.centerYConstraint.constant
  }

  func keyboardWillShow(sender: NSNotification) {
    self.centerYConstraint.constant += 30
  }

  func keyboardWillHide(sender: NSNotification) {
    self.centerYConstraint.constant = self.originalConstraint
  }
Sashi
источник
Внутри метода keyboardWillShow проверьте условие, если self.centerYConstraint.constant == self.originalCenterYConstraint имеет эту строку кода между этим условием. OriginalCenterYContraint - это исходное значение centerYContraint, которое я храню в viewdidload. Это сработало для меня.
Саши
3

Ответ Swift 4.x, объединяющий ответы @Joseph Lord и @Isuru. bottomConstraintпредставляет нижнее ограничение представления, которое вы хотите переместить.

override func viewDidLoad() {
    // Call super
    super.viewDidLoad()

    // Subscribe to keyboard notifications
    NotificationCenter.default.addObserver(self,
                                           selector: #selector(keyboardNotification(notification:)),
                                           name: UIResponder.keyboardWillChangeFrameNotification,
                                           object: nil)        
}


deinit {
    NotificationCenter.default.removeObserver(self)
}


@objc func keyboardNotification(notification: NSNotification) {
    if let userInfo = notification.userInfo {
        // Get keyboard frame
        let keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue

        // Set new bottom constraint constant
        let bottomConstraintConstant = keyboardFrame.origin.y >= UIScreen.main.bounds.size.height ? 0.0 : keyboardFrame.size.height

        // Set animation properties
        let duration = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
        let animationCurveRawNSN = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber
        let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
        let animationCurve = UIView.AnimationOptions(rawValue: animationCurveRaw)

        // Animate the view you care about
        UIView.animate(withDuration: duration, delay: 0, options: animationCurve, animations: {
            self.bottomConstraint.constant = bottomConstraintConstant
            self.view.layoutIfNeeded()
        }, completion: nil)
    }
}
Crashalot
источник
2

Я сделал следующим образом:

Это полезно, когда текстовое поле superview является просмотром

class AdminLoginViewController: UIViewController,
UITextFieldDelegate{

    @IBOutlet weak var txtUserName: UITextField!
    @IBOutlet weak var txtUserPassword: UITextField!
    @IBOutlet weak var btnAdminLogin: UIButton!

    private var activeField : UIView?

    var param:String!
    var adminUser : Admin? = nil
    var kbHeight: CGFloat!

    override func viewDidLoad()
    {
        self.addKeyBoardObserver()
        self.addGestureForHideKeyBoard()
    }

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

    func addGestureForHideKeyBoard()
    {
        let tapGesture = UITapGestureRecognizer(target: self, action: Selector("hideKeyboard"))
        tapGesture.cancelsTouchesInView = false
        view.addGestureRecognizer(tapGesture)
    }

    func hideKeyboard() {
        self.view.endEditing(true)
    }

    func addKeyBoardObserver(){

        NSNotificationCenter.defaultCenter().addObserver(self, selector: "willChangeKeyboardFrame:",
name:UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "willChangeKeyboardFrame:",
name:UIKeyboardWillHideNotification, object: nil)
    }

    func removeObserver(){
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }

    //MARK:- textfiled Delegate

    func textFieldShouldBeginEditing(textField: UITextField) -> Bool
    {
         activeField = textField

        return true
    }
    func textFieldShouldEndEditing(textField: UITextField) -> Bool
    {
        if activeField == textField
        {
            activeField = nil
        }

        return true
    }

    func textFieldShouldReturn(textField: UITextField) -> Bool {

        if txtUserName == textField
        {
            txtUserPassword.becomeFirstResponder()
        }
        else if (textField == txtUserPassword)
        {
            self.btnAdminLoginAction(nil)
        }
        return true;
    }

    func willChangeKeyboardFrame(aNotification : NSNotification)
    {
       if self.activeField != nil && self.activeField!.isFirstResponder()
    {
        if let keyboardSize =  (aNotification.userInfo![UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue()
        {
            let dy = (self.activeField?.superview?.convertRect((self.activeField?.frame)!, toView: view).origin.y)!

            let height = (self.view.frame.size.height - keyboardSize.size.height)

            if dy > height
            {
                var frame = self.view.frame

                frame.origin.y = -((dy - height) + (self.activeField?.frame.size.height)! + 20)

                self.view.frame = frame
            }
        }
    }
    else
    {
        var frame = self.view.frame
        frame.origin.y = 0
        self.view.frame = frame
    }
    } }
Кришна Гаваде
источник
2
    func registerForKeyboardNotifications(){
        //Keyboard
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWasShown), name: UIKeyboardDidShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWillBeHidden), name: UIKeyboardDidHideNotification, object: nil)


    }
    func deregisterFromKeyboardNotifications(){

        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)

    }
    func keyboardWasShown(notification: NSNotification){

        let userInfo: NSDictionary = notification.userInfo!
        let keyboardInfoFrame = userInfo.objectForKey(UIKeyboardFrameEndUserInfoKey)?.CGRectValue()

        let windowFrame:CGRect = (UIApplication.sharedApplication().keyWindow!.convertRect(self.view.frame, fromView:self.view))

        let keyboardFrame = CGRectIntersection(windowFrame, keyboardInfoFrame!)

        let coveredFrame = UIApplication.sharedApplication().keyWindow!.convertRect(keyboardFrame, toView:self.view)

        let contentInsets = UIEdgeInsetsMake(0, 0, (coveredFrame.size.height), 0.0)
        self.scrollViewInAddCase .contentInset = contentInsets;
        self.scrollViewInAddCase.scrollIndicatorInsets = contentInsets;
        self.scrollViewInAddCase.contentSize = CGSizeMake((self.scrollViewInAddCase.contentSize.width), (self.scrollViewInAddCase.contentSize.height))

    }
    /**
     this method will fire when keyboard was hidden

     - parameter notification: contains keyboard details
     */
    func keyboardWillBeHidden (notification: NSNotification) {

        self.scrollViewInAddCase.contentInset = UIEdgeInsetsZero
        self.scrollViewInAddCase.scrollIndicatorInsets = UIEdgeInsetsZero

    }
Kamalkumar.E
источник
1
Используйте приведенный выше код для перемещения текстового поля над клавиатурой в Swift 2.2, все будет хорошо. Я надеюсь, что это поможет кому-то.
Kamalkumar.E
1

Я сделал следующим образом:

class SignInController: UIViewController , UITextFieldDelegate {

@IBOutlet weak var scrollView: UIScrollView!

// outlet declartion
@IBOutlet weak var signInTextView: UITextField!

var kbHeight: CGFloat!

/**
*
* @method viewDidLoad
*
*/

override func viewDidLoad() {
    super.viewDidLoad()

    self.signInTextView.delegate = self

}// end viewDidLoad

/**
*
* @method viewWillAppear
*
*/

override func viewWillAppear(animated: Bool) {
    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)

}// end viewWillAppear

/**
*
* @method viewDidAppear
*
*/

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


}// end viewDidAppear

/**
*
* @method viewWillDisappear
*
*/
override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

/**
*
* @method textFieldShouldReturn
* retun the keyboard value
*
*/

// MARK -
func textFieldShouldReturn(textField: UITextField) -> Bool {
    signInTextView.resignFirstResponder()
    return true;

}// end textFieldShouldReturn

// MARK - keyboardWillShow
func keyboardWillShow(notification: NSNotification) {
    if let userInfo = notification.userInfo {
        if let keyboardSize =  (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            kbHeight = keyboardSize.height
            self.animateTextField(true)
        }
    }
}// end keyboardWillShow

// MARK - keyboardWillHide
func keyboardWillHide(notification: NSNotification) {
    self.animateTextField(false)
}// end keyboardWillHide

// MARK - animateTextField
func animateTextField(up: Bool) {
    var movement = (up ? -kbHeight : kbHeight)

    UIView.animateWithDuration(0.3, animations: {
        self.view.frame = CGRectOffset(self.view.frame, 0, movement)
    })
}// end animateTextField

/**
*
* @method didReceiveMemoryWarning
*
*/

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.

}// end didReceiveMemoryWarning


}// end SignInController
Винод Джоши
источник
1

Если вы похожи на меня, который перепробовал все вышеперечисленные решения, но ваша проблема не решена, у меня есть отличное решение для вас, которое работает как шарм. Сначала я хочу уточнить кое-что о некоторых решениях, упомянутых выше.

  1. В моем случае IQkeyboardmanager работал только тогда, когда к элементам не применен автоматический макет, если он применяется, то менеджер IQkeyboard не будет работать так, как мы думаем.
  2. То же самое с восходящим движением self.view.
  3. я написал целевой c заголовок с быстрой поддержкой для перемещения UITexfield вверх, когда пользователь нажимает на него, решая проблему клавиатуры, покрывающей поле UIText: https://github.com/coolvasanth/smart_keyboard .
  4. Тот, кто имеет средний или более высокий уровень в разработке приложений для iOS, может легко понять хранилище и реализовать его. Всего наилучшего
Взант
источник
1

Вот общее решение для всех шагов TextField -

1) Создайте общий ViewController, который расширяется другими ViewController

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)

}
 @objc func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        if self.view.frame.origin.y == 0 {
            self.view.frame.origin.y -= getMoveableDistance(keyboarHeight: keyboardSize.height)
        }
    }
}

@objc func keyboardWillHide(notification: NSNotification) {
    if self.view.frame.origin.y != 0 {
        self.view.frame.origin.y = 0
    }
}
deinit {
    NotificationCenter.default.removeObserver(self)
}

//get the distance to move up the main view for the focus textfiled
func getMoveableDistance(keyboarHeight : CGFloat) ->  CGFloat{
    var y:CGFloat = 0.0
    if let activeTF = getSelectedTextField(){
        var tfMaxY = activeTF.frame.maxY
        var containerView = activeTF.superview!
        while containerView.frame.maxY != self.view.frame.maxY{
            let contViewFrm = containerView.convert(activeTF.frame, to: containerView.superview)
            tfMaxY = tfMaxY + contViewFrm.minY
            containerView = containerView.superview!
        }
        let keyboardMinY = self.view.frame.height - keyboarHeight
        if tfMaxY > keyboardMinY{
            y = (tfMaxY - keyboardMinY) + 10.0
        }
    }

    return y
}

2) Создайте расширение UIViewController и текущего активного TextField.

//get active text field

расширение UIViewController {func getSelectedTextField () -> UITextField? {

    let totalTextFields = getTextFieldsInView(view: self.view)

    for textField in totalTextFields{
        if textField.isFirstResponder{
            return textField
        }
    }

    return nil

}

func getTextFieldsInView(view: UIView) -> [UITextField] {

    var totalTextFields = [UITextField]()

    for subview in view.subviews as [UIView] {
        if let textField = subview as? UITextField {
            totalTextFields += [textField]
        } else {
            totalTextFields += getTextFieldsInView(view: subview)
        }
    }

    return totalTextFields
}

}

Минту Бора
источник
По какой-то причине у меня возникла проблема с функцией keyboardWillShow, размер клавиатуры увеличивался после первого переключения клавиатуры (первый переключатель имеет правильный кадр). Я исправил это, изменив его на guard, позвольте userInfo =tification.userInfo else {return} guard, пусть клавиатурыSize = userInfo [UIResponder.keyboardFrameEndUserInfoKey] как? NSValue else {return} let keyboardFrame = keyboardSize.cgRectValue, если self.view.frame.origin.y == 0 {self.view.frame.origin.y - = getMoveableDistance (keyboarHeight: keyboardFrame.height)}. Надеюсь, это поможет если кто-то получил ту же проблему :)
Youstanzr
1

Очень просто и не нужно больше кодировать. Просто добавьте pod 'IQKeyboardManagerSwift'в ваш подфайл, и на вашей AppDelegateстранице добавьте код ниже.

import IQKeyboardManagerSwift

и в didFinishLaunchingWithOptions()типе метода

IQKeyboardManager.shared.enable = true

это оно проверьте эту ссылку на видео для лучшего понимания https://youtu.be/eOM94K1ZWN8 Надеюсь, это поможет вам.

Рагиб Арши
источник
Работает ли это для TextView и как я могу изменить заголовок для клавиши возврата «Готово»?
тдт киен
Перейти: - «IQKeyboardManager.m» Заменить эту строку (строка № 968): - [textField addDoneOnKeyboardWithTarget: self action: @selector (doneAction :) shouldShowPlaceholder: _shouldShowTextFieldPlaceholder] этим: - [textField addRightButtonTeTKTexTey: Key self action: @selector (doneAction :) shouldShowPlaceholder: _shouldShowTextFieldPlaceholder]; И еще не попробовал текстовое представление, надеюсь, это поможет вам.
Рагиб Арши
1

Полный код для управления клавиатурой.

        override func viewWillAppear(_ animated: Bool) {
            NotificationCenter.default.addObserver(self, selector: #selector(StoryMediaVC.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
            NotificationCenter.default.addObserver(self, selector: #selector(StoryMediaVC.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
        }
        override func viewWillDisappear(_ animated: Bool) {
            NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
            NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
        }
        @objc func keyboardWillShow(notification: NSNotification) {
            guard let userInfo = notification.userInfo else {return}
            guard let keyboardSize = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {return}
            let keyboardFrame = keyboardSize.cgRectValue

            if self.view.bounds.origin.y == 0{
                self.view.bounds.origin.y += keyboardFrame.height
            }
        }


        @objc func keyboardWillHide(notification: NSNotification) {
            if self.view.bounds.origin.y != 0 {
                self.view.bounds.origin.y = 0
            }
        }
Авижит Нагаре
источник
0

Я немного изменил решение @Simpa .........

override func viewDidLoad() 
{

    super.viewDidLoad()
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("makeSpaceForKeyboard:"), name:UIKeyboardWillShowNotification, object: nil);
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("makeSpaceForKeyboard:"), name:UIKeyboardWillHideNotification, object: nil);
}

deinit{
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

var keyboardIsVisible = false
override func makeSpaceForKeyboard(notification: NSNotification) {

    let info = notification.userInfo!
    let keyboardHeight:CGFloat = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue().size.height
    let duration:Double = info[UIKeyboardAnimationDurationUserInfoKey] as! Double

    if notification.name == UIKeyboardWillShowNotification && keyboardIsVisible == false{

        keyboardIsVisible = true

        UIView.animateWithDuration(duration, animations: { () -> Void in
            var frame = self.view.frame
            frame.size.height = frame.size.height - keyboardHeight
            self.view.frame = frame
        })

    } else if keyboardIsVisible == true && notification.name == UIKeyboardWillShowNotification{

    }else {
        keyboardIsVisible = false

        UIView.animateWithDuration(duration, animations: { () -> Void in
            var frame = self.view.frame
            frame.size.height = frame.size.height + keyboardHeight
            self.view.frame = frame
        })
    }
}
пресыщать
источник
0

Ни один из них не работал, и я в конечном итоге использовал контентные вставки для перемещения моего взгляда вверх, когда появляется клавиатура.

Примечание: я использовал UITableView

Ссылочное решение @ клавиатура-контент-смещение полностью написанное в задаче C, ниже приведено чистое решение Swift.

Добавьте наблюдателя уведомления @ viewDidLoad ()

NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(yourClass.keyboardWillBeShown), name:UIKeyboardWillShowNotification, object: nil);
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(yourClass.keyboardWillBeHidden), name:UIKeyboardWillHideNotification, object: nil);

Чтобы получить размер клавиатуры, мы сначала получаем словарь userInfo из объекта уведомления, в котором хранятся любые дополнительные объекты, которые может использовать наш получатель.

Из этого словаря мы можем получить объект CGRect, описывающий кадр клавиатуры, используя ключ UIKeyboardFrameBeginUserInfoKey.

Примените вставку содержимого для метода табличного представления @ keyboardWillBeShown,

func keyboardWillBeShown(sender: NSNotification)
{        
    // Move the table view

    if let keyboardSize = (sender.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue()
    {
        let contentInsets = UIEdgeInsetsMake(0.0, 0.0, (keyboardSize.height), 0.0);

        yourTableView.contentInset = contentInsets;

        yourTableView.scrollIndicatorInsets = contentInsets;
    }
}

Восстановите метод view @ keyboardWillBeHidden

func keyboardWillBeHidden(sender: NSNotification)
{
    // Moving back the table view back to the default position

    yourTableView.contentInset = UIEdgeInsetsZero;

    yourTableView.scrollIndicatorInsets = UIEdgeInsetsZero;
}

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

// Portrait
UIEdgeInsetsMake(0.0, 0.0, (keyboardSize.height), 0.0);

// Landscape
UIEdgeInsetsMake(0.0, 0.0, (keyboardSize.width), 0.0);
Викрам Ежиль
источник
0
override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

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

func keyboardWillShow(_ notification:Notification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0)
    }
}

func keyboardWillHide(_ notification:Notification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
    }
}

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

недотепа
источник
0

Решение Swift 4, которое я использую, имеет размер клавиатуры. Замените serverStatusStackViewна любой вид, который вас интересует, например self.view:

deinit {
    NotificationCenter.default.removeObserver(self)
}

@objc func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        serverStatusStackView.frame.origin.y = keyboardSize.height * 2 - serverStatusStackView.frame.height
    }
}

@objc func keyboardWillHide(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        serverStatusStackView.frame.origin.y += keyboardSize.height
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
    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)
}
В
источник