2

I am working with chat screens where I have a InputAccessoryView where user type message and UICollectionView which contains chats. I want to display latest chat message when user send it in UICollectionView. Problem is when user types message InputAccessoryView comes up to a keyboard and UICollectionView goes behind to Keyboard so latest messages are not visible. I want to move UICollectionView up to keyboard height when keyboard appears.

I am using following code.

This will register observer for keyboard hide and show events.

func handleKeyBoard(){
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    }


@objc func keyboardWillShow(notification: NSNotification) {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
            var contentInset = self.collectionView?.contentInset
            contentInset?.bottom = keyboardSize.height
            self.collectionView?.contentInset = contentInset!
            if self.messages.count > 0{
                UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
                    self.view.layoutIfNeeded()

                }, completion: { (completed:Bool) in
                    let indexPath = IndexPath(item: self.messages.count - 1, section: 0)
                    self.collectionView?.scrollToItem(at: indexPath, at: .bottom, animated: true)
                })

            }
        }
    }

In above function when keyboard appears I am getting it's height which always comes as 50 I don't know why may be because of InputAccessoryView. After getting keyboard height I am adding 50 more as a height for InputAccessoryView and then changing the contentInset of a UICollectionViewController and in the completion of animation I am scrolling to a last message which will scroll UICollectionViewController to it's last message.

But it is not happening.

This is my default contentInset for UICollectionView.

UIEdgeInsets(top: 8, left: 0, bottom: 52, right: 0)

after

var contentInset = self.collectionView?.contentInset
                contentInset?.bottom = keyboardSize.height
                self.collectionView?.contentInset = contentInset!

contentInset becomes this.

UIEdgeInsets(top: 8, left: 0, bottom: 52, right: 0)

Following will set defaul Inset for collectionview once keyboard hides

 @objc func keyboardWillHide(notification: NSNotification) {
            if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
              self.collectionView?.contentInset = UIEdgeInsets(top: 8, left: 0, bottom: 52, right: 0)

                UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
                    self.view.layoutIfNeeded()

                }, completion: { (completed:Bool) in

                })
            }
        }

can anyone help me to find right approach to do this. That will be very helpful. Almost all chatting app have this feature where when they type then can see a new message in there chat view.

Thank You.

Deepak
  • 1,030
  • 2
  • 10
  • 21

3 Answers3

0

Best way is to add constrains to collectionView and InputAccessoryView such that bottomview of collectionview is attached to accessory view and accessory view to bottom of super view. enter image description here enter image description here

Next Make the outlet of bottom space constrain in you .m file and call is scrollViewBottomSpace and add the following code. Constrain will do the work for you.

#pragma mark - Key board notifications

- (void)addObservers
{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}

- (void)removeObservers
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

#pragma mark - Keyboard notification handlers

- (void)keyboardWillShow:(NSNotification *)notification
{
    if(_keyboardIsVisible) return;

    CGFloat height = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;

    [UIView animateWithDuration:0.2 animations:^{
        scrollViewBottomSpace.constant = height+40;
    } completion:^(BOOL finished) {

    }];

    _keyboardIsVisible = YES;
}

- (void)keyboardWillHide:(NSNotification *)notification
{
    if(!_keyboardIsVisible) return;

    [UIView animateWithDuration:0.2 animations:^{
        scrollViewBottomSpace.constant = 0;
    } completion:^(BOOL finished) {

    }];

    _keyboardIsVisible = NO;
}
Himan Dhawan
  • 894
  • 5
  • 23
  • I am not using any storyboards. Also I am using `UICollectionViewControll` so the constraints for `UICollectionView` is sets by default. And for `InputAccessoryView` also I have not set any constraints it appears as it is by default. `self.autoresizingMask = .flexibleHeight` i have set only this property into the view of `InputAccessoryView` which adjust it's height base on content inside it. – Deepak Jul 13 '18 at 07:22
  • when first time chat screen appears keyboardWillShow method calls automatically and that time I am getting actual keyboard height. But after that when I click in `inputAccessoryView` to type message and keyboard appears it gives height of 50 which is same as `inputAccessoryView` height. That means I am not getting actual height of keyboard. – Deepak Jul 13 '18 at 07:48
0

it's very simple, if you are using UICollectionViewController you don't need to manage the contentInset by your self just let the CollectionViewController handle it by it self, all you have to is to scroll down when your 'textView' or 'textField' becomes first responder or when you send a message

To scroll collectionView down you just use this function

func scrollToBottom() {
    let numberOfSections = self.collectionView!.numberOfSections
    if numberOfSections > 0 {
        let numberOfRows = self.collectionView!.numberOfItems(inSection: numberOfSections - 1)
        if numberOfRows > 0 {
            let indexPath = IndexPath(row: numberOfRows-1, section: (numberOfSections-1))
            self.collectionView!.scrollToItem(at: indexPath, at: .bottom, animated: true)
        }
    }
}
Amr Mohamed
  • 2,290
  • 4
  • 20
  • 39
  • `let indexPath = IndexPath(item: self.messages.count - 1, section: 0) self.collectionView?.scrollToItem(at: indexPath, at: .bottom, animated: true)` this two lines doing the same what you suggested. – Deepak Jul 17 '18 at 06:24
  • First line gets the last item index and the second line scrolls to that line a bottom alignment so that you the cells on top of inputAccessoryView, I didn't understand what you want to know ? – Amr Mohamed Jul 17 '18 at 17:32
  • Hello @Amr the problem was `UIKeyboardFrameBeginUserInfoKey` because of this i was not getting actual height of keyboard. Instead of that I used this `UIKeyboardFrameEndUserInfoKey` and now I am able to get height of keyboard.The remaining problem was that when `KeyboardWillHide` method was getting called it automatically calls the `KeyboardWillShow` but at this time in `KeyboardWillShow` method the height of keyboard was 50 not the actual height. – Deepak Jul 18 '18 at 06:08
  • so I add a constraint if height is more than 50 then change the `UICollectionViewInsets` here is the solution I posted. [solution](https://stackoverflow.com/questions/51355483/input-accessory-view-behave-unexpectedly-with-keyboard-hide-and-show-events?noredirect=1#comment89684874_51355483) – Deepak Jul 18 '18 at 06:08
0

At this thread I have answer this question the problem was with UIKeyboardFrameBeginUserInfoKey instead of that I used UIKeyboardFrameEndUserInfoKey

Deepak
  • 1,030
  • 2
  • 10
  • 21