Messagekit: MessageInputBar UI problem when sending message or scrolling

Created on 1 Feb 2018  ·  36Comments  ·  Source: MessageKit/MessageKit

General Information

  • MessageKit Version:
    0.13.1

  • iOS Version(s):
    iOS 11.2.2

  • Swift Version:
    Swift 4

  • Devices/Simulators:
    iPhone 7

  • Reproducible in ChatExample? (Yes/No):
    No

What happened?

When I add an image or text in input bar at the bottom and press send, the message input bar UI looks off. You only see a thin horizontal message input bar and then messages on either side of the bar. When you scroll, the message input bar is not sticking to the bottom, but rather a bit above the bottom edge and you can see message bubbles above and below message input bar. Please see the images to get a better idea of the problem.

img_1295
img_1296
img_1298

What did you expect to happen?

I expect the message input bar to stick to the bottom when I'm scrolling or sending messages (images or text) and not have message bubbles appear above and below the message input bar.

bug? stale

Most helpful comment

If you are using IQKeyboardManager framework. it will be affected this situation. You can add this code

 override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        IQKeyboardManager.sharedManager().enable = false
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        IQKeyboardManager.sharedManager().enable = true
    }

All 36 comments

Hi @zoufishanmehdi is this reproducible in the chat example? What is your code for when a message is sent

Sent with GitHawk

Hi @nathantannar4 it didn't happen when I was playing around with chat example. Below you can see the code for sending a message:

`extension ChatVC {
func messageInputBar(_ inputBar: MessageInputBar, didPressSendButtonWith text: String) {
let sender = currentSender()

    let textMessage = ConversationMessage(text: text, senderId: sender.id, senderName: sender.displayName)
    textMessage.conversation = self.conversation
    messageList.append(textMessage)
    messagesCollectionView.insertSections([messageList.count - 1])

    saveConversation(conversationMessage: textMessage, isMediaMessage: false)

    inputBar.inputTextView.text = String()
    messagesCollectionView.scrollToBottom()
}

}`

Are you using Swift 3?

Sent with GitHawk

Also I thought the issue was for when you paste an image in the bar, where are you handling images

Sent with GitHawk

@nathantannar4 I'm using Swift 4. I don't paste the image in the bar. When the user presses camera icon in the left stackview of message input bar, they can select an image or take a picture. For that, I'm using TGCameraViewController library. After they press use this, that's when I make a post request to save the image. Below you can see methods from TGCameraViewController for handling images:

`func cameraDidTakePhoto(_ image: UIImage!) {
let conversationMessage = ConversationMessage()
conversationMessage.image = image
conversationMessage.conversation = self.conversation
saveConversation(conversationMessage: conversationMessage, isMediaMessage: true)

    self.dismiss(animated: true, completion: nil)
}`

`func cameraDidSelectAlbumPhoto(_ image: UIImage!) {
    let conversationMessage = ConversationMessage()
    conversationMessage.image = image
    conversationMessage.conversation = self.conversation
    saveConversation(conversationMessage: conversationMessage, isMediaMessage: true)

    self.dismiss(animated: true, completion: nil)
}`

Also, the problem tends to happen after I send some sort of message (either image or text). It also happens sometimes when I'm scrolling.

Are you using some kind of custom container controller?

Sent with GitHawk

No, not using a custom container controller, just a View Controller. For image styling, I'm using a custom bubble.

`func messageStyle(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageStyle {
let conversationMessage = messageList[indexPath.section]

    switch conversationMessage.data {
    case .photo:
        let configurationClosure = { (containerView: UIImageView) in
            let imageMask = UIImageView()
            imageMask.image = MessageStyle.bubble.image
            imageMask.frame = containerView.bounds
            containerView.mask = imageMask
            containerView.contentMode = .scaleAspectFill

            guard let urlString = conversationMessage.imageURL,
                let url = URL(string: urlString) else {
                    print("Could not convert message into a readable Message format")
                    return
            }
            containerView.af_setImage(withURL: url)
        }
        return .custom(configurationClosure)
    default:
        return .bubble
    }
}`

