键盘快速显示时移动文本字段


217

我正在使用Swift在iOS上进行编程,并且正在使用此代码移动UITextField,但是它不起作用。我keyboardWillShow正确调用了该函数,但是文本字段没有移动。我正在使用自动版式。

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/…–
Dan Beaulieu

也许deinit和viewDidLoad不平衡。
里卡多

基于Apple的文档和个人经验。这是我使用UIScrollView移动TF的git回购:github.com/29satnam/MoveTextFieldWhenKeyboardAppearsSwift
Codetard

Answers:


315

现有答案有一些改进。

首先,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()
    // Note that SO highlighting makes the new selector syntax (#selector()) look
    // like a comment but it isn't one
    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的出色注释,其编辑内容考虑了键盘动画在屏幕外而不是缩小)

迅捷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()
    // Note that SO highlighting makes the new selector syntax (#selector()) look
    // like a comment but it isn't one
    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不是nil,这是行不通的。我得到的结束帧为UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 1024}, {768, 264}}";。在iOS 8.3 iPad模拟器“肖像”上运行。Xcode6.3 beta4。
2015年

8
如果键盘没有隐藏,请尝试使用以下代码,如果endFrame?.origin.y> = UIScreen.mainScreen()。bounds.size.height {self.keyboardHeightLayoutConstraint?.constant = 0.0}其他{self.keyboardHeightLayoutConstraint?.constant = endFrame.size.height}
Gabriel Goncalves

3
keyBoardHeightLayoutConstraint是InterfaceBuilder中定义的约束,用于将要移动/缩小到视图底部的视图底部/底部布局指南或视图控制器的主视图底部约束。该常数最初设置为零,并将在键盘出现或更改大小时进行调整以为键盘留出空间。
约瑟夫·罗德

2
请注意.UIKeyboardWillChangeFrame,即使iOS键盘消失了,连接硬件键盘时也不会触发。您还需要观察.UIKeyboardWillHide以捕获这种极端情况。
jamesk

1
@Sulthan工作正常。.我的问题是,它的高度比键盘高。有什么办法可以解决这个问题?
巴甫洛斯

128

如果您使用的是自动版式,则假定您已将底部空间设置为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仅添加到键盘上方弹出文本字段的位置。否则,将触碰键盘的顶部空白和文本字段的底部空白。

关闭键盘后,将约束的值重置为其原始值。


1
您能解释一下我的定义吗?谢谢!我总是控制故事板上的所有内容
Pedro Manfredi 2014年

4
bottomConstraint是我为约束赋予的名称。我选择了常量,将其拖动并创建了一个IBOutlet并为其命名。您可以创建IBOutlet来约束,就像处理其他UI元素(例如按钮和文本字段)一样。
Isuru 2014年

2
这个答案非常有效,除了动画立即对我发生了。检查如何为约束更改设置动画?有关如何正确设置动画的信息。
亚当·约翰斯

2
@ vinbhai4u您必须注册才能收到UIKeyboardWillShowNotification通知。查看OP的问题中的代码。
Isuru,2015年

8
@AdamJohns要设置约束变化的动画,请更新外部的常量animateWithDurationself.view.layoutIfNeeded()在animate块内部调用。
最多

110

一个简单的解决方案是在键盘高度不变的情况下向上移动视图。

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
我喜欢这个简单的解决方案。但是我添加了keyboardShowing布尔值,因为我的动作比一个文本字段还重要。在显示时,我只移动了一次键盘。谢谢。
肯(Ken)

2
而不是移动视图,而是移动
textView

1
如果用户切换输入语言,它将保持减去视图的y值。
Jeffrey Neo

它没有更改我做过self.myConstraint值的self.view,它可以工作,但问题是(施加了约束的)视图不断向上移动。有人面对过这个问题吗?
Sashi

5
代替使用self.view.frame.origin.y -= 150use self.view.frame.origin.y = -150和代替self.view.frame.origin.y += 150use self.view.frame.origin.y = 0。这防止了每次触摸新字段时视图移动150。
甘文

43

要在编辑文本字段时移动视图,请尝试此方法,我已经应用了此方法,

选项1:-** ** 使用NotificationCenter 在Swift 5.0和iPhone X,XR,XS和XS Max Move中更新

  • 在此注册通知 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()
}

