簡體   English   中英

在不顯示鍵盤的情況下獲取 iOS 鍵盤的高度

[英]Get height of iOS keyboard without displaying keyboard

我正在嘗試獲取 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 中,這樣我就可以將一個實例變量設置為該值,然后隱藏它並擺脫這兩種情況的動畫。 但這真的是唯一的方法嗎?

這個 Swift 類提供了一個統包解決方案,可以管理所有必要的通知和初始化,讓您只需調用類方法並返回鍵盤大小或高度。

從 Swift 調用:

let keyboardHeight = KeyboardService.keyboardHeight()
let keyboardSize = KeyboardService.keyboardSize()

從 Objective-C 調用:

 CGFloat keyboardHeight = [KeyboardService keyboardHeight];
 CGRect keyboardSize = [KeyboardService keyboardSize];

如果想將它用於初始視圖布局,請從您希望在鍵盤出現之前獲得鍵盤高度或大小的類的viewWillAppear方法調用它。 不應在 viewDidLoad 中調用它,因為正確的值取決於您的視圖已布局。 然后,您可以使用從 KeyboardService 返回的值設置自動布局約束常量,或以其他方式使用該值。 例如,您可能希望在prepareForSegue獲取鍵盤高度,以幫助設置與通過嵌入 segue 填充的 containerView 的內容相關聯的值。

請注意安全區域、鍵盤高度和 iPhone X
鍵盤高度的值返回鍵盤的完整高度,在 iPhone X 上,它延伸到屏幕本身的邊緣,而不僅僅是安全區域插入。 因此,如果使用返回值設置自動布局約束值,則應將該約束附加到超級視圖底部邊緣,而不是安全區域。

注意模擬器中的硬件鍵盤
當連接硬件鍵盤時,此代碼將提供該硬件鍵盤的屏幕高度,即沒有高度。 當然,確實需要考慮這種狀態,因為這模擬了將硬件鍵盤連接到實際設備時會發生的情況。 因此,需要鍵盤高度的布局需要對鍵盤高度為零做出適當的響應。

鍵盤服務類:
像往常一樣,如果從 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 對這個問題的回答中概述的原始方法。

您可以使用的快速解決方案與您想要緩存鍵盤時使用的解決方案相同(第一次顯示它時,您會遇到一點延遲......)。 圖書館在這里 有趣的部分:

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

所以基本上是顯示它然后隱藏它。 您可以收聽通知並在沒有實際看到的情況下獲取height 獎勵:你可以緩存它。 :)

看起來這個解決方案確實停止工作了。

我修改了它:

  • 添加回調以了解通知何時以真實高度到達,
  • 將文本字段移動到另一個窗口以避免顯示它,以及
  • 為模擬器中使用的情況設置超時並且軟件鍵盤設置為現在顯示。

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

對於 iOS 14.0,我注意到這個解決方案在第 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

我正在嘗試確定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中,以便我可以將實例變量設置為該值,然后將其隱藏並消除這兩種動畫。 但這真的是唯一的方法嗎?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM