从 Swift 中的 userInfo 获取键盘大小
Posted
技术标签:
【中文标题】从 Swift 中的 userInfo 获取键盘大小【英文标题】:Getting keyboard size from userInfo in Swift 【发布时间】:2014-08-22 15:54:21 【问题描述】:我一直在尝试添加一些代码以在键盘出现时向上移动我的视图,但是,我在尝试将 Objective-C 示例转换为 Swift 时遇到了问题。我已经取得了一些进展,但我被困在一个特定的路线上。
这是我一直关注的两个教程/问题:
How to move content of UIViewController upwards as Keypad appears using Swift http://www.ioscreator.com/tutorials/move-view-when-keyboard-appears
这是我目前拥有的代码:
override func viewWillAppear(animated: Bool)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)
override func viewWillDisappear(animated: Bool)
NSNotificationCenter.defaultCenter().removeObserver(self)
func keyboardWillShow(notification: NSNotification)
var keyboardSize = notification.userInfo(valueForKey(UIKeyboardFrameBeginUserInfoKey))
UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
let frame = self.budgetEntryView.frame
frame.origin.y = frame.origin.y - keyboardSize
self.budgetEntryView.frame = frame
func keyboardWillHide(notification: NSNotification)
//
目前,我在这一行遇到错误:
var keyboardSize = notification.userInfo(valueForKey(UIKeyboardFrameBeginUserInfoKey))
如果有人能让我知道这行代码应该是什么,我应该自己弄清楚其余的。
【问题讨论】:
【参考方案1】:你的线路有一些问题:
var keyboardSize = notification.userInfo(valueForKey(UIKeyboardFrameBeginUserInfoKey))
notification.userInfo
返回一个可选 字典[NSObject : AnyObject]?
,
因此必须在访问其值之前对其进行解包。
Objective-C NSDictionary
映射到 Swift 原生字典,所以你必须
使用字典下标语法 (dict[key]
) 来访问这些值。
该值必须转换为NSValue
,以便您可以调用CGRectValue
。
所有这些都可以通过可选赋值、可选链接和可选强制转换的组合来实现:
if let userInfo = notification.userInfo
if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue()
let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
// ...
else
// no UIKeyboardFrameBeginUserInfoKey entry in userInfo
else
// no userInfo dictionary in notification
或一步到位:
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue()
let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
// ...
Swift 3.0.1 (Xcode 8.1) 更新:
if let userInfo = notification.userInfo
if let keyboardSize = userInfo[UIKeyboardFrameBeginUserInfoKey] as? CGRect
let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
// ...
else
// no UIKeyboardFrameBeginUserInfoKey entry in userInfo
else
// no userInfo dictionary in notification
或一步到位:
if let keyboardSize = notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? CGRect
let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
// ...
Swift 5 (Xcode 11.6) 更新:
guard let userInfo = notification.userInfo,
let keyboardSize = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else return
我建议使用 keyboardFrameEndUserInfoKey
而不是 keyboardFrameBeginUserInfoKey
,因为在旧 iOS 设备上首次显示后,键盘会更改初始渲染高度。
【讨论】:
@MartinR 很抱歉我评论错了帖子:) 抱歉 您好,我正在尝试通过通知获取键盘大小,但无法正常工作。我在 viewDidload 中添加了观察者(也尝试了 viewWillAppear) NSNotificationCenter.defaultCenter().addObserver(self, selector:"keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil) 但是没有调用该方法。我在真机和模拟器上试了一下。有什么建议吗?非常感谢。 “一步”答案中有 cgRectValue,但应该是 CGRectValue @krotov 答案的第一部分适用于 Swift 2,第二部分适用于 Swift 3。该属性在这些版本之间已重命名。 我认为在键盘框架发生变化时使用 UIKeyboardFrameEndUserInfoKey 而不是 UIKeyboardFrameBeginUserInfoKey 更好(在 iOS 9 或更高版本中启用预测或切换到表情符号键盘)【参考方案2】:对于更少的代码,请考虑查看THIS
这对我真的很有帮助。 您只需在视图控制器中包含视图约束并使用您添加的两个观察者。然后就用下面的方法(这里假设你移动了一个tableView)
func keyboardWillShow(sender: NSNotification)
if let userInfo = sender.userInfo
if let keyboardHeight = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue().size.height
tableViewBottomConstraint.constant = keyboardHeight
UIView.animateWithDuration(0.25, animations: () -> Void in
self.view.layoutIfNeeded()
)
和
func keyboardWillHide(sender: NSNotification)
if let userInfo = sender.userInfo
if let keyboardHeight = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue().size.height
tableViewBottomConstraint.constant = 0.0
UIView.animateWithDuration(0.25, animations: () -> Void in self.view.layoutIfNeeded() )
【讨论】:
直到我在其他地方看到它才得到这个答案,我很清楚 tableViewBottomConstraint 是 Xib 的一个出口。然后很明显这是完美的答案! (如果您使用的是自动布局) @JorisvanLiempd 是的,我正在使用自动布局。很好,对你有帮助。 看来动画是免费的,没有动画块。在这个答案中,无论如何都不遵循键盘曲线和持续时间。【参考方案3】:如果您使用故事板,而不是操纵视图本身,您可以利用自动布局。
(这是尼古拉斯的答案的清理版本)
设置通知中心,通知您键盘的出现和消失:
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)
并确保在不再需要观察者时将其移除:
override func viewWillDisappear(animated: Bool)
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: self.view.window)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: self.view.window)
在情节提要中,设置底部约束。创建该约束的出口:
并在键盘显示或隐藏时设置约束的常量属性:
func keyboardWillShow(notification: NSNotification)
guard let keyboardHeight = (notification.userInfo! as NSDictionary).objectForKey(UIKeyboardFrameBeginUserInfoKey)?.CGRectValue.size.height else
return
nameOfOutlet.constant = keyboardHeight
view.layoutIfNeeded()
func keyboardWillHide(notification: NSNotification)
nameOfOutlet.constant = 0.0
view.layoutIfNeeded()
现在,无论何时键盘出现或消失,自动布局都会处理所有事情。
【讨论】:
【参考方案4】:斯威夫特 2
func keyboardWasShown(notification:NSNotification)
guard let info:[NSObject:AnyObject] = notification.userInfo,
let keyboardSize:CGSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue().size else return
let insets:UIEdgeInsets = UIEdgeInsetsMake(self.scrollView.contentInset.top, 0.0, keyboardSize.height, 0.0)
self.scrollView.contentInset = insets
self.scrollView.scrollIndicatorInsets = insets
斯威夫特 3
func keyboardWasShown(notification:NSNotification)
guard let info:[AnyHashable:Any] = notification.userInfo,
let keyboardSize:CGSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size else return
let insets:UIEdgeInsets = UIEdgeInsets(top: self.scrollView.contentInset.top, left: 0.0, bottom: keyboardSize.height, right: 0.0)
self.scrollView.contentInset = insets
self.scrollView.scrollIndicatorInsets = insets
【讨论】:
谢谢,对我帮助很大!【参考方案5】:这对我有帮助:https://developer.apple.com/library/ios/samplecode/UICatalog/Listings/Swift_UICatalog_TextViewController_swift.html
let userInfo = notification.userInfo!
let animationDuration: NSTimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as NSNumber).doubleValue
let keyboardScreenBeginFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as NSValue).CGRectValue()
let keyboardScreenEndFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as NSValue).CGRectValue()
【讨论】:
【参考方案6】:您可以将这一行用作您的行
var keyboardSize:CGSize = userInfo.objectForKey(UIKeyboardFrameBeginUserInfoKey)!.CGRectValue().size
【讨论】:
从该字典中强制展开键盘框架是不安全的。不可能在那里。【参考方案7】:Swift 3:更新
override func viewWillAppear(_ animated: Bool)
super.viewWillAppear(animated)
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)
override func viewWillDisappear(_ animated: Bool)
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: self.view.window)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: self.view.window)
【讨论】:
【参考方案8】:Swift - 来自keyboardWillShowNotification的键盘高度
您可以使用来自键盘 Will/did Show/hide Notifications 的数据将约束或任何其他值增大或缩小到键盘的大小。
带有布局约束
此最小代码注册通知键盘将显示并根据其大小更新约束。
@IBOutlet weak var keyboardConstraint: NSLayoutConstraint!
let keyboardConstraintMargin:CGFloat = 20
override func viewDidLoad()
super.viewDidLoad()
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: nil) (notification) in
if let keyboardSize = notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? CGRect
self.keyboardConstraint.constant = keyboardSize.height + self.keyboardConstraintMargin
NotificationCenter.default.addObserver(forName: UIResponder.keyboardDidHideNotification, object: nil, queue: nil) (notification) in
self.keyboardConstraint.constant = self.keyboardConstraintMargin
带有滚动视图
以同样的方式,这会根据键盘的大小更新滚动视图的内容插图。
@IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad()
super.viewDidLoad()
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: nil) (notification) in
if let keyboardSize = notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? CGRect
let insets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
self.scrollView.contentInset = insets
self.scrollView.scrollIndicatorInsets = insets
NotificationCenter.default.addObserver(forName: UIResponder.keyboardDidHideNotification, object: nil, queue: nil) (notification) in
let insets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
self.scrollView.contentInset = insets
self.scrollView.scrollIndicatorInsets = insets
【讨论】:
【参考方案9】:详情
Xcode 版本 11.1 (11A1027)、iOS 13、Swift 5解决方案
import UIKit
protocol KeyboardNotificationsDelegate: class
func keyboardWillShow(notification: NSNotification)
func keyboardWillHide(notification: NSNotification)
func keyboardDidShow(notification: NSNotification)
func keyboardDidHide(notification: NSNotification)
extension KeyboardNotificationsDelegate
func keyboardWillShow(notification: NSNotification)
func keyboardWillHide(notification: NSNotification)
func keyboardDidShow(notification: NSNotification)
func keyboardDidHide(notification: NSNotification)
class KeyboardNotifications
fileprivate var _isEnabled: Bool
fileprivate var notifications: [KeyboardNotificationsType]
fileprivate weak var delegate: KeyboardNotificationsDelegate?
init(notifications: [KeyboardNotificationsType], delegate: KeyboardNotificationsDelegate)
_isEnabled = false
self.notifications = notifications
self.delegate = delegate
deinit if isEnabled isEnabled = false
// MARK: - enums
extension KeyboardNotifications
enum KeyboardNotificationsType
case willShow, willHide, didShow, didHide
var selector: Selector
switch self
case .willShow: return #selector(keyboardWillShow(notification:))
case .willHide: return #selector(keyboardWillHide(notification:))
case .didShow: return #selector(keyboardDidShow(notification:))
case .didHide: return #selector(keyboardDidHide(notification:))
var notificationName: NSNotification.Name
switch self
case .willShow: return UIResponder.keyboardWillShowNotification
case .willHide: return UIResponder.keyboardWillHideNotification
case .didShow: return UIResponder.keyboardDidShowNotification
case .didHide: return UIResponder.keyboardDidHideNotification
// MARK: - isEnabled
extension KeyboardNotifications
private func addObserver(type: KeyboardNotificationsType)
NotificationCenter.default.addObserver(self, selector: type.selector, name: type.notificationName, object: nil)
var isEnabled: Bool
set
if newValue
for notificaton in notifications addObserver(type: notificaton)
else
NotificationCenter.default.removeObserver(self)
_isEnabled = newValue
get return _isEnabled
// MARK: - Notification functions
extension KeyboardNotifications
@objc func keyboardWillShow(notification: NSNotification)
delegate?.keyboardWillShow(notification: notification)
@objc func keyboardWillHide(notification: NSNotification)
delegate?.keyboardWillHide(notification: notification)
@objc func keyboardDidShow(notification: NSNotification)
delegate?.keyboardDidShow(notification: notification)
@objc func keyboardDidHide(notification: NSNotification)
delegate?.keyboardDidHide(notification: notification)
用法
class ViewController: UIViewController
private lazy var keyboardNotifications: KeyboardNotifications! =
return KeyboardNotifications(notifications: [.willShow, .willHide, .didShow, .didHide], delegate: self)
()
override func viewWillAppear(_ animated: Bool)
super.viewWillAppear(animated)
keyboardNotifications.isEnabled = true
override func viewWillDisappear(_ animated: Bool)
super.viewWillDisappear(animated)
keyboardNotifications.isEnabled = false
extension ViewController: KeyboardNotificationsDelegate
// If you don't need this func you can remove it
func keyboardWillShow(notification: NSNotification)
print("keyboardWillShow")
guard let userInfo = notification.userInfo as? [String: NSObject],
let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else return
print("keyboardFrame: \(keyboardFrame)")
// If you don't need this func you can remove it
func keyboardWillHide(notification: NSNotification) print("keyboardWillHide")
// If you don't need this func you can remove it
func keyboardDidShow(notification: NSNotification) print("keyboardDidShow")
// If you don't need this func you can remove it
func keyboardDidHide(notification: NSNotification) print("keyboardDidHide")
完整样本
import UIKit
class ViewController: UIViewController
private lazy var keyboardNotifications: KeyboardNotifications! =
return KeyboardNotifications(notifications: [.willShow, .willHide, .didShow, .didHide], delegate: self)
()
override func viewDidLoad()
super.viewDidLoad()
let textField = UITextField(frame: CGRect(x: 40, y: 40, width: 200, height: 30))
textField.borderStyle = .roundedRect
view.addSubview(textField)
let gesture = UITapGestureRecognizer(target: view, action: #selector(UIView.endEditing(_:)))
view.addGestureRecognizer(gesture)
override func viewWillAppear(_ animated: Bool)
super.viewWillAppear(animated)
keyboardNotifications.isEnabled = true
override func viewWillDisappear(_ animated: Bool)
super.viewWillDisappear(animated)
keyboardNotifications.isEnabled = false
extension ViewController: KeyboardNotificationsDelegate
// If you don't need this func you can remove it
func keyboardWillShow(notification: NSNotification)
print("keyboardWillShow")
guard let userInfo = notification.userInfo as? [String: NSObject],
let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else return
print("keyboardFrame: \(keyboardFrame)")
// If you don't need this func you can remove it
func keyboardWillHide(notification: NSNotification) print("keyboardWillHide")
// If you don't need this func you can remove it
func keyboardDidShow(notification: NSNotification) print("keyboardDidShow")
// If you don't need this func you can remove it
func keyboardDidHide(notification: NSNotification) print("keyboardDidHide")
结果
日志
【讨论】:
【参考方案10】:Swift 3.0
下面是一个检索键盘大小并使用它为向上的视图设置动画的示例。在我的情况下,当用户开始输入时,我将包含我的 UITextFields 的 UIView 向上移动,以便他们可以完成表单并仍然在底部看到提交按钮。
我在想要动画的视图的底部空间约束中添加了一个出口,并将其命名为myViewsBottomSpaceConstraint
:
@IBOutlet weak var myViewsBottomSpaceConstraint: NSLayoutConstraint!
然后我将以下代码添加到我的 swift 类中:
override func viewWillAppear(_ animated: Bool)
super.viewWillAppear(animated)
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)
override func viewWillDisappear(_ animated: Bool)
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: self.view.window)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: self.view.window)
func keyboardWillShow(notification: NSNotification)
let userInfo = notification.userInfo as! [String: NSObject] as NSDictionary
let keyboardFrame = userInfo.value(forKey: UIKeyboardFrameEndUserInfoKey) as! CGRect
let keyboardHeight = keyboardFrame.height
myViewsBottomSpaceConstraint.constant = keyboardHeight
view.layoutIfNeeded()
func keyboardWillHide(notification: NSNotification)
myViewsBottomSpaceConstraint.constant = 0.0
view.layoutIfNeeded()
【讨论】:
【参考方案11】:对于 xamarin,您可以使用 c#6
private void KeyboardWillChangeFrame(NSNotification notification)
var keyboardSize = notification.UserInfo.ValueForKey(UIKeyboard.FrameEndUserInfoKey) as NSValue;
if (keyboardSize != null)
var rect= keyboardSize.CGRectValue;
//do your stuff here
c#7
private void KeyboardWillChangeFrame(NSNotification notification)
if (!(notification.UserInfo.ValueForKey(UIKeyboard.FrameEndUserInfoKey) is NSValue keyboardSize)) return;
var rect= keyboardSize.CGRectValue;
【讨论】:
【参考方案12】:在 Swift 4.2 中,您可以使用 UIResponder.keyboardFrameEndUserInfoKey
guard let userInfo = notification.userInfo , let keyboardFrame:CGRect = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else return ```
【讨论】:
以上是关于从 Swift 中的 userInfo 获取键盘大小的主要内容,如果未能解决你的问题,请参考以下文章
SWIFT - 从 userInfo 获取值以填充推送通知的字段
NotificationCenter 中的推送通知在 userinfo 中返回 nil - Swift3
增加框架的大小 - Keyboardwillshow - Swift