I have written my own function to scroll text fields up when the keyboard shows up. In order to dismiss the keyboard by tapping away from the text field, I've created a UITapGestureRecognizer that takes care of resigning first responder on the text field when tapping away.
However, when selecting one of the entries in the auto completed table, didSelectRowAtIndexPath does not get called. Instead, it seems that the tap gesture recognizer is getting called and just resigns first responder.
I'm guessing there's some way to tell the tap gesture recognizer to keep passing the tap message on down to the UITableView, but I can't figure out what it is. There are only solutions for this in objc and posts from over 9 years ago on stack overflow , I was wondering if there was an updated solution to this! Thank you , Here is my code if you would like to see whats going on :
class ChatLogController : UIViewController, UITextFieldDelegate , UITableViewDelegate , UITableViewDataSource, UIGestureRecognizerDelegate, UIImagePickerControllerDelegate ,UINavigationControllerDelegate {
var messages = [Message]()
var user : User?{
didSet{
observeMessages()
}
}
@IBOutlet weak var tabelView: UITableView!
@IBOutlet weak var messageTextField: UITextField!
@IBOutlet weak var bottomViewHeightConstraint: NSLayoutConstraint!
@IBOutlet weak var currentMessageRecieverImage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
configureTableView()
currentMessageRecieverImage.translatesAutoresizingMaskIntoConstraints = false;
currentMessageRecieverImage.layer.cornerRadius = 30;
currentMessageRecieverImage.layer.masksToBounds = true
currentMessageRecieverImage.contentMode = .scaleAspectFill
if let currentMessageRecieverUser = user{
currentMessageRecieverImage.loadImageUsingCacheWithUrlString(urlString: currentMessageRecieverUser.picURL!);
print(currentMessageRecieverUser.userName!)
}
tabelView.delegate = self;
tabelView.dataSource = self;
messageTextField.delegate = self;
let dragAwayFromTextGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleTapAwayFromTextEdit))
dragAwayFromTextGesture.direction = UISwipeGestureRecognizerDirection.down
dragAwayFromTextGesture.delegate = self;
tabelView.addGestureRecognizer(dragAwayFromTextGesture)
let dragBackToMessages = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipeBackToMessages))
dragBackToMessages.direction = UISwipeGestureRecognizerDirection.right
dragBackToMessages.delegate = self;
tabelView.addGestureRecognizer(dragBackToMessages)
let TapAwayFromTextEditTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapAwayFromTextEdit))
tabelView.addGestureRecognizer(TapAwayFromTextEditTapGesture)
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
@objc func handleSwipeBackToMessages(){
dismiss(animated: false, completion: nil)
}
func observeMessages(){
guard let uid = Auth.auth().currentUser?.uid else{
return;
}
// ref gets user that is logged in
let ref = Database.database().reference().child("user-messages").child(uid)
ref.observe(.childAdded) { (snapshot) in
// gets needed messages
let messageId = snapshot.key
let messagesRef = Database.database().reference().child("messages").child(messageId)
messagesRef.observe(.value, with: { (snapshot) in
guard let dict = snapshot.value as? [String : AnyObject] else{
return
}
let message = Message()
message.imageUrl = dict["imageurl"] as? String
message.fromId = dict["fromid"] as? String
message.text = dict["text"] as? String
message.timestamp = dict["timestamp"] as? String
message.toId = dict["toid"] as? String
if message.chatPartnerId() == self.user?.toId{
self.messages.append(message)
}
DispatchQueue.main.async {
self.tabelView.reloadData()
}
})
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return messages.count;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellid" , for: indexPath) as! CustomChatTableViewCell;
let gray = UIColor(red:0.94, green:0.94, blue:0.94, alpha:1.0)
let red = UIColor(red:1.00, green:0.22, blue:0.37, alpha:1.0)
let message = messages[indexPath.item]
if message.toId == user?.toId{
cell.messageBackground.backgroundColor = red
cell.messageLabel.textColor = UIColor.white
}
else{
cell.messageBackground.backgroundColor = gray
cell.messageLabel.textColor = UIColor.black
}
cell.messageLabel.text = message.text
if message.imageUrl != nil{
//print(message.imageUrl!)
// cell.messageImageView.image = UIImage(named : "user.jpg")
cell.messageImageView.loadImageUsingCacheWithUrlString(urlString: message.imageUrl!)
//print(cell.messageImageView.image.debugDescription)
cell.messageImageView.isHidden = false;
cell.messageImageView.translatesAutoresizingMaskIntoConstraints = false;
cell.messageImageView.contentMode = .scaleAspectFill
cell.messageLabel.isHidden = true
cell.messageBackground.isHidden = true;
}
else
{
cell.messageImageView.isHidden = true;
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.messageLabel.isHidden = false
cell.messageBackground.isHidden = false;
}
return cell;
}
@objc func handleTapAwayFromTextEdit(){
//print("handle tap away from text edit running ")
messageTextField.endEditing(true)
}
@IBAction func backToMessageListPressed(_ sender: Any) {
dismiss(animated: false, completion: nil)
}
@IBAction func infoButtonPressed(_ sender: Any) {
}
// saves text to fire base
@IBAction func sendButtonPressed(_ sender: Any) {
handleSendMessageToDataBase()
}
func handleSendMessageToDataBase(){
let ref = Database.database().reference().child("messages")
// needed for making list in firebase for unique texts
let childRef = ref.childByAutoId()
if messageTextField.text == ""{
return
}
if let message = messageTextField.text{
let toID = user!.toId!
let fromId = Auth.auth().currentUser!.uid
let timeStamp : Int = Int(Int(NSDate().timeIntervalSince1970))
print(timeStamp)
let values = ["text" : message , "toid" : toID , "timestamp" : "\(timeStamp)" , "fromid" : fromId]
childRef.updateChildValues(values, withCompletionBlock: { (error, ref) in
if error != nil{
print(error!)
return
}
let userMessagesref = Database.database().reference().child("user-messages").child(fromId)
let messageID = childRef.key
userMessagesref.updateChildValues([messageID: 1])
let recipientUserMessageRef = Database.database().reference().child("user-messages").child(toID)
recipientUserMessageRef.updateChildValues([messageID: 1])
})
}
messageTextField.text = ""
messageTextField.endEditing(true)
}
@IBAction func sendImageButtonPressed(_ sender: Any) {
handleSendImage()
}
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
print("You selected cell #\(indexPath.row)!")
}
func handleSendImage(){
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
present(imagePickerController, animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
var selectedImageFromPicker : UIImage?
if let editedImage = info["UIImagePickerControllerEditedImage"] as? UIImage{
selectedImageFromPicker = editedImage
}
else if let originalImage = info["UIImagePickerControllerOriginalImage"] as? UIImage{
selectedImageFromPicker = originalImage
}
if let selectedImage = selectedImageFromPicker{
uploadToFireBaseUsingSelectedImage(selectedImage : selectedImage)
}
dismiss(animated: true, completion: nil)
}
private func uploadToFireBaseUsingSelectedImage(selectedImage :
UIImage){
let imageName = NSUUID().uuidString
let refToStorage = Storage.storage().reference().child("message_images").child(imageName)
if let uploadData = UIImageJPEGRepresentation(selectedImage, 0.2){
refToStorage.putData(uploadData, metadata: nil, completion: { (metaData, error) in
if error != nil{
print("failed to upload firebase image when sending in chatlogcontroller")
print(error!)
return
}
if let imageURL = metaData?.downloadURL()?.absoluteString{
self.sendMessageWithImageURL(imageURL: imageURL)
}
})
}
}
private func sendMessageWithImageURL(imageURL : String){
let ref = Database.database().reference().child("messages")
// needed for making list in firebase for unique texts
let childRef = ref.childByAutoId()
let toID = user!.toId!
let fromId = Auth.auth().currentUser!.uid
let timeStamp : Int = Int(Int(NSDate().timeIntervalSince1970))
print(timeStamp)
let values = ["imageurl" : imageURL , "toid" : toID , "timestamp" : "\(timeStamp)" , "fromid" : fromId]
childRef.updateChildValues(values, withCompletionBlock: { (error, ref) in
if error != nil{
print(error!)
return
}
let userMessagesref = Database.database().reference().child("user-messages").child(fromId)
let messageID = childRef.key
userMessagesref.updateChildValues([messageID: 1])
let recipientUserMessageRef = Database.database().reference().child("user-messages").child(toID)
recipientUserMessageRef.updateChildValues([messageID: 1])
})
}
func textFieldDidBeginEditing(_ textField: UITextField) {
UIView.animate(withDuration: 0.2, animations:{
self.bottomViewHeightConstraint.constant = 308;
self.view.layoutIfNeeded()
})
}
func textFieldDidEndEditing(_ textField: UITextField) {
UIView.animate(withDuration: 0.2, animations:{
self.bottomViewHeightConstraint.constant = 50;
self.view.layoutIfNeeded()
})
}
func configureTableView()
{
tabelView.delegate = self;
tabelView.dataSource = self;
tabelView.register(UINib(nibName: "MessageCell" , bundle : nil), forCellReuseIdentifier: "customMessageCell");
tabelView.allowsSelection = true;
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self);
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let message = messages[indexPath.item]
if message.imageUrl != nil{
return 200
}
return (CGFloat((message.text?.count)! + 70) )
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 80
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
handleSendMessageToDataBase()
return true
}
}