nathantannar4 / InputBarAccessoryView

A simple and easily customizable InputAccessoryView for making powerful input bars with autocomplete and attachments
MIT License
1.17k stars 229 forks source link

KeyboardManager doesn't support interactive dismissals with additional bottom space. #198

Closed aldrincb closed 3 years ago

aldrincb commented 3 years ago

Describe the bug Using the KeyboardManager to bind a subview input bar view does not interactively dismiss the keyboard with the correct additionalBottomSpace

To Reproduce I am using InputBarAccessoryView in a similar manner to this issue. The difference is that instead of the bottom space being determined by a UITabBar, it is due to some safe area paddings, and multiple nested view controllers:

SafeAreaConstrainedViewController -> ChatViewController -> ThirdPartyChatViewController

An instance of the InputBarAccessoryView lives within the ThirdPartyChatViewController

A part of issue was fixed here for me.

Here's how I'm using it:

        keyboardManager.bind(inputAccessoryView: subviewInputBar) { () -> CGFloat in
            return -(self.view.window?.safeAreaInsets.bottom ?? .zero)
        }

        // Binding to the messagesCollectionView will enable interactive dismissal
        keyboardManager.bind(to: messagesCollectionView)

The issue is when the interactive keyboard dismissal is triggered, the additionalBottomSpace that is not taken into consideration, and the same layout issue is visible again.

Expected behavior Essentially the issue is here: keyboardManager.bind(to: messagesCollectionView). We want a similar implementation for binding to a scrollView, to allow for an additionalBottomSpace.

Screenshots The layout is correct when not interactively dismissing the keyboard: Simulator Screen Shot - iPhone 11 Pro Max - 2021-03-25 at 13 04 45

Once I begin the interactive dismissal of the keyboard, the layout breaks: Simulator Screen Shot - iPhone 11 Pro Max - 2021-03-25 at 13 05 08

Environment

Additional context Something like this will fix it in KeyboardManager.swift for me. It's probably better to have a flag to determine if the additionalBottomSpace should also be applied for interactive gestures. The default value should replicate the existing behavior.

@@ -44,6 +44,8 @@ open class KeyboardManager: NSObject, UIGestureRecognizerDelegate {

    // MARK: - Properties [Private]

+    private var shouldApplyAdditionalBottomSpaceForInteractiveKeyboardDismissal = false
+    private var additionalBottomSpace: (() -> CGFloat)?

    /// The `NSLayoutConstraintSet` that holds the `inputAccessoryView` to the bottom if its superview
    private var constraints: NSLayoutConstraintSet?

@@ -142,6 +144,7 @@ open class KeyboardManager: NSObject, UIGestureRecognizerDelegate {

        guard let superview = inputAccessoryView.superview else {
            fatalError("`inputAccessoryView` must have a superview")
        }
+        self.additionalBottomSpace = additionalBottomSpace
        self.inputAccessoryView = inputAccessoryView
        inputAccessoryView.translatesAutoresizingMaskIntoConstraints = false
        constraints = NSLayoutConstraintSet(

@@ -287,7 +290,7 @@ open class KeyboardManager: NSObject, UIGestureRecognizerDelegate {

        frame.size.height = window.bounds.height - frame.origin.y
        keyboardNotification.endFrame = frame

-        let yCoordinateDirectlyAboveKeyboard = -frame.height
+        var yCoordinateDirectlyAboveKeyboard = -frame.height
+ 
+        if shouldApplyAdditionalBottomSpaceForInteractiveKeyboardDismissal {
+             yCoordinateDirectlyAboveKeyboard -= (self.additionalBottomSpace?() ?? 0)
+        }
+
        /// If a tab bar is shown, letting this number becoming > 0 makes it so the accessoryview disappears below the tab bar. setting the max value to 0 prevents that
        let aboveKeyboardAndAboveTabBar = min(0, yCoordinateDirectlyAboveKeyboard)
        self.constraints?.bottom?.constant = aboveKeyboardAndAboveTabBar