Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MessageInputBar UI problem when sending message or scrolling #503

Closed
zoufishanmehdi opened this issue Feb 1, 2018 · 36 comments
Closed

MessageInputBar UI problem when sending message or scrolling #503

zoufishanmehdi opened this issue Feb 1, 2018 · 36 comments

Comments

@zoufishanmehdi
Copy link

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.

@nathantannar4
Copy link
Member

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

Sent with GitHawk

@zoufishanmehdi
Copy link
Author

zoufishanmehdi commented Feb 1, 2018

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

}`

@nathantannar4
Copy link
Member

Are you using Swift 3?

Sent with GitHawk

@nathantannar4
Copy link
Member

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

Sent with GitHawk

@zoufishanmehdi
Copy link
Author

zoufishanmehdi commented Feb 1, 2018

@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.

@nathantannar4
Copy link
Member

Are you using some kind of custom container controller?

Sent with GitHawk

@zoufishanmehdi
Copy link
Author

zoufishanmehdi commented Feb 1, 2018

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
    }
}`

@SD10 SD10 added the bug? label Feb 3, 2018
@zoufishanmehdi
Copy link
Author

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

@nathantannar4
Copy link
Member

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

@zoufishanmehdi
Copy link
Author

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

@zoufishanmehdi
Copy link
Author

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

@nathantannar4
Copy link
Member

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.

@zoufishanmehdi
Copy link
Author

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.

@nathantannar4
Copy link
Member

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

Sent with GitHawk

@nathantannar4
Copy link
Member

Closing as it’s a client side issue

Sent with GitHawk

@zoufishanmehdi
Copy link
Author

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.

@SD10
Copy link
Member

SD10 commented Feb 13, 2018

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

@zoufishanmehdi
Copy link
Author

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

@nathantannar4
Copy link
Member

@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

@andre991
Copy link

andre991 commented Feb 17, 2018

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

@shravansukumar
Copy link

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.

@austinwright
Copy link
Contributor

@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()
        }
    }

@nathantannar4
Copy link
Member

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

@nathantannar4 nathantannar4 reopened this Mar 14, 2018
@austinwright
Copy link
Contributor

austinwright commented Mar 14, 2018

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?

@aybarsyalcin
Copy link

aybarsyalcin commented Mar 20, 2018

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
    }

@austinwright
Copy link
Contributor

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

@SD10
Copy link
Member

SD10 commented May 4, 2018

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

@Shrutimittal91
Copy link

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.

@nathantannar4
Copy link
Member

Are you using the IQKeyboardManager?

@Shrutimittal91
Copy link

No. I am using the default keyboard.

@nathantannar4
Copy link
Member

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

@Shrutimittal91
Copy link

Shrutimittal91 commented May 15, 2018 via email

@Shrutimittal91
Copy link

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

@SD10
Copy link
Member

SD10 commented May 16, 2018

@Shrutimittal91 See #399 #85

@stale
Copy link

stale bot commented Jun 14, 2018

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

@stale stale bot added the stale label Jun 14, 2018
@stale
Copy link

stale bot commented Jun 21, 2018

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 👍.

@stale stale bot closed this as completed Jun 21, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants