【问题标题】:How to detect when keyboard is shown and hidden如何检测键盘何时显示和隐藏
【发布时间】:2011-05-21 10:33:51
【问题描述】:

如何检测键盘何时在我的应用程序中显示和隐藏?

【问题讨论】:

标签: ios iphone iphone-sdk-3.0


【解决方案1】:

在你的类的 ViewDidLoad 方法中设置监听关于键盘的消息:

// Listen for keyboard appearances and disappearances
[[NSNotificationCenter defaultCenter] addObserver:self 
                                         selector:@selector(keyboardDidShow:)
                                             name:UIKeyboardDidShowNotification
                                           object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardDidHide:)
                                             name:UIKeyboardDidHideNotification
                                           object:nil];

然后在你指定的方法中(在本例中为keyboardDidShowkeyboardDidHide)你可以做一些事情:

- (void)keyboardDidShow: (NSNotification *) notif{
    // Do something here
}

- (void)keyboardDidHide: (NSNotification *) notif{
    // Do something here
}

【讨论】:

  • 如果您在字段中使用标签,则不起作用。想知道解决方案是什么,如果您甚至可以在实际的 iPad 上使用标签?
  • @apprentice 你的意思是说如果你Tab键不显示键盘?
  • 如果在焦点所在的字段下方仍有键盘覆盖的字段,则视图将保持在选项卡上,因为仅在键盘向上滑动时才发送通知
  • @apprentice 您必须手动管理它,根据每个文本字段变为活动状态滑动滚动视图,这与知道键盘何时出现是不同的问题。使您的视图控制器成为UITextFieldDelegate,然后实现textFieldShouldReturn: 方法。您将获得刚刚作为参数输入的textField,您可以将其与您自己的文本字段进行比较并滚动scrollView,以便显示相应的文本字段。
【解决方案2】:

您可能只需要addObserver 中的viewDidLoad。但是在viewWillAppear 中添加addObserver 和在viewWillDisappear 中添加removeObserver 可以防止在您更改视图时发生罕见的崩溃。

斯威夫特 4.2

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillDisappear), name: UIResponder.keyboardWillHideNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillAppear), name: UIResponder.keyboardWillShowNotification, object: nil)
}

@objc func keyboardWillAppear() {
    //Do something here
}

@objc func keyboardWillDisappear() {
    //Do something here
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    NotificationCenter.default.removeObserver(self)
}

斯威夫特 3 和 4

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillDisappear), name: Notification.Name.UIKeyboardWillHide, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillAppear), name: Notification.Name.UIKeyboardWillShow, object: nil)
}

@objc func keyboardWillAppear() {
    //Do something here
}

@objc func keyboardWillDisappear() {
    //Do something here
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    NotificationCenter.default.removeObserver(self)
}

老斯威夫特

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)

    NSNotificationCenter.defaultCenter().addObserver(self, selector:"keyboardWillAppear:", name: UIKeyboardWillShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector:"keyboardWillDisappear:", name: UIKeyboardWillHideNotification, object: nil)
}

func keyboardWillAppear(notification: NSNotification){
    // Do something here
}

func keyboardWillDisappear(notification: NSNotification){
    // Do something here
}

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

【讨论】:

  • 如果您在 viewWillDisappear 上删除了您的观察者...您应该将其添加到 viewWillAppear 而不是 viewDidLoad。
  • 是的,请随时编辑答案。我会接受的
  • @FouZ 像这样从deinit 中删除观察者是否更好:deinit { NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil) NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil) }
  • 在 Swift 3 中,上面的 deinit 代码块是这样的:deinit { NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil) }
  • @Crashalot deinit 在您关闭 vc 之前不会运行。因此,如果您在此之上提供另一个 vc,它仍会收到通知。我相信目的是仅在此 vc 可见时才收听此通知,因此在 viewdidappear 上添加它并在 viewdiddissapear 上删除它对我来说似乎更好。
【解决方案3】:

斯威夫特 3:

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)

func keyboardWillShow(_ notification: NSNotification){
    // Do something here
}

func keyboardWillHide(_ notification: NSNotification){
    // Do something here
}

