Search code examples
iosswiftuiscrollviewios13uikeyboard

Handling keyboard events when view controller has a scrollview inside


In my project, there is a view controller which is embedded inside a tab bar controller. This view controller has a button which shows a drawer (a slide-in view). I'm using this third-party library for this. I'll refer to it the panel view controller from now on.

enter image description here

This panel view controller is a simple view controller with a scroll view inside. The scrollview has a stack view within it. All the other subviews (green and orange views) are laid out via the stack view.

The orange view has a textfield at the bottom of it. I have written the below code to handle the keyboard events to move the textfield above the keyboard and back down.

@objc private func didReceiveKeyboardNotification(_ notification: Notification) {
    if
        let userInfo = notification.userInfo,
        let endValue = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue,
        let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double,
        let curve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt {

        // Transform the keyboard's frame into our view's coordinate system
        let endRect = view.convert(endValue.cgRectValue, from: view.window)

        // Find out how much the keyboard overlaps the scroll view
        // We can do this because our scroll view's frame is already in our view's coordinate system
        let keyboardOverlap = scrollView.frame.maxY - endRect.origin.y

        // Set the scroll view's content inset to avoid the keyboard
        // Don't forget the scroll indicator too!
        scrollView.contentInset.bottom = keyboardOverlap
        scrollView.verticalScrollIndicatorInsets.bottom = keyboardOverlap

        UIView.animate(withDuration: duration, delay: 0, options: UIView.AnimationOptions(rawValue: curve), animations: {
            self.view.layoutIfNeeded()
        }, completion: nil)
    }
}

After the keyboard is dismissed, the view should move back down to the initial position. The problem is the scrollview does not reset to the original position. The textfield ends up lower than the initial position as you see.

enter image description here

I can't figure out what's the problem here.

Demo project


Solution

  • The problem in your didReceiveKeyboardNotification method. You are using the same method for handling show/hide keyboard, and in both cases, you make scrollView.contentInset.bottom = keyboardOverlap so after keyboard will hide your inset will be set to 0. This is more correct way:

            if notification.name == UIResponder.keyboardWillHideNotification {
                let inset = tabBarController?.tabBar.frame.size.height as! CGFloat //Tabbar height
                scrollView.contentInset.bottom = inset
                scrollView.verticalScrollIndicatorInsets.bottom = inset
            }
    
            if notification.name == UIResponder.keyboardWillShowNotification {
                scrollView.contentInset.bottom = keyboardOverlap
                scrollView.verticalScrollIndicatorInsets.bottom = keyboardOverlap
            }