当键盘出现在Swift中时,我从这个源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()
    }

1
@ Jogendra.Com,感谢您的辛勤工作。但是,它在iPhone 5,4s和6上效果最佳。但是,如何在iPhone 6plus和iPad(更高版本)上禁用它
Thiha Aung

如果使用选项1,请确保添加约束IBOutlet。您将创建一个要使用自动布局调整大小的约束,然后将其拖放到视图控制器以创建一个IBOutlet,在动画函数中我将其称为self.iboutletConstraint.constant。同样,这并不会在隐藏键盘时重新调整出口,我通过将约束重置为其原始值来解决了这一问题。
哈马德·塔里克

21

我喜欢干净的Swift代码。因此,这是我想出的最紧凑的代码,可以使用键盘上下移动文本视图。目前,它正在iOS8 / 9 Swift 2生产应用中运行。

更新(2016年3月):我只是尽可能地收紧了我以前的代码。另外,这里有很多流行的答案,它们对键盘的高度和动画参数进行了硬编码。无需这样做,更不用说这些答案中的数字并不总是与我在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键盘的深入文章。如果您需要处理所有情况,这可能会有所帮助。


似乎是Swift 1.1,我认为它不会在Swift 1.2中编译,因为它as用于强制施放。as!可能有效,但正如您在本页其他地方所看到的,我避免强行施压和强行打开包装箱。
约瑟夫·罗德

现在可以在Swift 1.2中进行编译。我在代码re中添加了一条注释:强制展开。干杯。
scootermg

哎呀。我的意思是斯威夫特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)
        }
    }

您能解释一下您的编辑吗?我无法正常工作。我有一个UIScrollView,底部有一个Button。我将类设置在底部的边距底部约束处。
schw4ndi

@ schw4ndi您的底部约束与哪些视图相关?它应该将scrollview的底部连接到该scrollview的superview的底部。
Hlung

哦,谢谢,我在按钮和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)删除通知观察者像

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用于在屏幕上的textfeild之外进行触摸时隐藏键盘

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

    }

需要是UIKeyboardFrameEndUserInfoKey
Micro

1
NSNotification.Name.UIKeyboardWillShow 被重命名为UIResponder.keyboardWillShowNotificationUIKeyboardFrameBeginUserInfoKey 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. 如果尚未使用Spring动画框架,请下载KeyboardLayoutConstraint.swift并将文件添加(拖放)到您的项目中。
  2. 在情节提要中,为对象/视图/文本字段创建底部约束,选择约束(双击),然后在Identity Inspector中,将其类从NSLayoutConstraint更改为KeyboardLayoutConstraint。
  3. 做完了!

该对象将与键盘同步自动上移。


2
很好的解决方案!但是您需要“安全区域”作为约束的第一项(第二项对我不起作用)。并且将常量设置为0时效果最佳,因为它可以保留常量并对其进行调整,而不仅仅是将其移动到足以显示字段和键盘的程度。
Mythlandia '18

您有KeyboardLayoutConstraint swift4版本吗?
jeet.chanchawat 18-10-11

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!

    (我建议将所有UI嵌入“ contentView”中,并将此contentView的底部约束链接到此底部布局指南,以将此属性链接到该属性) 内容视图底部约束

  • 最高约束的约束优先级更改为250(低)

内容视图顶部约束

这是为了让整个内容视图在出现键盘时向上滑动。优先级必须低于子视图中的任何其他约束优先级,包括内容拥抱优先级/内容压缩抵抗优先级。

  • 确保您的自动版式有足够的约束来确定contentView应该如何向上滑动。

您可能需要为此添加“大于等于”约束: “大于等于”约束

然后你去! 不带键盘

带键盘


如果您也输入“ Relation = Equal”,而没有警告,则该方法有效。
Valtoni Boaventura

如果建立平等关系,则可能仅在特定情况下有效。在其他情况下,您可能会收到自动版式不一致警告。这取决于您自己的布局。这就是为什么我说“您可能必须”。
弗雷德里克·阿达

OkFrédéric,同意了。是一个很好的解决方案!
Valtoni Boaventura

失踪import UIKit
Mirko

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

斯威夫特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")
                                                }
        })
    }
}

2
比其他解决方案“迅速”得多/效果很好/可在每个控制器中重复使用,而无需重写所有内容->绝对是这里最好的解决方案:)
Tib

