Reputation: 1773
With the help of the following, I was able to follow the button on the keyboard display. However, animation cannot be applied well.
How to show complete List when keyboard is showing up in SwiftUI
import SwiftUI
import Combine
import UIKit
class KeyboardResponder: ObservableObject {
let willset = PassthroughSubject<CGFloat, Never>()
private var _center: NotificationCenter
@Published var currentHeight: CGFloat = 0
var keyboardDuration: TimeInterval = 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
guard let duration:TimeInterval = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double else { return }
keyboardDuration = duration
}
}
@objc func keyBoardWillHide(notification: Notification) {
currentHeight = 0
}
}
import SwiftUI
struct Content: View {
@ObservedObject var keyboard = KeyboardResponder()
var body: some View {
VStack {
Text("text")
Spacer()
NavigationLink(destination: SubContentView()) {
Text("Done")
}
}
.padding(.bottom, keyboard.currentHeight)
animation(Animation.easeInOut(duration: keyboard.keyboardDuration))
}
}
Upvotes: 3
Views: 4090
Reputation: 40489
Your main problem, is that you are using an implicit animation. Not only it may be animating things you may not want to animate, but also, you should never apply .animation() on containers. Of the few warnings in SwiftUI's documentation, this is one of them:
Use this modifier on leaf views rather than container views. The animation applies to all child views within this view; calling animation(_:) on a container view can lead to unbounded scope.
Source: https://developer.apple.com/documentation/swiftui/view/3278508-animation
The modified code removes the implicit .animation()
call and replaces it with two implicit withAnimation
closures.
I also replaced keyboardFrameEndUserInfoKey
with keyboardFrameEndUserInfoKey
, second calls were giving useless geometry.
class KeyboardResponder: ObservableObject {
let willset = PassthroughSubject<CGFloat, Never>()
private var _center: NotificationCenter
@Published var currentHeight: CGFloat = 0
var keyboardDuration: TimeInterval = 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 {
guard let duration:TimeInterval = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double else { return }
keyboardDuration = duration
withAnimation(.easeInOut(duration: duration)) {
self.currentHeight = keyboardSize.height
}
}
}
@objc func keyBoardWillHide(notification: Notification) {
guard let duration:TimeInterval = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double else { return }
withAnimation(.easeInOut(duration: duration)) {
currentHeight = 0
}
}
}
struct ContentView: View {
@ObservedObject var keyboard = KeyboardResponder()
var body: some View {
return VStack {
Text("text \(keyboard.currentHeight)")
TextField("Enter text", text: .constant(""))
Spacer()
NavigationLink(destination: Text("SubContentView()")) {
Text("Done")
}
}
.padding(.bottom, keyboard.currentHeight)
// .animation(Animation.easeInOut(duration: keyboard.keyboardDuration))
}
}
Upvotes: 6