当 inputAccessoryView 出现时移动集合视图聊天

Posted

技术标签:

【中文标题】当 inputAccessoryView 出现时移动集合视图聊天【英文标题】:move collection view chats up when inputAccessoryView appears 【发布时间】:2018-07-13 06:19:37 【问题描述】:

我正在使用聊天屏幕,其中有一个InputAccessoryView,其中用户键入消息,UICollectionView 包含聊天。 当用户在 UICollectionView 中发送它时,我想显示最新的聊天消息。 问题是当用户键入消息时 InputAccessoryView 出现在键盘上,而 UICollectionView 落后于键盘,因此最新消息不可见。 当键盘出现时,我想将 UICollectionView 移动到键盘高度。

我正在使用以下代码。

这将为键盘隐藏和显示事件注册观察者。

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)
                )

            
        
    

在上面的函数中,当键盘出现时,我得到它的高度总是 50 我不知道为什么可能是因为InputAccessoryView。 获得键盘高度后,我将增加 50 个高度作为 InputAccessoryView 的高度,然后更改 contentInsetUICollectionViewController 并在完成动画时滚动到最后一条消息,该消息会将 UICollectionViewController 滚动到它的最后一条消息。

但这并没有发生。

这是 UICollectionView 的默认 contentInset

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

之后

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

contentInset 变成这个。

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

一旦键盘隐藏,以下将为collectionview设置默认Inset

 @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

                )
            
        

谁能帮我找到正确的方法来做到这一点。这将非常有帮助。 几乎所有聊天应用程序都具有此功能,当他们键入时,可以在聊天视图中看到一条新消息。

谢谢。

【问题讨论】:

像IQKeyboardManager这样的库可以用来处理此类问题 【参考方案1】:

最好的方法是给collectionView和InputAccessoryView添加约束,这样collectionview的bottomview附加到accessory view,accessory view附加到super view的底部。

下一步在你的.m文件中设置底部空间约束的出口,调用scrollViewBottomSpace并添加以下代码。 Constrain 将为您完成工作。

#pragma mark - 键盘通知

- (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;

【讨论】:

我没有使用任何故事板。我也在使用UICollectionViewControll,所以UICollectionView 的约束是默认设置的。对于InputAccessoryView,我也没有设置任何约束,默认情况下它会出现。 self.autoresizingMask = .flexibleHeight 我只将此属性设置到 InputAccessoryView 的视图中,它会根据其中的内容调整它的高度。 当第一次聊天屏幕出现时,keyboardWillShow 方法会自动调用,那时我得到了实际的键盘高度。但在那之后,当我点击inputAccessoryView 输入消息并出现键盘时,它给出的高度为50,与inputAccessoryView 高度相同。这意味着我没有得到键盘的实际高度。【参考方案2】:

这很简单,如果您使用UICollectionViewController,则无需自己管理contentInset,只需让CollectionViewController 自己处理即可,您只需在您使用时向下滚动即可'textView' 或 'textField' 成为第一响应者或当您发送消息时

要向下滚动collectionView,只需使用此功能

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)
        
    

【讨论】:

let indexPath = IndexPath(item: self.messages.count - 1, section: 0) self.collectionView?.scrollToItem(at: indexPath, at: .bottom, animated: true) 这两行与您的建议相同。 第一行获取最后一个项目索引,第二行滚动到该行底部对齐,以便您在 inputAccessoryView 顶部的单元格,我不明白您想知道什么? 你好@Amr 问题是UIKeyboardFrameBeginUserInfoKey 因为这个我没有得到键盘的实际高度。而不是我使用了这个UIKeyboardFrameEndUserInfoKey,现在我可以获得键盘的高度。剩下的问题是当KeyboardWillHide方法被调用时,它会自动调用KeyboardWillShow,但此时在KeyboardWillShow方法中键盘高度是 50 不是实际高度。 所以如果高度超过 50,我会添加一个约束,然后更改 UICollectionViewInsets 这是我发布的解决方案。 solution【参考方案3】:

在这个thread 我已经回答了这个问题,问题在于UIKeyboardFrameBeginUserInfoKey 而不是我使用的UIKeyboardFrameEndUserInfoKey

【讨论】:

以上是关于当 inputAccessoryView 出现时移动集合视图聊天的主要内容,如果未能解决你的问题,请参考以下文章

如何在 inputAccessoryView 内的 UILabel 中使用 AutoScrollLabel?

UITextField 的 InputAccessoryView 在旋转时消失

调用“reloadInputViews”后 inputAccessoryView 不会自动出现

inputAccessoryView 出现键盘时不调用

ObjC iOS11:在 inputAccessoryView 中成为第一响应者时,TextField 不显示

当用户点击 InputAccessoryView 时如何滚动到 CollectionView 的底部