我试图获得iOS键盘的高度。我已经完成并使用了订阅通知的方法,如下所述: https://gist.github.com/philipmcdermott/5183731
- (void)viewDidAppear:(BOOL) animated {
[super viewDidAppear:animated];
// Register notification when the keyboard will be show
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
// Register notification when the keyboard will be hide
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)keyboardWillShow:(NSNotification *)notification {
CGRect keyboardBounds;
[[notification.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] getValue:&keyboardBounds];
// Do something with keyboard height
}
- (void)keyboardWillHide:(NSNotification *)notification {
CGRect keyboardBounds;
[[notification.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] getValue:&keyboardBounds];
// Do something with keyboard height
}
当用户实际显示键盘时,此功能正常。
我的问题:我有另一种观点,我们称它为micView,可以在键盘出现之前显示。用户可以在键入之前选择使用麦克风。我希望micView与键盘的高度相同,这就是为什么我需要键盘的高度,但是在键盘被迫出现之前我需要它。因此,在我需要读取高度值之前,未达到UIKeyboardWillShowNotification。
我的问题是:如何在没有键盘出现的情况下通过通知或其他方法获得键盘的高度。
我考虑过明确强制键盘出现在viewDidLoad中,这样我就可以将一个实例变量设置为该值,然后将其隐藏并删除两者的动画。但这真的是唯一的方法吗?
答案 0 :(得分:5)
您可以使用的快速解决方案与您想要缓存键盘时使用的解决方案相同(第一次显示时,您会稍微延迟......)。该库是here。有趣的一点:
[[[[UIApplication sharedApplication] windows] lastObject] addSubview:field];
[field becomeFirstResponder];
[field resignFirstResponder];
[field removeFromSuperview];
所以基本上是显示它然后隐藏它。您可以收听通知,只是在没有真正看到它的情况下获取height
。 奖金:你可以缓存它。 :)
答案 1 :(得分:5)
这个Swift类提供了一个交钥匙解决方案,可以管理所有必要的通知和初始化,让您只需调用类方法并返回键盘大小或高度。
来自Swift的电话:
let keyboardHeight = KeyboardService.keyboardHeight()
let keyboardSize = KeyboardService.keyboardSize()
从Objective-C调用:
CGFloat keyboardHeight = [KeyboardService keyboardHeight];
CGRect keyboardSize = [KeyboardService keyboardSize];
如果要将其用于初始视图布局,请在键盘出现之前希望键盘高度或大小的类的viewWillAppear
方法中调用此方法。它不应该在viewDidLoad中调用,因为正确的值依赖于您已经布局的视图。然后,您可以使用KeyboardService返回的值设置autolayout约束常量,或以其他方式使用该值。例如,您可能希望获得prepareForSegue
中的键盘高度,以帮助设置与通过嵌入segue填充的containerView内容相关联的值。
注意重新安全区域,键盘高度和iPhone X :
键盘高度的值返回键盘的完整高度,它在iPhone X上延伸到屏幕边缘,而不仅仅是安全区域插入。因此,如果使用返回值设置自动布局约束值,则应将该约束附加到superview底边,而不是安全区域。
注意模拟器中的硬件键盘:
连接硬件键盘时,此代码将提供该硬件键盘的屏幕高度,即没有高度。当然,这个状态确实需要考虑,因为这模拟了如果你有一个硬件键盘连接到实际设备会发生什么。因此,期望键盘高度的布局需要适当地响应键盘高度为零。
KeyboardService类:
像往常一样,如果从Objective-C调用,您只需要在Objective-C类中导入应用程序的Swift桥接头MyApp-Swift.h
。
import UIKit
class KeyboardService: NSObject {
static var serviceSingleton = KeyboardService()
var measuredSize: CGRect = CGRect.zero
@objc class func keyboardHeight() -> CGFloat {
let keyboardSize = KeyboardService.keyboardSize()
return keyboardSize.size.height
}
@objc class func keyboardSize() -> CGRect {
return serviceSingleton.measuredSize
}
private func observeKeyboardNotifications() {
let center = NotificationCenter.default
center.addObserver(self, selector: #selector(self.keyboardChange), name: .UIKeyboardDidShow, object: nil)
}
private func observeKeyboard() {
let field = UITextField()
UIApplication.shared.windows.first?.addSubview(field)
field.becomeFirstResponder()
field.resignFirstResponder()
field.removeFromSuperview()
}
@objc private func keyboardChange(_ notification: Notification) {
guard measuredSize == CGRect.zero, let info = notification.userInfo,
let value = info[UIKeyboardFrameEndUserInfoKey] as? NSValue
else { return }
measuredSize = value.cgRectValue
}
override init() {
super.init()
observeKeyboardNotifications()
observeKeyboard()
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
头点头:
这里的observeKeyboard方法基于Peres在Objective-C中概述的原始方法回答了这个问题。
答案 2 :(得分:0)
好像this solution确实停止工作。
我修改了它:
使用Swift 4:
import UIKit
public class KeyboardSize {
private static var sharedInstance: KeyboardSize?
private static var measuredSize: CGRect = CGRect.zero
private var addedWindow: UIWindow
private var textfield = UITextField()
private var keyboardHeightKnownCallback: () -> Void = {}
private var simulatorTimeout: Timer?
public class func setup(_ callback: @escaping () -> Void) {
guard measuredSize == CGRect.zero, sharedInstance == nil else {
return
}
sharedInstance = KeyboardSize()
sharedInstance?.keyboardHeightKnownCallback = callback
}
private init() {
addedWindow = UIWindow(frame: UIScreen.main.bounds)
addedWindow.rootViewController = UIViewController()
addedWindow.addSubview(textfield)
observeKeyboardNotifications()
observeKeyboard()
}
public class func height() -> CGFloat {
return measuredSize.height
}
private func observeKeyboardNotifications() {
let center = NotificationCenter.default
center.addObserver(self, selector: #selector(self.keyboardChange), name: UIResponder.keyboardDidShowNotification, object: nil)
}
private func observeKeyboard() {
let currentWindow = UIApplication.shared.keyWindow
addedWindow.makeKeyAndVisible()
textfield.becomeFirstResponder()
currentWindow?.makeKeyAndVisible()
setupTimeoutForSimulator()
}
@objc private func keyboardChange(_ notification: Notification) {
textfield.resignFirstResponder()
textfield.removeFromSuperview()
guard KeyboardSize.measuredSize == CGRect.zero, let info = notification.userInfo,
let value = info[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue
else { return }
saveKeyboardSize(value.cgRectValue)
}
private func saveKeyboardSize(_ size: CGRect) {
cancelSimulatorTimeout()
KeyboardSize.measuredSize = size
keyboardHeightKnownCallback()
KeyboardSize.sharedInstance = nil
}
private func setupTimeoutForSimulator() {
#if targetEnvironment(simulator)
let timeout = 2.0
simulatorTimeout = Timer.scheduledTimer(withTimeInterval: timeout, repeats: false, block: { (_) in
print(" KeyboardSize")
print(" .keyboardDidShowNotification did not arrive after \(timeout) seconds.")
print(" Please check \"Toogle Software Keyboard\" on the simulator (or press cmd+k in the simulator) and relauch your app.")
print(" A keyboard height of 0 will be used by default.")
self.saveKeyboardSize(CGRect.zero)
})
#endif
}
private func cancelSimulatorTimeout() {
simulatorTimeout?.invalidate()
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
以下列方式使用:
let splashVC = some VC to show in the key window during the app setup (just after the didFinishLaunching with options)
window.rootViewController = splashVC
KeyboardSize.setup() { [unowned self] in
let kbHeight = KeyboardSize.height() // != 0 :)
// continue loading another things or presenting the onboarding or the auth
}
答案 3 :(得分:0)
对于iOS 14.0,我注意到this solution停止了第10个通话,因为NotificationCenter
停止广播keyboardChange
通知。我无法完全弄清为什么会这样。
因此,我调整了解决方案以使KeyboardSize
为单例,并添加了方法updateKeyboardHeight()
如下:
static let shared = KeyboardSize()
/**
Height of keyboard after the class is initialized
*/
private(set) var keyboardHeight: CGFloat = 0.0
private override init() {
super.init()
observeKeyboardNotifications()
observeKeyboard()
}
func updateKeyboardHeight() {
observeKeyboardNotifications()
observeKeyboard()
}
并将其用作
KeyboardSize.shared.updateKeyboardHeight()
let heightOfKeyboard = KeyboardSize.shared.keyboardHeight
答案 4 :(得分:-1)
Swift 5.1解决方案:
import UIKit
// MARK: - Properties & Lifecycle
class Keyboard {
static private let shared = Keyboard()
static private(set) var size = CGRect.zero
static var isVisible: Bool = false
static var height: CGFloat {
return size.height
}
init() {
observeKeyboardNotifications()
triggerKeyboardNotification()
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
// MARK: - NotificationCenter
extension Keyboard {
private func observeKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(self.handleKeyboardShow), name: UIResponder.keyboardDidShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.handleKeyboardHide), name: UIResponder.keyboardDidHideNotification, object: nil)
}
private func triggerKeyboardNotification() {
let field = UITextField()
UIApplication.shared.windows.first?.addSubview(field)
field.becomeFirstResponder()
field.resignFirstResponder()
field.removeFromSuperview()
}
@objc private func handleKeyboardShow(_ notification: Notification) {
Keyboard.isVisible = true
if let info = notification.userInfo, let value = info[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
Keyboard.size = value.cgRectValue
}
}
@objc private func handleKeyboardHide(_ notification: Notification) {
Keyboard.isVisible = false
}
}