我尝试过但没有机会,需要UIScrollView还是什么?
erdemgc

@erdemgc您看到用法示例了吗?您只需要UIViewControlller + addKeyboardChangeFrameObserver,然后别忘了将其删除
ale_stro

removeKeyboardObserver()此处的方法实际上并未删除观察者。如果不调用它,您将Invalid conditions for UIKeyboardWillChangeFrameNotification在add方法的控制台中看到a 。如果调用此命令,则会看到相同的错误,这意味着不会删除观察者。文档指出“要取消注册观察值,请将此方法返回的对象传递给removeObserver(_:)。” 因此,您要做的是保存该方法返回的对象,然后在要删除观察者时将其传递。
惠安·昂

实际上,一旦滚动视图被加载,bound就会被分配一个值,并且如果键盘框架与bound相交,您将无法检测是否隐藏了键盘。
詹姆士·金


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
    }

通过使用Objective-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
        })
    }

}

1
UIKeyboardWillShowNotification调用后显示黑屏。
Sachin Kumaram '16

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栏”部分)。

最后不要忘了删除观察者

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

3

好吧,我想我可能为时已晚,但是我找到了Saqib答案的另一个简单版本。我使用带有约束的自动版式。我在另一个带有用户名和密码字段的主视图中有一个小视图。我没有将视图的y坐标更改为将原始约束值保存在变量中,而是将约束的常量更改为某个值,然后在键盘退出后再次将约束设置为原始约束。这样可以避免Saqib的答案存在问题(视图不断向上移动并且不会停止)。下面是我的代码...

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
  }

keyboardWillShow方法内部, 检查self.centerYConstraint.constant == self.originalCenterYConstraint是否满足条件在此条件之间那一行代码。OriginalCenterYContraint是我存储在viewdidload中的centerYContraint的原始值。这对我有用。
Sashi 2015年

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

2

我已经通过以下方式完成:

在查看文本字段超级视图时,此功能很有用

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

    }

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 Manager将无法按照我们的想法工作。
  2. self.view向上移动时也是如此
  3. 我编写了一个客观的c标头,并提供了快速支持,当用户单击它时可以将UITexfield向上推,解决了键盘覆盖UITextfield的问题: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函数中遇到问题,在第一次键盘切换后(第一次切换具有正确的帧),keyboardSize的大小错误。我通过更改它来解决此问题,以保护let userInfo = notification.userInfo else {return}保护let keyboardSize = userInfo [UIResponder.keyboardFrameEndUserInfoKey]如?NSValue否则{返回}如果self.view.frame.origin.y == 0 {self.view.frame.origin.y-= getMoveableDistance(keyboarHeight:keyboardFrame.height)},则让keyboardFrame = keyboardSize.cgRectValue。希望这会有所帮助如果有人遇到同样的问题:)
Youstanzr

1

非常简单,无需编写更多代码。只需添加pod 'IQKeyboardManagerSwift'您的podfile,然后在您的AppDelegate页面中添加以下代码即可。

import IQKeyboardManagerSwift

和方法didFinishLaunchingWithOptions()类型

IQKeyboardManager.shared.enable = true

这就对了。请查看此视频链接以更好地了解https://youtu.be/eOM94K1ZWN8 希望对您有所帮助。


它适用于TextView,如何更改返回键“ Done”的标题?
tdt kien

转到:-“ IQKeyboardManager.m”替换此行(行号968):-[textField addDoneOnKeyboardWithTarget:self action:@selector(doneAction :) shouldShowPlaceholder:_shouldShowTextFieldPlaceholder]与此:-[textField addRightButtonOnKeyboardWithText:@“ YOURTEXT”目标:自我行动:@selector(doneAction :) shouldShowPlaceholder:_shouldShowTextFieldPlaceholder]; 尚未尝试使用textview,希望对您有所帮助。
Raghib Arshi

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

引用的解决方案@ Keyboard-content-offset完全用目标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字典,该对象存储了接收者可能使用的任何其他对象。

从该字典中,我们可以使用键UIKeyboardFrameBeginUserInfoKey获得描述键盘框架的CGRect对象。

将内容插图应用于表视图@ 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;
    }
}

还原视图@ 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)
}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.