Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITableView adds height of keyboard to contentSize when the keyboard appears on iOS11

I'm working on a chat that should work on iOS 11 and 12. On iOS 12 everything works as expected. On iOS 11, however, I have the problem that the table view content size increases (no cells) as soon as the keyboard appears. The amount of extra height matches with the keyboard height.

Demo

Here is a demo with iOS 11 on the left and iOS 12 on the right. On iOS 12 everything works just fine. Put attention to the bottom of the table view on iOS 11 when the keyboard appeared.

Table view content size iOS 11 Table view content size iOS 12

View/View controller hierarchy setup

- = View controller
+ = View

- UINavigationViewController
    - UIViewController // Controlling contentInsets, contentOffset of the tableView
        + UIView
        - UITableViewController
            + UITableView
        - UIViewController // Controlling the text input bar at the bottom
            + ... // Other views
            + UITextView

Layout Constraints

The table view's anchors are equal to its superview's anchors. So fullscreen, ignoring safe area. So when the keyboard appears the frame doesn't change, but the bottom content insets.

More Details

I've set tableView.contentInsetAdjustmentBehavior = .never

This is how I calculate the insets and offset of the table view when the keyboard appears. It's complex because there are several scenarios where there should be different behavior. There's a similar complex calculation when the keyboard disappears, and when the height of the text input changes. I always want to scroll the table view up or down according to view frame changes.

@objc func handleKeyboardWillShowNotification(_ notification: NSNotification) {
    let frameEnd: CGRect = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as AnyObject).cgRectValue ?? .zero
    let keyboardHeight = frameEnd.height
    let contentHeight = tableView.contentSize.height
    let visibleTableViewHeight = tableView.frame.height - (tableView.contentInset.top + tableView.contentInset.bottom)
    let distanceToScroll = (keyboardHeight - view.safeAreaInsets.bottom)
    var y: CGFloat = 0
    if contentHeight > visibleTableViewHeight {
        y = tableView.contentOffset.y + distanceToScroll
    } else {
        let diff = visibleTableViewHeight - contentHeight
        let positionAtKeyboard = distanceToScroll - tableView.contentInset.top - diff
        y = positionAtKeyboard < tableView.contentInset.top ? -tableView.contentInset.top : positionAtKeyboard
    }
    let contentOffset = CGPoint(x: 0, y: y)
    tableView.contentInset.bottom = keyboardHeight + inputBar.frame.height
    tableView.scrollIndicatorInsets = tableView.contentInset
    tableView.setContentOffset(contentOffset, animated: false)
}

I also have tried this on different screen sizes and it always adds an amount to the contentSize that matches exactly the height of the keyboard.

like image 287
Lukas Würzburger Avatar asked Feb 26 '19 10:02

Lukas Würzburger


2 Answers

You can use following code for keyboard hide and show.

//Show keyboard.

@objc func keyboardWillAppear(_ notification: NSNotification) {
    if let newFrame = (notification.userInfo?[ UIResponder.keyboardFrameEndUserInfoKey ] as? NSValue)?.cgRectValue {
        if self.tableView.contentInset.bottom == 0 {
            let insets: UIEdgeInsets = UIEdgeInsets( top: 0, left: 0, bottom: newFrame.height, right: 0 )
            self.tableView.contentInset = insets
            self.tableView.scrollIndicatorInsets = insets
            UIView.animate(withDuration: 0.1) {
                self.view.layoutIfNeeded()
            }
        }
    }
}

//Hide keyboard.

@objc func keyboardWillDisappear(_ notification: NSNotification) {
    if self.tableView.contentInset.bottom != 0 {
        self.tableView.contentInset = UIEdgeInsets( top: 0, left: 0, bottom: 0, right: 0 )
        self.tableView.scrollIndicatorInsets = UIEdgeInsets( top: 0, left: 0, bottom: 0, right: 0 )
        UIView.animate(withDuration: 0.1) {
            self.view.layoutIfNeeded()
        }
    }
}

This is work for me.

like image 156
Mohd Sultan Avatar answered Nov 17 '22 16:11

Mohd Sultan


First of all you don't have to do unnecessary calculation Simply calculate the keyboard height, and move the keyboard upside.

Swift Version:

@objc func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        self.tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height + 10, right: 0)
        UIView.animate(withDuration: 0.25) {
            self.tableView.layoutIfNeeded()
            self.view.layoutIfNeeded()
        }
    }
}

@objc func keyboardWillHide(notification: NSNotification) {

    self.tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    UIView.animate(withDuration: 0.5) {
        self.tableView.layoutIfNeeded()
        self.view.layoutIfNeeded()
    }
}

Objecttive-C Version:

- (void)keyboardWillShow:(NSNotification *)notification
{
    NSDictionary *keyInfo = [notification userInfo];
    CGRect keyboardFrame = [[keyInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];

    self.tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardFrame.size.height + 10, 0);

    [UIView animateWithDuration:0.2 animations:^{
        [self.tableView layoutIfNeeded];
        [self.view layoutIfNeeded];
    } completion:nil];
}

- (void) keyboardWillHide:  (NSNotification *) notification
{
    self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
    [UIView animateWithDuration:0.2 animations:^{
        [self.view layoutIfNeeded];
    } completion:nil];
}

Let me know If you find any difficulties. This works for me very well

like image 29
VIJAY SINGH RAGHAV Avatar answered Nov 17 '22 15:11

VIJAY SINGH RAGHAV