This problem is still occurring. Are there any updates? Would really appreciate your input!

Hi @zoufishanmehdi we can't reproduce this in the example. Can you please give us a detailed list on how you reproduce it and if possible also attach the code you use in your VC as I expect this is a client side issue.

Sent with GitHawk

@nathantannar4 What's your email address so I can send you the code for my VC and steps to reproduce it?

I just emailed you the information above. Let me know if you need anything else. Thank you!

I think is your issue self.tabBarController?.tabBar.isHidden = true
The bar is sitting on top of the tab bar. Because setting it to hidden doesn’t change the frame.

That makes sense. What's the best way of dealing with that? I'm guessing you either need to remove it from superview or change the frame of the tab bar.

Change the frame. I know there are a lot of samples on stack overflow

Sent with GitHawk

Closing as it’s a client side issue

Sent with GitHawk

Thank you, will look into it. Just as a side note- this problem wasn't happening when I was using JSQMessages and I was hiding the tab bar in viewWillAppear similar to the way I do it now.

Hey @zoufishanmehdi, we do have quite a few differences between JSQMessagesViewController and MessageKit. The biggest difference is the MessageInputBar

@SD10 I see. This problem is still occurring even when I change the frame of the tab bar.

@zoufishanmehdi I'm sorry but in this case it is likely due to some other client side bug in your code. I'd recommend starting from our example project and working to what you need. When reviewing your code there was a lot that looked unused or scattered which makes the problem harder to isolate.

Sent with GitHawk

ezgif com-video-to-gif
I have the same issue.

Even I had the same issue, not only did my input bar start floating, but also the growth of the bar started jumping and went out of frame. So, couldn't pin point the issue, tried removing the tabBar but that was of no help. Finally, I found out that this was occurring because of IQKeyboardManager. Disabling it in the viewWillAppear did the trick for me. Hope this of help.

@nathantannar4 & @SD10, I was able to reproduce this issue (or at least a similar one).
Some technical details as a starting point if I can't get back here with a sample project:

When one of MessageInputBar's InputButtonItems reactive actions is triggerd via textViewDidChange (such as clearing the InputTextView which triggers the default sendButton's onDisabled reaction), and if the closure definition causes the InputTextView to re-size, then when textViewDidChange eventually gets to this expression:

        if requiredInputTextViewHeight != inputTextView.bounds.height {
            // Prevent un-needed content size invalidation
            invalidateIntrinsicContentSize()
        }

the expression will always evaluate to false because the inputTextView.bounds.height will already have been updated. A side-effect of skipping the invalidateIntrinsicContentSize() call in this case could be causing the above issue. If the InputTextView has multiple lines, what happens is the inputTextView gets resized as part of the InputButtonItem action closure, but the MessageInputBar's intrinsic content size does not get invalidated. The MessageInputBar's height is not updated and thus remains larger than needed. If the MessageInputBar is translucent this could be causing the issue as shown in the above GIF.

To workaround this issue, I implemented the MessageInputBarDelegate.messageInputBar(inputBar:textViewTextDidChangeTo:) function as follows:

    func messageInputBar(_ inputBar: MessageInputBar, textViewTextDidChangeTo text: String) {
        let totalPadding = inputBar.padding.top
            + inputBar.padding.bottom
            + inputBar.topStackViewPadding.top
            + inputBar.textViewPadding.top
            + inputBar.textViewPadding.bottom
        // Fix for MessageKit layout bug when MessageInputBar.textViewDidChange() disables the send button and the inputTextView resizes early preventing the MessageInputBar intrinsic content size from getting updated.
        if(text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty &&
            inputBar.requiredInputTextViewHeight == inputBar.inputTextView.bounds.height &&
            inputBar.intrinsicContentSize.height - inputBar.inputTextView.bounds.height > totalPadding) {
                        inputBar.invalidateIntrinsicContentSize()
        }
    }

Wanna make a PR for:

