2020-11-28

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:

Before the keyboard appears

When the keyboard first appears

Once you begin typing

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

No comments:

Post a Comment