Avoiding Keyboard in SwifUI with TextEditor
I'm attempting to recreate a simple version of the iOS notes app. Mind you, I'm a complete Swift novice. My current issue is that I want my view to move up as the keyboard appears. I've implemented some code that does do this, but it has some nasty bugs. It first moves the view up WAY too high, then when you begin typing, the view is where it should be. Here are some photos for representation, as well as my code:
When the keyboard first appears
Code:
class KeyboardResponder: ObservableObject {
@Published var currentHeight: CGFloat = 0
var _center: NotificationCenter
@objc func keyBoardWillShow(notification: Notification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
withAnimation {
currentHeight = keyboardSize.height
print("KEYBOARDSIZE.HEIGHT IN OBSERVER: \(keyboardSize.height)")
}
}
print("KEYBOARD HEIGHT IN OBSERVER: \(currentHeight)")
}
@objc func keyBoardWillHide(notification: Notification) {
withAnimation {
currentHeight = 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)
}
}
First, there is a KeyboardResponder class, listening for keyboard appearances and disappearances (with a published variable for its height).
VStack {
TextEditor(text: $content).padding(.all).foregroundColor(fontColors[self.color]).font(fontStyles[self.font])
HStack {
Spacer()
Button(action: {
self.show = false
}) {
Text("Cancel").foregroundColor(.gray).font(.headline)
}
Spacer()
Button(action: {
self.showPanel = true
}) {
Image(systemName: "textformat").font(.headline).foregroundColor(.white).padding(.all)
}.background(Color.green).clipShape(Circle())
Spacer()
Button(action: {
self.show.toggle()
self.saveData()
}) {
Text("Save").foregroundColor(Color(UIColor.systemBlue)).font(.headline)
}
Spacer()
}
}.padding(.bottom, keyboardResponder.currentHeight)
This is the view, with the editor shown in the photos. At the top of this view I have @ObservedObject var keyboardResponder = KeyboardResponder(). I've tried both .padding(.bottom, keyboardResponder.currentHeight) as well as .offset(y: -keyboardResponder.currentHeight). Anyone know what's going on?
SOLUTION FOUND:
I finally found a solution that works! I got this code from https://augmentedcode.io/2020/03/29/revealing-content-behind-keyboard-in-swiftui/
fileprivate final class KeyboardObserver: ObservableObject {
struct Info {
let curve: UIView.AnimationCurve
let duration: TimeInterval
let endFrame: CGRect
}
private var observers = [NSObjectProtocol]()
init() {
let handler: (Notification) -> Void = { [weak self] notification in
self?.keyboardInfo = Info(notification: notification)
}
let names: [Notification.Name] = [
UIResponder.keyboardWillShowNotification,
UIResponder.keyboardWillHideNotification,
UIResponder.keyboardWillChangeFrameNotification
]
observers = names.map({ name in
NotificationCenter.default.addObserver(forName: name,
object: nil,
queue: .main,
using: handler)
})
}
@Published var keyboardInfo = Info(curve: .linear, duration: 0, endFrame: .zero)
}
fileprivate extension KeyboardObserver.Info {
init(notification: Notification) {
guard let userInfo = notification.userInfo else { fatalError() }
curve = {
let rawValue = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as! Int
return UIView.AnimationCurve(rawValue: rawValue)!
}()
duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as! TimeInterval
endFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! CGRect
}
}
struct KeyboardVisibility: ViewModifier {
@ObservedObject fileprivate var keyboardObserver = KeyboardObserver()
func body(content: Content) -> some View {
GeometryReader { geometry in
withAnimation() {
content.padding(.bottom, max(0, self.keyboardObserver.keyboardInfo.endFrame.height - geometry.safeAreaInsets.bottom))
.animation(Animation(keyboardInfo: self.keyboardObserver.keyboardInfo))
}
}
}
}
fileprivate extension Animation {
init(keyboardInfo: KeyboardObserver.Info) {
switch keyboardInfo.curve {
case .easeInOut:
self = .easeInOut(duration: keyboardInfo.duration)
case .easeIn:
self = .easeIn(duration: keyboardInfo.duration)
case .easeOut:
self = .easeOut(duration: keyboardInfo.duration)
case .linear:
self = .linear(duration: keyboardInfo.duration)
@unknown default:
self = .easeInOut(duration: keyboardInfo.duration)
}
}
}
extension View {
func keyboardVisibility() -> some View {
return modifier(KeyboardVisibility())
}
}
Then just add the modifier to the view you want to move up with the keyboard like so .keyboardVisibility()
from Recent Questions - Stack Overflow https://ift.tt/2V4eyBx
https://ift.tt/eA8V8J
Comments
Post a Comment