if requiredInputTextViewHeight != inputTextView.bounds.height {
      // Prevent un-needed content size invalidation
     invalidateIntrinsicContentSize()
}

to

invalidateIntrinsicContentSize()

The purpose was to only help improve the performance

If you want to keep the performance improvements I have two ideas:

1: Add an observer to the InputStackView's size, intrinsicContentSize, or some other property of your choosing which performs the conditional check. This should probably be in addition to the conditional check in textViewDidChange.

2: Replace inputTextView.bounds.height in the existing conditional with a computed property which calculates the inputTextView's expected size similar to my workaround:

let verticalPadding = inputBar.padding.top
            + inputBar.padding.bottom
            + inputBar.topStackViewPadding.top
            + inputBar.textViewPadding.top
            + inputBar.textViewPadding.bottom
return inputBar.intrinsicContentSize.height - verticalPadding

What do you think?

If you are using IQKeyboardManager framework. it will be affected this situation. You can add this code

 override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        IQKeyboardManager.sharedManager().enable = false
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        IQKeyboardManager.sharedManager().enable = true
    }

I haven't tested it, but option 3 might be to move the check to happen before iterating on each InputBarButtonItem and calling its textViewDidChangeAction (and before the messageInputBar's textViewTextDidChangeTo delegate method), and delaying the content size invalidation to afterwards. Something like:

    /// Enables/Disables the sendButton based on the InputTextView's text being empty
    /// Calls each items `textViewDidChangeAction` method
    /// Calls the delegates `textViewTextDidChangeTo` method
    /// Invalidates the intrinsicContentSize
    @objc
    open func textViewDidChange() {
        let trimmedText = inputTextView.text.trimmingCharacters(in: .whitespacesAndNewlines)

        if shouldManageSendButtonEnabledState {
            sendButton.isEnabled = !trimmedText.isEmpty || inputTextView.images.count > 0
        }
        inputTextView.placeholderLabel.isHidden = !inputTextView.text.isEmpty

        // Moved to here
        let shouldInvalidateIntrinsicContentSize = requiredInputTextViewHeight != inputTextView.bounds.height

        items.forEach { $0.textViewDidChangeAction(with: inputTextView) }

        delegate?.messageInputBar(self, textViewTextDidChangeTo: trimmedText)

        // Moved above
        if shouldInvalidateIntrinsicContentSize {
            // Prevent un-needed content size invalidation
            invalidateIntrinsicContentSize()
        }
    }

@nathantannar4 Have any input here? Can't tell if this is a client issue or MessageKit issue

Even i am facing the same problem. If press return on keyboard or scroll when keyboard is open, the input textview shifts up.
Can i know the solution for this.

Are you using the IQKeyboardManager?

No. I am using the default keyboard.

This has still been something I cannot reproduce in the Example, are you doing anything else? Using any custom controller containers? Tab bar controllers?

The roortViewController of my application is TabBarController but this
screen do not have any tab bar.

On Sat, May 12, 2018 at 4:22 AM, Nathan Tannar notifications@github.com
wrote:

This has still been something I cannot reproduce in the Example, are you
doing anything else? Using any custom controller containers? Tab bar
controllers?


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/MessageKit/MessageKit/issues/503#issuecomment-388504624,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AH4jc5FpiD77j0vibZtCGweH4HZYpniAks5txhY_gaJpZM4R1_vZ
.

--
Thanks & Regards
Shruti Mittal

The roortViewController of my application is TabBarController but this screen do not have any tab bar.

@Shrutimittal91 See #399 #85

This issue has been marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

This issue has been auto-closed because there hasn't been any activity for at least 21 days. However, we really appreciate your contribution, so thank you for that! 🙏 Also, feel free to open a new issue if you still experience this problem 👍.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

NiklasWilson picture NiklasWilson  ·  4Comments

ChandraPrakashJangid picture ChandraPrakashJangid  ·  3Comments

Leon12345679 picture Leon12345679  ·  3Comments

mlequeux picture mlequeux  ·  3Comments

emmanuelay picture emmanuelay  ·  3Comments