如何使用 SwiftUI 在多个屏幕上获取键盘高度并移动按钮
Posted
技术标签:
【中文标题】如何使用 SwiftUI 在多个屏幕上获取键盘高度并移动按钮【英文标题】:How to get the keyboard height on multiple screens with SwiftUI and move the button 【发布时间】:2019-09-01 12:28:57 【问题描述】:以下代码获取键盘显示时的键盘高度,并将按钮移动键盘高度。
在转换源(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
【问题讨论】:
在模拟器上试过了,真是个解决办法。很遗憾看到 SwiftUI 不能原生处理这个问题。还是我错过了什么? 【参考方案1】:使用 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()
会自动在你的视图中添加一个 padding,这样更优雅。
【讨论】:
确实非常优雅的解决方案。处理 safeAreaInsets.bottom ``` func body(content: Content) -> some View content .padding(.bottom, keyboardHeight) .edgesIgnoringSafeArea(keyboardHeight==0 ? [] :.bottom) .onReceive(keyboardHeightPublisher) 的微小改动) self.keyboardHeight = $0 ``` 不错的解决方案,但是如何使它也适用于放置在滚动视图中的 VStack? 由于某种原因,我的键盘高度在69.0
(隐藏时)和336.0
(显示时)之间切换,从来没有 0.0 这很奇怪。有什么想法吗?
@MatrosovAlexander,还添加一个 .edgesIgnoringSafeArea(.bottom)【参考方案2】:
SwiftUI + 组合
@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)
【讨论】:
我猜这应该是在一个类而不是结构中吧?【参考方案3】:您的代码缺少一些内容。没有 NavigationView,也没有让键盘出现的 TextField。考虑更新您的代码。
无论如何,通过将keyboardFrameBeginUserInfoKey
替换为keyboardFrameEndUserInfoKey
即可轻松解决问题。
更新:
您还需要使用相同的 KeyboardResponder,否则您将创建多个实例。或者,您可以将其放入您的环境中。
而且你忘记在代码中包含 TextFields,所以键盘会显示出来进行测试。
以下代码有效:
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
【讨论】:
对不起。添加了导航视图。我将keyboardFrameBeginUserInfoKey更改为keyboardFrameEndUserInfoKey,但无法确认SubContentView中按钮的移动。 您忘记包含文本字段。我更新了我的答案以包含一个可行的解决方案。 一旦有人开始在键盘上打字,这就会停止工作以上是关于如何使用 SwiftUI 在多个屏幕上获取键盘高度并移动按钮的主要内容,如果未能解决你的问题,请参考以下文章