5

I am working with JSQMessagesViewController and Firebase to implement a chat feature in my app. I had everything working well in my ChatViewController. But now I have moved my ChatViewController into a container view that is within a parent view controller and now the "send" button does not work when the keyboard is expanded.

In other words, in order to send a chat message, I must call view.endEditing(true) on the parent view controller that itself is within a UITabBarController, and then the send button will work. But as long as the keyboard is expanded, the send button doesn't respond. below is my ChatViewController code...

import Foundation
import UIKit
import FirebaseDatabase
import JSQMessagesViewController


final class ChatViewController: JSQMessagesViewController {

    var outgoingBubbleImageView: JSQMessagesBubbleImage!
    var incomingBubbleImageView: JSQMessagesBubbleImage!
    var fireRootRef: FIRDatabaseReference!
    var chatMessages = [JSQMessage]()
    var messagesRefHandle: UInt!
    var chatChannelId: String!


    override func viewDidLoad(){
        super.viewDidLoad()
        self.inputToolbar.contentView.textView.backgroundColor = UIColor.clear
        inputToolbar.alpha = 0.7
        ...
    }


    override func viewWillAppear(_ animated: Bool){
        super.viewWillAppear(animated)
    }

    override func viewDidAppear(_ animated: Bool){
        super.viewDidAppear(animated)
    }


    func setupView(){
     ...
    }    

    override func viewWillDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        removeChatObserver()
    }

    func removeChatObserver(){
       ...
    }

    private func setupMessageBubbles() {
       ...
    }



    override func collectionView(_ collectionView: UICollectionView,
                                 numberOfItemsInSection section: Int) -> Int {
        return chatMessages.count
    }

    override func collectionView(_ collectionView: JSQMessagesCollectionView!, messageBubbleImageDataForItemAt indexPath: IndexPath!) -> JSQMessageBubbleImageDataSource! {
        let message = chatMessages[indexPath.item]
        if message.senderId == senderId {
            return outgoingBubbleImageView
        } else {
            return incomingBubbleImageView
        }
    }


    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = super.collectionView(collectionView, cellForItemAt: indexPath) as! JSQMessagesCollectionViewCell


        let message = chatMessages[indexPath.item]

        if message.senderId == senderId {
            cell.textView!.textColor = UIColor.white
        } else {
            cell.textView!.textColor = UIColor.black
        }

        return cell
    }

    override func collectionView(_ collectionView: JSQMessagesCollectionView!, layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout!, heightForMessageBubbleTopLabelAt indexPath: IndexPath!) -> CGFloat {
        let message = chatMessages[indexPath.item]
        if message.senderId == self.senderId {
            return 0
        }

        if indexPath.item > 0 {
            let previousMessage = chatMessages[indexPath.item - 1]
            if previousMessage.senderId == message.senderId {
                return 0
            }
        }

        return kJSQMessagesCollectionViewCellLabelHeightDefault
    }



    override func collectionView(_ collectionView: JSQMessagesCollectionView!, attributedTextForMessageBubbleTopLabelAt indexPath: IndexPath!) -> NSAttributedString! {

        let message = chatMessages[indexPath.item]

        switch message.senderId {
        case senderId:
            return nil
        default:
            guard let senderDisplayName = message.senderDisplayName else {
            assertionFailure()
            return nil
        }
        return NSAttributedString(string: senderDisplayName)

        }
    }





    //no avatar images
    override func collectionView(_ collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAt indexPath: IndexPath!) -> JSQMessageAvatarImageDataSource! {
        return nil
    }


    override func didPressSend(_ button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: Date!) {
        print("DID PRESS SEND")
        let fireMessagesRef =     fireRootRef.child("messages").child(chatChannelId)
        let itemRef = fireMessagesRef.childByAutoId()
        let messageItem = [
            "text": text,
            K.MessageKeys.senderIdKey: senderId,
            "displayName": senderDisplayName,
        ]
        itemRef.setValue(messageItem)

        JSQSystemSoundPlayer.jsq_playMessageSentSound()

        finishSendingMessage()
    }




    override func didPressAccessoryButton(_ sender: UIButton!) {
        //
    }


    private func observeMessages() {
        ...
    }


    func addMessage(id: String, text: String, name: String) {
        ...
    }


}

I would like to fix the send button so the user can tap send when the keyboard is expanded. It is interesting that in order to dismiss the keyboard I have to call view.endEditing(true) on the parent view controller and not on the child view itself. This made me think that I need to configure the button action on the parent view however i haven't had any success. Thanks for your help