【讨论】:

    【解决方案4】:

    斯威夫特 5

    上面的答案是正确的。虽然我更愿意创建一个助手来包装notification's observers

    好处:

    1. 您不必在每次处理键盘行为时都重复。
    2. 您可以通过实现其他枚举值来扩展其他通知
    3. 当您必须在多个控制器中处理键盘时,它很有用。

    示例代码:

    extension KeyboardHelper {
        enum Animation {
            case keyboardWillShow
            case keyboardWillHide
        }
    
        typealias HandleBlock = (_ animation: Animation, _ keyboardFrame: CGRect, _ duration: TimeInterval) -> Void
    }
    
    final class KeyboardHelper {
        private let handleBlock: HandleBlock
    
        init(handleBlock: @escaping HandleBlock) {
            self.handleBlock = handleBlock
            setupNotification()
        }
    
        deinit {
            NotificationCenter.default.removeObserver(self)
        }
    
        private func setupNotification() {
            _ = NotificationCenter.default
                .addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { [weak self] notification in
                    self?.handle(animation: .keyboardWillShow, notification: notification)
                }
    
            _ = NotificationCenter.default
                .addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main) { [weak self] notification in
                    self?.handle(animation: .keyboardWillHide, notification: notification)
                }
        }
    
        private func handle(animation: Animation, notification: Notification) {
            guard let userInfo = notification.userInfo,
                let keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
                let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double
            else { return }
    
            handleBlock(animation, keyboardFrame, duration)
        }
    }
    

    使用方法:

    private var keyboardHelper: KeyboardHelper?
    ...
    
    override func viewDidLoad() {
       ...
       keyboardHelper = KeyboardHelper { [unowned self] animation, keyboardFrame, duration in
            switch animation {
            case .keyboardWillShow:
                print("keyboard will show")
            case .keyboardWillHide:
                print("keyboard will hide")
            }
        }
    

    }

    【讨论】:

    • hmm 看起来不太好,我想多次检测键盘事件,我认为更好的是创建类来监听键盘事件并在收到事件后立即销毁
    • @famfamfam 不明白你的意思,但是上面的实现可以连续列出键盘事件,直到keyboardHelper 实例被释放
    【解决方案5】:

    斯威夫特 4:

      NotificationCenter.default.addObserver( self, selector: #selector(ControllerClassName.keyboardWillShow(_:)),
      name: Notification.Name.UIKeyboardWillShow,
      object: nil)
      NotificationCenter.default.addObserver(self, selector: #selector(ControllerClassName.keyboardWillHide(_:)),
      name: Notification.Name.UIKeyboardWillHide,
      object: nil)
    

    接下来,添加在对象生命周期结束时停止监听通知的方法:-

    Then add the promised methods from above to the view controller:
    deinit {
      NotificationCenter.default.removeObserver(self)
    }
    func adjustKeyboardShow(_ open: Bool, notification: Notification) {
      let userInfo = notification.userInfo ?? [:]
      let keyboardFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
      let height = (keyboardFrame.height + 20) * (open ? 1 : -1)
      scrollView.contentInset.bottom += height
      scrollView.scrollIndicatorInsets.bottom += height
    }
    
    @objc func keyboardWillShow(_ notification: Notification) {
      adjustKeyboardShow(true, notification: notification)
    }
    @objc func keyboardWillHide(_ notification: Notification) {
      adjustKeyboardShow(false, notification: notification)
    }
    

    【讨论】:

    • += 似乎使插图越来越大。
    • 我觉得adjustKeyboardShow函数是一个制作精良的函数。谢谢。
    • 从 Swift 5 开始,通知名称为 UIResponder.keyboardWillShowNotificationUIResponder.keyboardWillHideNotification,键盘信息键为 UIResponder.keyboardFrameBeginUserInfoKey
    【解决方案6】:

    在 Swift 4.2 中,通知名称已移至不同的命名空间。所以现在是

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        addKeyboardListeners()
    }
    
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        NotificationCenter.default.removeObserver(self)
    }
    
    
    func addKeyboardListeners() {
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
    }
    
    @objc private extension WhateverTheClassNameIs {
    
        func keyboardWillShow(_ notification: Notification) {
            // Do something here.
        }
    
        func keyboardWillHide(_ notification: Notification) {
            // Do something here.
        }
    }
    

    【讨论】:

      【解决方案7】:

      查看“文本、Web 和编辑编程指南”的Managing the Keyboard 部分,了解有关跟踪显示或隐藏键盘以及如何手动显示/关闭它的信息。

      【讨论】:

        【解决方案8】:

        斯威夫特 - 4

        override func viewWillAppear(_ animated: Bool) {
           super.viewWillAppear(animated)
           addKeyBoardListener()
        }
        
        override func viewWillDisappear(_ animated: Bool) {
            super.viewWillDisappear(animated)
            NotificationCenter.default.removeObserver(self) //remove observer
        }
        
        func addKeyBoardListener() {
            NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil);
            NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil);
        }
        
        @objc func keyboardWillShow(_ notification: Notification) {
        
        }
        
        @objc func keyboardWillHide(_ notification: Notification) {
        
        }
        

        【讨论】:

          【解决方案9】:

          您需要为 2 个键盘通知注册自己:

          [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name: UIKeyboardDidShowNotification object:nil];
          [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector (keyboardDidHide:) name: UIKeyboardDidHideNotification object:nil];
          

          关于如何将 TextField 调整为键盘的精彩帖子 - http://iosdevelopertips.com/user-interface/adjust-textfield-hidden-by-keyboard.html

          【讨论】:

            【解决方案10】:

            Swift 4 - dd 20 october 2017

            override func viewDidLoad() {
                [..]
            
                NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillDisappear(_:)), name: Notification.Name.UIKeyboardWillHide, object: nil)
                NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillAppear(_:)), name: Notification.Name.UIKeyboardWillShow, object: nil)
            }
            
            @objc func keyboardWillAppear(_ notification: NSNotification) {
                if let userInfo = notification.userInfo, 
                   let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue).cgRectValue {
                       let inset = keyboardFrame.height // if scrollView is not aligned to bottom of screen, subtract offset
                       scrollView.contentInset.bottom = inset
                       scrollView.scrollIndicatorInsets.bottom = inset
                }
            }
            
            @objc func keyboardWillDisappear(_ notification: NSNotification) {
                scrollView.contentInset.bottom = 0
                scrollView.scrollIndicatorInsets.bottom = 0
            }
            
            deinit {
                NotificationCenter.default.removeObserver(self)
            }
            

            【讨论】:

              【解决方案11】:

              如果您有多个UITextFields,并且您需要在键盘出现或消失时(或之前)执行某些操作,则可以实现此方法。

              UITextFieldDelegate 添加到您的班级。分配整数计数器,假设:

              NSInteger editCounter; 
              

              viewDidLoad 的某处将此计数器设置为零。 然后,实现textFieldShouldBeginEditingtextFieldShouldEndEditing 委托方法。

              在第一个添加 1 到 editCounter。如果 editCounter 的值变为 1 - 这意味着将出现键盘(如果您返回 YES)。如果 editCounter > 1 - 这意味着键盘已经可见并且另一个 UITextField 持有焦点。

              textFieldShouldEndEditing 中从editCounter 中减1。如果你得到零 - 键盘将被关闭,否则它将保留在屏幕上。

              【讨论】:

                【解决方案12】:

                您可以使用KBKeyboardObserver 库。它包含一些示例并提供简单的界面。

                【讨论】:

                  【解决方案13】:

                  这里有一个 CocoaPods 便于观察 NSNotificationCentr 以了解键盘的可见性:https://github.com/levantAJ/Keyhi

                  pod 'Keyhi'

                  【讨论】:

                    【解决方案14】:

                    所以啊,这才是真正的答案。

                    import Combine
                    
                    
                    class MrEnvironmentObject {
                        /// Bind into yr SwiftUI views
                        @Published public var isKeyboardShowing: Bool = false
                    
                        /// Keep 'em from deallocatin'
                        var subscribers: [AnyCancellable]? = nil
                    
                        /// Adds certain Combine subscribers that will handle updating the
                        ///  `isKeyboardShowing` property 
                        ///
                        /// - Parameter host: the UIHostingController of your views. 
                        func setupSubscribers<V: View>(
                            host: inout UIHostingController<V>
                        ) {
                            subscribers = [
                                NotificationCenter
                                    .default
                                    .publisher(for: UIResponder.keyboardWillShowNotification)
                                    .sink { [weak self] _ in
                                        self?.isKeyboardShowing = true
                                    },
                                NotificationCenter
                                    .default
                                    .publisher(for: UIResponder.keyboardWillHideNotification)
                                    .sink { [weak self, weak host] _ in
                                        self?.isKeyboardShowing = false
                                        // Hidden gem, ask me how I know:
                                        UIAccessibility.post(
                                            notification: .layoutChanged, 
                                            argument: host
                                        )
                                    },
                                // ...
                                Profit
                                    .sink { [weak self] profit in profit() },
                            ]
                        }
                    }
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2012-10-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2018-09-09
                      • 2013-06-26
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多