SwiftUI中处理Keyboard弹出与隐藏
7个月前 • 324次点击 • 来自 iOS
标签: SwiftUI
在UIKit时代处理UITextField & UITextView弹出与隐藏Keyboard的代码虽然也能用,但是那显得特不SwiftUI,SwiftUI下处理TextField的显示和隐藏Keyboard的逻辑和UIKit相同的,Google之找到下文:
Keyboard Avoidance for SwiftUI Views
上代码:
https://github.com/V8tr/KeyboardAvoidanceSwiftUI
当Keyboard显示时上移View
import Combine
import SwiftUI
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 keyboardHeight: CGFloat = 0
func body(content: Content) -> some View {
content
.padding(.bottom, keyboardHeight)
.onReceive(Publishers.keyboardHeight) { self.keyboardHeight = $0 }
}
}
extension View {
func keyboardAdaptive() -> some View {
ModifiedContent(content: self, modifier: KeyboardAdaptive())
}
}
处理过度滑动
在View下部空间完全足够Keyboard显示,依然对View进行了上移操作:
import Combine
import SwiftUI
extension Publishers {
// 1.
static var keyboardHeight: AnyPublisher<CGFloat, Never> {
// 2.
let willShow = NotificationCenter.default.publisher(for: UIApplication.keyboardWillShowNotification)
.map { $0.keyboardHeight }
let willHide = NotificationCenter.default.publisher(for: UIApplication.keyboardWillHideNotification)
.map { _ in CGFloat(0) }
// 3.
return MergeMany(willShow, willHide)
.eraseToAnyPublisher()
}
}
extension Notification {
var keyboardHeight: CGFloat {
return (userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect)?.height ?? 0
}
}
extension View {
func keyboardAdaptive() -> some View {
ModifiedContent(content: self, modifier: KeyboardAdaptive())
}
}
struct KeyboardAdaptive: ViewModifier {
@State private var bottomPadding: CGFloat = 0
func body(content: Content) -> some View {
// 1.
GeometryReader { geometry in
content
.padding(.bottom, self.bottomPadding)
// 2.
.onReceive(Publishers.keyboardHeight) { keyboardHeight in
// 3.
let keyboardTop = geometry.frame(in: .global).height - keyboardHeight
// 4.
let focusedTextInputBottom = UIResponder.currentFirstResponder?.globalFrame?.maxY ?? 0
// 5.
self.bottomPadding = max(0, focusedTextInputBottom - keyboardTop - geometry.safeAreaInsets.bottom)
}
// 6.
.animation(.easeOut(duration: 0.16))
}
}
}
// From https://stackoverflow.com/a/14135456/6870041
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)
}
}
修改完代码,它就不会乱动了:
点击隐藏
在弹出Keyboard后,再次点击TextField则显示操作栏,点击Keyboard上的 return 隐藏Keyboard:
如果你的应用中想要实现简洁的操作,再次点击TextField隐藏Keyboard,可通过以下代码实现:
import SwiftUI
public struct KeyboardDismissModifier: ViewModifier {
public func body(content: Content) -> some View {
content.onTapGesture {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
}
extension TextField {
/// Dismiss the keyboard when pressing on something different then a form field
/// - Returns: KeyboardDismissModifier
public func hideKeyboardOnTap() -> ModifiedContent<Self, KeyboardDismissModifier> {
return modifier(KeyboardDismissModifier())
}
}
给TextField添加上hideKeyboardOnTap():
TextField("Enter something", text: $text)
.hideKeyboardOnTap()
效果如下:
标签