我有一个应用,它在视图的下半部分有一个文本框。 这意味着当我在文本框中输入时,键盘会覆盖文本框。
我如何在打字时向上移动视图,这样我就可以看到我键入的内容,然后在键盘消失时将它移回原来的位置?
我到处都看了,但所有的解似乎都在Obj-C中,我还不能完全转换。
任何帮助都将不胜感激。
我有一个应用,它在视图的下半部分有一个文本框。 这意味着当我在文本框中输入时,键盘会覆盖文本框。
我如何在打字时向上移动视图,这样我就可以看到我键入的内容,然后在键盘消失时将它移回原来的位置?
我到处都看了,但所有的解似乎都在Obj-C中,我还不能完全转换。
任何帮助都将不胜感激。
当前回答
Swift 3更新…
正如其他人所说,你需要在控制器的viewDidLoad()方法中添加通知观察者,如下所示:
NotificationCenter.default.addObserver(forName: .UIKeyboardWillShow, object: nil, queue: nil)
{ notification in
self.keyboardWillShow(notification)
}
NotificationCenter.default.addObserver(forName: .UIKeyboardWillHide, object: nil, queue: nil)
{ notification in
self.keyboardWillHide(notification)
}
NotificationCenter.default.addObserver(forName: .UIKeyboardDidShow, object: nil, queue: nil)
{ _ in
self.enableUserInteraction()
}
NotificationCenter.default.addObserver(forName: .UIKeyboardDidHide, object: nil, queue: nil)
{ _ in
self.enableUserInteraction()
}
记得在适当的地方删除你的观察器(我在viewWillDisappear()方法中这样做)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardDidShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardDidHide, object: nil)
然后,实现你的show和hide方法——注意告诉应用程序忽略交互事件的行(beginIgnoringInteractionEvents)。这很重要,因为如果没有它,用户可能会点击一个字段甚至滚动视图,导致第二次发生移位,从而导致可怕的UI故障。在键盘显示和隐藏之前忽略交互事件将防止这种情况:
func keyboardWillShow(notification: Notification)
{
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
{
UIApplication.shared.beginIgnoringInteractionEvents()
self.view.frame.origin.y -= keyboardSize.height
// add this line if you are shifting a scrollView, as in a chat application
self.timelineCollectionView.contentInset.top += keyboardSize.height
}
}
func keyboardWillHide(notification: Notification)
{
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
{
UIApplication.shared.beginIgnoringInteractionEvents()
self.view.frame.origin.y += keyboardSize.height
// add this line if you are shifting a scrollView, as in a chat application
self.timelineCollectionView.contentInset.top -= keyboardSize.height
}
}
最后,重新启用用户交互(记住,这个方法在键盘didShow或didHide之后触发):
func enableUserInteraction()
{
UIApplication.shared.endIgnoringInteractionEvents()
}
其他回答
由于在Combine中没有如何做到这一点的答案,这里是我使用的方法。
我们创建一个发布者来监听通知,显示和隐藏。 为了显示,我们从通知userInfo中获取键盘帧,并检查当前活动响应器是否包含在其中。如果被覆盖,返回键盘帧高度。如果它没有被覆盖,返回0,我们不想移动帧。对于隐藏通知,我们简单地返回0。
private var keyboardHeightPublisher: AnyPublisher<CGFloat, Never> {
Publishers.Merge(
NotificationCenter.default
.publisher(for: UIResponder.keyboardWillShowNotification)
.compactMap { $0.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect }
.map { $0.intersects(self.view.firstResponder!.frame) ? $0.height : 0 }
.map { $0 * -1 },
NotificationCenter.default
.publisher(for: UIResponder.keyboardWillHideNotification)
.map { _ in CGFloat(0) }
).eraseToAnyPublisher()
}
在viewDidLoad中,我们只需订阅发布者,相应地改变视图框架。
override func viewDidLoad() {
super.viewDidLoad()
keyboardHeightPublisher.sink{ [weak self] height in
self?.view.frame.origin.y = height
}.store(in: &cancelables)
}
编辑 小心!如果firstResponder在子视图中,你必须计算与整个屏幕相对应的帧,以检查它们是否相交。 例子:
let myViewGlobalFrame = myView.convert(myView.frame, to: parentView)
对于黑屏错误(Swift 4和4.2)。
我解决了黑屏的问题。在验证方案中,敲击后键盘高度变化,导致黑屏。
必须使用UIKeyboardFrameEndUserInfoKey而不是UIKeyboardFrameBeginUserInfoKey
var isKeyboardAppear = false
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
@objc func keyboardWillShow(notification: NSNotification) {
if !isKeyboardAppear {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == 0{
self.view.frame.origin.y -= keyboardSize.height
}
}
isKeyboardAppear = true
}
}
@objc func keyboardWillHide(notification: NSNotification) {
if isKeyboardAppear {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y != 0{
self.view.frame.origin.y += keyboardSize.height
}
}
isKeyboardAppear = false
}
}
@Boris的解决方案非常好,但观点有时会被破坏。
为了实现完美的对齐,请使用下面的代码
override func viewDidLoad() {
super.viewDidLoad()
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)}
功能:
@objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == 0{
self.view.frame.origin.y -= keyboardSize.height
}
}}
And,
@objc func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y != 0{
self.view.frame.origin.y = 0
}
} }
我改进了其中一个答案,使它与不同的键盘和不同的文本视图/字段在一个页面上工作:
添加观察员:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange(notification:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
func keyboardWillHide() {
self.view.frame.origin.y = 0
}
func keyboardWillChange(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
if YOURTEXTVIEW.isFirstResponder {
self.view.frame.origin.y = -keyboardSize.height
}
}
}
删除观察员:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
}
所以其他答案似乎都不正确。
iOS上的良好行为键盘应该:
键盘改变大小时自动调整大小(YES IT CAN) 动画的速度与键盘相同 使用与键盘相同的曲线进行动画 如果相关,尊重安全区域。 也适用于iPad/Undocked模式
我的代码使用一个声明为@IBOutlet的NSLayoutConstraint
@IBOutlet private var bottomLayoutConstraint: NSLayoutConstraint!
你也可以使用变换,视图偏移量,....我觉得有了约束就容易多了。它通过在底部设置一个约束来工作,如果你的常量不是0/不是底部,你可能需要修改代码。
代码如下:
// In ViewDidLoad
NotificationCenter.default.addObserver(self, selector: #selector(?MyViewController.keyboardDidChange), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
@objc func keyboardDidChange(notification: Notification) {
let userInfo = notification.userInfo! as [AnyHashable: Any]
let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
let animationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber
let animationCurve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as! NSNumber
bottomLayoutConstraint.constant = view.frame.height - endFrame.origin.y - view.safeAreaInsets.bottom // If your constraint is not defined as a safeArea constraint you might want to skip the last part.
// Prevents iPad undocked keyboard.
guard endFrame.height != 0, view.frame.height == endFrame.height + endFrame.origin.y else {
bottomLayoutConstraint.constant = 0
return
}
UIView.setAnimationCurve(UIView.AnimationCurve(rawValue: animationCurve.intValue)!)
UIView.animate(withDuration: animationDuration.doubleValue) {
self.view.layoutIfNeeded()
// Do additional tasks such as scrolling in a UICollectionView
}
}