如何使用 SwiftUI 在多个屏幕上获取键盘高度并移动按钮



以下代码在显示键盘时获取键盘高度,并按键盘高度移动按钮。

此移动在过渡源 (ContentView( 和过渡目标 (SecibdContentView( 上以相同的方式执行,但按钮不会在过渡目标处移动。

如何使按钮在多个屏幕上移动相同?

import SwiftUI
struct ContentView: View {
@ObservedObject private var keyboard = KeyboardResponder()
var body: some View {
NavigationView {
VStack {
Text("ContentView")
Spacer()
NavigationLink(destination: SecondContentView()) {
Text("Next")
}
.offset(x: 0, y: -keyboard.currentHeight)
}
}
}
}
import SwiftUI
struct SecondContentView: View {
@ObservedObject private var keyboard = KeyboardResponder()
var body: some View {
VStack {
Text("SubContentView")
Spacer()
NavigationLink(destination: ThirdContentView()) {
Text("Next")
}
.offset(x: 0, y: -keyboard.currentHeight)
}
}
}
class KeyboardResponder: ObservableObject {
private var _center: NotificationCenter
@Published var currentHeight: CGFloat = 0
init(center: NotificationCenter = .default) {
_center = center
_center.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
_center.addObserver(self, selector: #selector(keyBoardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
deinit {
_center.removeObserver(self)
}
@objc func keyBoardWillShow(notification: Notification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
currentHeight = keyboardSize.height
}
}
@objc func keyBoardWillHide(notification: Notification) {
currentHeight = 0
}
}

Using ViewModifier

你可以使用swiftui的ViewModifier要简单得多

import SwiftUI
import Combine
struct KeyboardAwareModifier: ViewModifier {
@State private var keyboardHeight: CGFloat = 0
private var keyboardHeightPublisher: AnyPublisher<CGFloat, Never> {
Publishers.Merge(
NotificationCenter.default
.publisher(for: UIResponder.keyboardWillShowNotification)
.compactMap { $0.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue }
.map { $0.cgRectValue.height },
NotificationCenter.default
.publisher(for: UIResponder.keyboardWillHideNotification)
.map { _ in CGFloat(0) }
).eraseToAnyPublisher()
}
func body(content: Content) -> some View {
content
.padding(.bottom, keyboardHeight)
.onReceive(keyboardHeightPublisher) { self.keyboardHeight = $0 }
}
}
extension View {
func KeyboardAwarePadding() -> some View {
ModifiedContent(content: self, modifier: KeyboardAwareModifier())
}
}

在您看来

struct SomeView: View {
@State private var someText: String = ""
var body: some View {
VStack {
Spacer()
TextField("some text", text: $someText)
}.KeyboardAwarePadding()
}
}

KeyboardAwarePadding()会自动在您的视图中添加填充,它更优雅。

SwiftUI + Combine

@Published var keyboardHeight: CGFloat = 0 // if one is in ViewModel: ObservableObject
private var cancellableSet: Set<AnyCancellable> = []

init() {

NotificationCenter.default.publisher(for: UIWindow.keyboardWillShowNotification)
.map {
guard
let info = $0.userInfo,
let keyboardFrame = info[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect
else { return 0 }
return keyboardFrame.height
}
.assign(to: .keyboardHeight, on: self)
.store(in: &cancellableSet)

NotificationCenter.default.publisher(for: UIWindow.keyboardDidHideNotification)
.map { _ in 0 }
.assign(to: .keyboardHeight, on: self)
.store(in: &cancellableSet)
}

您的代码缺少一些内容。没有导航视图,也没有文本字段来显示键盘。请考虑更新代码。

无论如何,可以通过替换问题轻松解决:keyboardFrameBeginUserInfoKeykeyboardFrameEndUserInfoKey

更新:

您还需要使用相同的键盘响应器,否则您将创建多个实例。或者,您可以将其放入您的环境中。

而且您忘记在代码中包含文本字段,因此将显示键盘以对其进行测试。

以下代码有效:

import SwiftUI
struct ContentView: View {
@ObservedObject private var keyboard = KeyboardResponder()
var body: some View {
NavigationView {
VStack {
Text("ContentView")
TextField("enter text", text: .constant(""))
Spacer()
NavigationLink(destination: SecondContentView(keyboard: keyboard)) {
Text("Next")
}
.offset(x: 0, y: -keyboard.currentHeight)
}
}
}
}
import SwiftUI
struct SecondContentView: View {
@ObservedObject var keyboard: KeyboardResponder
var body: some View {
VStack {
Text("SubContentView")
TextField("enter text", text: .constant(""))
Spacer()
NavigationLink(destination: Text("ThirdContentView()")) {
Text("Next")
}
.offset(x: 0, y: -keyboard.currentHeight)
}
}
}
class KeyboardResponder: ObservableObject {
private var _center: NotificationCenter
@Published var currentHeight: CGFloat = 0
init(center: NotificationCenter = .default) {
_center = center
_center.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
_center.addObserver(self, selector: #selector(keyBoardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
deinit {
_center.removeObserver(self)
}
@objc func keyBoardWillShow(notification: Notification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
currentHeight = keyboardSize.height
}
}
@objc func keyBoardWillHide(notification: Notification) {
currentHeight = 0
}
}

最新更新