获取iOS键盘的高度而不显示键盘

问题描述 投票:6回答:3

我正试图获得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。

我的问题是:如何在没有键盘出现的情况下通过Notifications或其他方法获得键盘的高度。

我考虑过明确强制键盘出现在viewDidLoad中,这样我就可以将一个实例变量设置为该值,然后将其隐藏起来并摆脱两者的动画。但这真的是唯一的方法吗?

ios iphone cocoa-touch keyboard
3个回答
5
投票

您可以使用的快速解决方案与您想要缓存键盘时使用的快速解决方案相同(第一次显示时,您会稍微延迟......)。图书馆是here。有趣的一点:

[[[[UIApplication sharedApplication] windows] lastObject] addSubview:field];
[field becomeFirstResponder];
[field resignFirstResponder];
[field removeFromSuperview];

所以基本上是显示它然后隐藏它。您可以收听通知,只是在没有真正看到它的情况下获取height。额外奖励:你可以缓存它。 :)


7
投票

这个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中概述的原始方法回答了这个问题。


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
    }
© www.soinside.com 2019 - 2024. All rights reserved.