在 SwiftUI 中开始编辑文本字段时检索键盘高度

问题描述 投票:0回答:2

所以我有一个类,其中包含一个名为keyboardHeight的已发布变量,用于检索keyboardHeight的值:

class KeyboardHeightHelper: ObservableObject {
    @Published var keyboardHeight: Double = 0
    
    init() {
        listenForKeyboardNotifications()
    }
    
    private func listenForKeyboardNotifications() {
        NotificationCenter.default.addObserver(
            forName: UIResponder.keyboardDidShowNotification,
            object: nil,
            queue: .main
        ) { notification in
            guard
                let userInfo = notification.userInfo,
                let keyboardRect = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect
            else { return }
            
            self.keyboardHeight = keyboardRect.height
        }
        
        NotificationCenter.default.addObserver(
            forName: UIResponder.keyboardDidHideNotification,
            object: nil,
            queue: .main)
        { _ in
            self.keyboardHeight = 0
        }
    }
}

然后,在 ContentView 中,我只有一个 TextField,当您开始/停止编辑字段时,它应该打印键盘高度:

import SwiftUI

struct ContentView: View {
    @State var textFieldText = ""
    @ObservedObject var keyboardHeightHelper = KeyboardHeightHelper()
    var body: some View {
    VStack {
        TextField("Text field",
                  text: $textFieldText, onEditingChanged: { _ in print("the keyboard height is \(self.keyboardHeightHelper.keyboardHeight)") })
        
    }

   }
}

我遇到的问题是这样的:当我不编辑文本字段然后单击它时,它会打印键盘高度为0.0(我猜这是因为它在呈现键盘之前获取了keyboardHeight值,所以当时的高度为 0.0,因为看不到键盘)。当我按回车键并且键盘关闭时,键盘的高度(对于 iPhone 8 模拟器)将打印为正确的值 260.0。我的问题是当我开始编辑时如何访问键盘的值?

swift swiftui keyboard textfield
2个回答
2
投票

试试这个。您只需使用修饰符即可将其集成到 SwiftUI 视图中:

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)
    }
}
extension Publishers {
    static var keyboardHeight: AnyPublisher<CGFloat, Never> {
        let willShow = NotificationCenter.default.publisher(for: UIApplication.keyboardWillShowNotification)
            .map { $0.keyboardHeight }
        
        let willHide = NotificationCenter.default.publisher(for: UIApplication.keyboardWillHideNotification)
            .map { _ in CGFloat(0) }
        
        return MergeMany(willShow, willHide)
            .eraseToAnyPublisher()
    }
}

extension Notification {
    var keyboardHeight: CGFloat {
        return (userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect)?.height ?? 0
    }
}
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())
    }
}

用途:

struct ContentView: View {
    @State private var text = ""
    
    var body: some View {
        VStack {
            Spacer()
            
            TextField("Enter something", text: $text)
                .textFieldStyle(RoundedBorderTextFieldStyle())
        }
        .padding()
        .keyboardAdaptive()//note this
    }
}

归功于多个在线资源。


1
投票

.onEditingChanged
不是读取键盘高度的合适位置,因为您在单击 TextField 时立即收到此回调,因此尚未显示键盘(因此,没有收到通知)。

相反,您可以显式侦听您的

keyboardHeight
属性发布者,并在其更改时准确收到通知(同步在键盘通知上执行的操作,因此及时)

这是一个解决方案(使用 Xcode 12 / iOS 14 测试)

VStack {
    TextField("Text field",
              text: $textFieldText, onEditingChanged: { _ in  })
        .onReceive(keyboardHeightHelper.$keyboardHeight) { value in
            print("the keyboard height is \(value)")
        }
}
© www.soinside.com 2019 - 2024. All rights reserved.