MikeG
  • 3,745
  • 1
  • 29
  • 51
  • A common cause of a view being visible but not being interactive is that it falls outside the bounds of one of its parent views. Views still get drawn outside of their parent's bounds but they can't receive touches. I would compare the frame of the button to the bounds of the views above it in the hierarchy. – Dave Weston Feb 12 '17 at 03:42
  • Thanks for the input Dave but that doesn't seem to be the issue, the bounds of my container view are within the bounds of its parent view. The button that i am referring to is part of the iOS keyboard, it's not a button that I have added/configured myself – MikeG Feb 12 '17 at 18:26
  • Is the button unresponsive? Or does it react to your touch but doesn't cause anything to happen? – Dave Weston Feb 12 '17 at 18:32
  • It is unresponsive when the keyboard is expanded. I can tell because the color off the button text doesn't change when it is touched. When the keyboard is collapsed, the button text changes from a light blue to a darker blue (when the finger is on it), and then back to a light blue when released. But this color change doesn't happen when the keyboard is expanded, so It seems the button is completely unresponsive/disabled – MikeG Feb 12 '17 at 23:33
  • 1
    I'm not sure what the problem could be. If you can share your project or a sample that reproduces the issue, I'm happy to take a deeper look at it. – Dave Weston Feb 13 '17 at 01:02
  • I can't share the project unfortunately :( but I will try to put together a sample project when I find time, thanks for your help – MikeG Feb 13 '17 at 23:09

1 Answers1

0

What I guess is jsq collection view cover the input view, so you are pressing on collection view, not the send button. Put a breakpoint on - (void)jsq_didReceiveKeyboardWillChangeFrameNotification:(NSNotification *)notification in JSQMessagesViewController, check whether CGRectGetHeight(keyboardEndFrame) and insets.bottom for setting collection view bottom is sufficient space to show the input view in your container. A problem is that the jsq controller use autolayout to adjust subviews, the collection view is align with its topLayoutGuide and bottomLayoutGuide which is the view controller thing, when you put a view controller inside another view controller, that may cause confusion.

Take iPhone 6(s)/7 plus for example, the keyboard height is 271, the inputtoolbar is 44 height in controller, so the total height is 315. Therefore CGRectGetHeight(keyboardEndFrame) + insets.bottom should be 315. Put a breakpoint on that line, check whether the sum is 315, if not, that means something calculated wrong.

Update for solution If the cause is indeed mentioned above, try this to solve the problem

self.additionalContentInset = UIEdgeInsets(top: 0, left: 0, bottom: 44, right: 0)

This will add a bottom inset to the collection view. Add this after viewDidLoad

Terence
  • 443
  • 3
  • 13
  • could you add an example in Swift of how to check wether `CGRectGetHeight(keyboardEndFrame)` and `insets.bottom` for setting collection view bottom is sufficient space to show input view in container? I am having difficulty. – MikeG Feb 15 '17 at 16:47
  • Ok I have used a breakpoint but I am not able to find a value for `insets.bottom` anywhere. All I can see is the input tool bar `_preferredDefaultHeight = 44` . Even If you are right and something is being calculated wrong then how would I actually go about fixing it? – MikeG Feb 16 '17 at 14:33
  • It depends on how do you move your `inputToolBar` to your container view controller, without your layouting code, I can't help you. You need to show me how do you use your container wrap your chat view controller. – Terence Feb 16 '17 at 14:46
  • The chat view controller is embedded in a container view via storyboard. I hooked up an IBOutlet to the parent view controller to let me hide/unhide the chat view. And I can call functions in the chat view controller from the parent controller by acessing its child view controllers like `let chatVC = self.childViewControllers[0] as! ChatVC`. The input toolbar is part of JSQMessagesViewController, I did not have to write any custom code for that to work – MikeG Feb 16 '17 at 14:55
  • I am sure that the keyboard response to your container view controller, not your chat view controller, and your container does not have toolbar, so the keyboard height is only 271, and then your chat view controller use this value to adjust collection view inset, which will cover the inputToolbar. That's why it can't be touched, you could try to add a invisible/transparent toolbar with disabling the user interaction, to see if it works, otherwise, change the inset code in `JSQMessagesViewController` if you insist to put it in a container. – Terence Feb 16 '17 at 16:08
  • I get the error `chatvc has no member additionalContentInset`. I tried instead `self.topContentAdditionalInset = 44` but that doesn't work either – MikeG Feb 16 '17 at 17:45
  • you may have an old version, in that case, I can't help you because source code is different. – Terence Feb 16 '17 at 17:57