在 SwiftUI 中开始文本字段编辑时检索键盘高度
Retrieving keyboard height when textfield editing starts in SwiftUI
所以我有一个 class,其中包含一个名为 keyboardHeight 的已发布变量,用于检索 keyboardHeight 的值:
import UIKit
class KeyboardHeightHelper: ObservableObject {
@Published var keyboardHeight: CGFloat = 0
init() {
self.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) { (notification) in
self.keyboardHeight = 0
}
}
}
然后,在 ContentView 中,我只有一个 TextField,当您 start/stop 编辑该字段时,它应该打印键盘高度:
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 的时间)。当我按下 return 并且键盘消失时,键盘的高度(对于 iPhone 8 模拟器)被打印为正确的值 260.0.我的问题是如何在开始编辑时访问键盘的值?
试试这个。您可以仅使用 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 { [=11=].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
}
}
感谢多个在线资源。
.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)")
}
}
所以我有一个 class,其中包含一个名为 keyboardHeight 的已发布变量,用于检索 keyboardHeight 的值:
import UIKit
class KeyboardHeightHelper: ObservableObject {
@Published var keyboardHeight: CGFloat = 0
init() {
self.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) { (notification) in
self.keyboardHeight = 0
}
}
}
然后,在 ContentView 中,我只有一个 TextField,当您 start/stop 编辑该字段时,它应该打印键盘高度:
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 的时间)。当我按下 return 并且键盘消失时,键盘的高度(对于 iPhone 8 模拟器)被打印为正确的值 260.0.我的问题是如何在开始编辑时访问键盘的值?
试试这个。您可以仅使用 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 { [=11=].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
}
}
感谢多个在线资源。
.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)")
}
}