iOS:如何在键盘按键后停止接收keyboardWillShowNotification

时间:2020-04-23 07:08:48

标签: ios swift swiftui

我正在尝试进行键盘自适应行为。我已经订阅了keyboardWillShowNotificationkeyboardWillHideNotification,并且收到了所有预期的事件。但是我对keyboardWillShowNotification有问题:按下键盘按钮后,我收到了一次:

Example

这是我的侦听器的代码:

import SwiftUI
import Combine

// MARK: - View

struct ContentView: View {
    @State private var text: String = ""

    var body: some View {
        VStack{
            Text(text)
            Spacer()
            TextField("Input your text", text: $text)
            .textFieldStyle(RoundedBorderTextFieldStyle())
        }
        .padding()
        .keyboardAdaptive()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

// MARK: - Keyboard hacks

extension Notification {
    var keyboardHeight: CGFloat {
        return (userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect)?.height ?? 0
    }
}

extension Publishers {
    static var keyboardHeight: AnyPublisher<CGFloat, Never> {
        let willShow = NotificationCenter.default.publisher(for: UIApplication.keyboardWillShowNotification)
            .map { (n: Notification) -> CGFloat in
                print("will show")
                return n.keyboardHeight
            }

        let willHide = NotificationCenter.default.publisher(for: UIApplication.keyboardWillHideNotification)
            .map { (n: Notification) -> CGFloat in
                print("will hide")
                return CGFloat(0)
            }

        return MergeMany(willShow, willHide)
            .eraseToAnyPublisher()
    }
}

// MARK: -- Detect focused element
extension UIResponder {
    static var currentFirstResponder: UIResponder? {
        _currentFirstResponder = nil
        UIApplication.shared.sendAction(#selector(UIResponder.findFirstResponder(_:)), to: nil, from: nil, for: nil)
        return _currentFirstResponder
    }

    private static weak var _currentFirstResponder: UIResponder?

    @objc private func findFirstResponder(_ sender: Any) {
        UIResponder._currentFirstResponder = self
    }

    var globalFrame: CGRect? {
        guard let view = self as? UIView else { return nil }
        return view.superview?.convert(view.frame, to: nil)
    }
}

// MARK: -- Keyboard Adaptive Modifier

struct KeyboardAdaptive: ViewModifier {
    @State private var bottomPadding: CGFloat = 0

    func body(content: Content) -> some View {
        GeometryReader { geometry in
            content
                .padding(.bottom, self.bottomPadding)
                .onReceive(Publishers.keyboardHeight) { keyboardHeight in
                    let keyboardTop = geometry.frame(in: .global).height - keyboardHeight
                    let focusedTextInputBottom = UIResponder.currentFirstResponder?.globalFrame?.maxY ?? 0
                    self.bottomPadding = max(0, focusedTextInputBottom - keyboardTop - geometry.safeAreaInsets.bottom)
            }
            .animation(.easeOut(duration: 0.16))
        }
    }
}

extension View {
    func keyboardAdaptive() -> some View {
        ModifiedContent(content: self, modifier: KeyboardAdaptive())
    }
}

我是移动开发领域的新手,有人可以帮助我吗?

2 个答案:

答案 0 :(得分:1)

简单的是忽略相同的条件,如下所示。使用Xcode 11.4 / iOS 13.4进行了测试

which is to use a float

struct KeyboardAdaptive: ViewModifier {
    @State private var bottomPadding: CGFloat = 0

    func body(content: Content) -> some View {
        GeometryReader { geometry in
            content
                .padding(.bottom, self.bottomPadding)
                .onReceive(Publishers.keyboardHeight) { keyboardHeight in
                    // Just ignore equal resend
                    if keyboardHeight > 0 && self.bottomPadding > 0 { return }   // << here !!

                    let keyboardTop = geometry.frame(in: .global).height - keyboardHeight
                    let focusedTextInputBottom = UIResponder.currentFirstResponder?.globalFrame?.maxY ?? 0
                    self.bottomPadding = max(0, focusedTextInputBottom - keyboardTop - geometry.safeAreaInsets.bottom)
            }
            .animation(.easeOut(duration: 0.16))
        }
    }
}

答案 1 :(得分:0)

仅在前keyboardWillShowNotification中,keyboardFrameBeginUserInfoKeykeyboardFrameEndUserInfoKey包含不同的CGRects值。在随后的通知中,开始和结束矩形均相同。您可以将其用作指示器,以忽略后续的通知。

另一方面,我相信在按下已经显示的键盘上的某个键之后再获得另一个keyboardWillShowNotification是iOS中的一个错误。它不仅在使用SwiftUI时也发生在UIKit中。