TexField Disappears

Hi everyone,


So I have a ViewController, inside it i have a TableView and at the bottom of it a View that contains a TextField and a Button.


When the keyboard appears I tried to put the View at the bottom just above the keyboard and my View appears only if I put my View in front like this :

self.view.bringSubviewToFront(SendMessageView)

(Just to be clear my View is visble and I can see it, it only dissapears when I try to move it on top of the keyboard when it appears)

So now that I can see my View when I mooved it above the keyboard, when I begin to type something my View dissapears instantly and I hane no clue why and I don't know how to fix this.

I hope I explained my problem clearly, and of course if needed I can post more of my code, I tried to put a screenshot in the post but it didn't work.


Thanks in advance,


Arkning

Yes, please post the code that involves the textView, anywhere it is.


when you call

self.view.bringSubviewToFront(SendMessageView)


what is self.view ? Is it the view containing TextField and a Button ? Or the overall view ?


You should not have to bring to fromnt, just move the whole view upward with the right offset.

import UIKit

import Foundation


extension UIViewController {

func hideKeyboardWhenTappedAround() {

let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))

tap.cancelsTouchesInView = false

view.addGestureRecognizer(tap)

}


@objc func dismissKeyboard() {

view.endEditing(true)

}

}


class Chat_user: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {


@IBOutlet weak var SendMessageView: UIView!

@IBOutlet weak var SendMessageInput: UITextField!

@IBOutlet weak var SendButton: UIButton!

@IBOutlet weak var MessageList: UITableView!


var cellMessage: CustomMessage!

var count = 0


override func viewDidLoad() {

super.viewDidLoad()

SendMessageInput.delegate = self

self.view.bringSubviewToFront(SendMessageView)

self.view.bringSubviewToFront(SendMessageInput)

self.view.bringSubviewToFront(SendButton)

self.view.sendSubviewToBack(MessageList)

self.tabBarController?.tabBar.isHidden = true

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)

self.hideKeyboardWhenTappedAround()

}


override func viewWillDisappear(_ animated: Bool) {

self.tabBarController?.tabBar.isHidden = false

}


@objc func keyboardWillShow(notification: NSNotification) {

if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {

self.SendMessageView.frame.origin.y -= keyboardSize.height

self.MessageList.frame.origin.y -= keyboardSize.height

let indexPath = NSIndexPath(row: self.count - 1, section: 0)

self.MessageList.scrollToRow(at: indexPath as IndexPath, at: .bottom, animated: true)

}

}


@objc func keyboardWillHide(notification: NSNotification) {

if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {

self.SendMessageView.frame.origin.y += keyboardSize.height

self.MessageList.frame.origin.y += keyboardSize.height

}

}


func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

self.count = 10

return (self.count)

}


func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

self.cellMessage = tableView.dequeueReusableCell(withIdentifier: "message", for: indexPath) as? CustomMessage

return (cellMessage)

}


func textFieldShouldReturn(_ textField: UITextField) -> Bool {

self.view.endEditing(true)

return true

}

}


class CustomMessage: UITableViewCell {


@IBOutlet weak var MessageReceiveView: UIView!

@IBOutlet weak var MessageReceivedText: UILabel!


override func awakeFromNib() {

super.awakeFromNib()

}

}


Thank you for your answer, above this message you'll find the code corresponding to my view.

Also so it's more clear for you, my "Main View" is composed by a "View" and a "TableView", my "View" is composed by a "TextField" and a "Button"

I don't understand your func


func hideKeyboardWhenTappedAround() {

let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))

tap.cancelsTouchesInView = false

view.addGestureRecognizer(tap)

}


Why do you add gesture each time you tap ? And not do it once in viewDidLoad ?


I usually do not use this logic.

When a field is completed, I call resignFirstResponder()on the IBActions connected to the sent Events ; that hides the keyboard ; then adjust the UIViews positions as you do.

I just wanted that when I click outside the keyboard it'll dissmiss it.


I deleted that code and replace it by this in the viewDidLoad:


let singleTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.test))

singleTapGestureRecognizer.numberOfTapsRequired = 1

singleTapGestureRecognizer.isEnabled = true

singleTapGestureRecognizer.cancelsTouchesInView = false

self.view.addGestureRecognizer(singleTapGestureRecognizer)


and this outside the viewDidLoad :


@objc func test(sender: UITapGestureRecognizer) {

self.view.endEditing(true)

}


What i just did above is it a better way of doing it ? Thank you for your help.

If you just want the keyboard to hide when you click outside of a textField, there is a simple way.


In IB, select the enclosing view of the ViewController

Change its class from UIView to UIControl (which is a subclass of UIView) so that you can connect an IBAction.


Then connect it to an IBAction where you resignFistResponder for all the textFields (I think you just have sendMessageInput)


@IBAction func backgroundTap(sender: UIControl) {
    sendMessageInput.resignFirstResponder()
}


Note that I changed the name to start with lowercase, as you should do with all var.

So basically I need to change UIView to UIControl of the view that contain the textField or the "Main View", then I add the code you sent and connect to my TextField in the storyboard ?

Thanks for that but what about my actual problem ?

Could you remind what is your exact problem ?


I understood it was

"I just wanted that when I click outside the keyboard it'll dissmiss it."

That wasn't my main problem, you asked me why do I add gesture and then I replied, because I wanted that my keyboard disappear when I click outside. I found this code on stackoverflow that's why.


It was the problem on the first message.

My View dissapear when I'm typing in the TextField.

So for you the topic was over ? Like honestly I don't understand how you could forget, I know you help me for the keyboard and I thank you for that, but it wasn't at all my problem since the beginning. I'm sorry if this message feels rude but it's not my point at all, I hope you understand and could help me for my real problem.

I tried to implement your code on a small project.


Seems tableView is not really needed for this test.


The view disappears: do you mean it moves offscreen to the top (tha's what I get here)


The problem is that keyboard will show is called several times.


And you offset each time by the keyboard height.


2 ways to solve :


test if the view bottom is already above keyboard: then don't move (in fact that should be done: no need to move in that case)

I let you code this taking into account bottom of view, screenSize height, keyboard height

tesi if bottom of view + keyboard height <= screenSize height

Then do nothing

otherwise, offset by the difference (no need to move more)



test if view has already moved


var hasMoved = false

in keyboardWillShow
    @objc func keyboardWillShow(notification: NSNotification) {
        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
           if !hasMoved {
            self.sendMessageView.frame.origin.y -=  keyboardSize.height
               hasMoved = true
          }
        }
    }


restore to false in


    @objc func keyboardWillHide(notification: NSNotification) {
        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
            self.sendMessageView.frame.origin.y +=  keyboardSize.height
//            self.MessageList.frame.origin.y += keyboardSize.height
            hasMoved = false
        }
    }

Tested : it works OK.


Probably I forgot the initial question because there were many things to look after in your code 😉

Hi,


So I've tested it and I understood that as soon as I'm editing the TextField my sendMessageView reset it's position to the bottom of the screen, but I don't know why this happens and how I can prevent that from happening ?


Kindly,


Arkning.

Likely, the problem is that yopu do not control all the sending of keyboard notifications.


So, to be safe, you should as well test in keyboardWillHide


   @objc func keyboardWillHide(notification: NSNotification) { 
        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue { 
            if hasMoved {
               self.sendMessageView.frame.origin.y +=  keyboardSize.height 
            }
//            self.MessageList.frame.origin.y += keyboardSize.height 
            hasMoved = false 
        } 
    }

Like I said I tried it and testing the hasMoved in the keyboardWillHide doesn't do nothing. Because like I told you as soon as I edit the textField the entire view that contains the textField (sendMessageView) goes to it's original place (the bottom of the screen).


So before the keyboardWillHide trigger myView dissapear and I don't want that to happen, so is there a function or something that allows me to do that ?

I've continued testing and noticed that keyboard height is not the same value in willShow or willHide. And so, the view moves down progressively after each keyboard show.


have you defined constraints for the view ?


What I did to solve the problem


create a property to hold the view original position


    var originalFrame: CGRect = .zero


initialize it


    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        originalFrame = self.sendMessageView.frame
    }

Restore in keyboardWillHide


            self.sendMessageView.frame = originalFrame // .origin.y +=  keyboardSize.height


Note: in fact, you'd probably better move the whole self.view and not the subviews indifdually


For me, no more shift in view position.

Could you test, and if still problem, post the complete code of the class ?

As well as indicate the constraints defined for the view as well as for the tableView.


When you say:

i have a TableView and at the bottom of it a View that contains a TextField and a Button.

Do you mean that the View is below the bottom the tableview

Yes exacly my View is below the bottom of the tableview.

I tried it but I still have the same issue i'm posting my code so you can see what am I doing wrong.


import UIKit

import Foundation

import SocketIO

import SwiftyJSON

import Alamofire


class Chat_user: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {

@IBOutlet weak var SendMessageView: UIView!

@IBOutlet weak var SendMessageInput: UITextField!

@IBOutlet weak var SendButton: UIButton!

@IBOutlet weak var MessageList: UITableView!

var cellMessage: CustomMessage!

var count = 0

var hasMoved = false

var messages: Hoze_Messages!

var originalFrame: CGRect = .zero

override func viewDidLoad() {

super.viewDidLoad()

let singleTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.test))

singleTapGestureRecognizer.numberOfTapsRequired = 1

singleTapGestureRecognizer.isEnabled = true

singleTapGestureRecognizer.cancelsTouchesInView = false

self.view.addGestureRecognizer(singleTapGestureRecognizer)

SendMessageInput.delegate = self

self.view.bringSubviewToFront(SendMessageView)

self.view.bringSubviewToFront(SendMessageInput)

self.view.bringSubviewToFront(SendButton)

self.view.sendSubviewToBack(MessageList)

self.tabBarController?.tabBar.isHidden = true

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)

}

override func viewDidAppear(_ animated: Bool) {

super.viewDidAppear(animated)

originalFrame = self.SendMessageView.frame

}

@objc func test(sender: UITapGestureRecognizer) {

self.view.endEditing(true)

}

override func viewWillDisappear(_ animated: Bool) {

self.tabBarController?.tabBar.isHidden = false

}

@objc func keyboardWillShow(notification: NSNotification) {

if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {

if !self.hasMoved {

self.SendMessageView.frame.origin.y -= keyboardSize.height

self.MessageList.frame.origin.y -= keyboardSize.height

if (self.count > 0) {

let indexPath = NSIndexPath(row: self.count - 1, section: 0)

self.MessageList.scrollToRow(at: indexPath as IndexPath, at: .bottom, animated: true)

}

self.hasMoved = true

}

}

}


@objc func keyboardWillHide(notification: NSNotification) {

if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {

if hasMoved {

self.SendMessageView.frame = originalFrame

//self.SendMessageView.frame.origin.y += keyboardSize.height

self.MessageList.frame.origin.y += keyboardSize.height

}

hasMoved = false

}

}

func json(from object:Any) -> String? {

guard let data = try? JSONSerialization.data(withJSONObject: object, options: []) else {

return nil

}

return String(data: data, encoding: String.Encoding.utf8)

}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

let defaults = UserDefaults.standard

let check = defaults.integer(forKey: "id")

let jsonObject: [String: Any] = [

"user_id1": check,

"user_id2": My_Friends.result![y].id,

]

socket?.on("getmessages") { data, ack in

print("DATA ========================", data)

print("DATA2 ========================", data[0])

let mstring = data[0] as! String

var dictonary:NSDictionary?

if let test = mstring.data(using: String.Encoding.utf8) {

do {

dictonary = try JSONSerialization.jsonObject(with: test, options: []) as? NSDictionary

} catch let error as NSError {

print(error)

}

}

let azer = Hoze_Messages(json: dictonary as! Dictionary<String,Any>) //as! Dictionary<String,Any>)

print(azer)

}

socket?.emit("getmessages", jsonObject)

return (self.count)

}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

self.cellMessage = tableView.dequeueReusableCell(withIdentifier: "message", for: indexPath) as? CustomMessage

if (self.count > 0) {

self.cellMessage.MessageReceivedText.text = self.messages.result?[indexPath.row].text

}

return (cellMessage)

}

func textFieldShouldReturn(_ textField: UITextField) -> Bool {

self.view.endEditing(true)

return true

}


@IBAction func sendMessage(sender: UIButton) {

let defaults = UserDefaults.standard

let check = defaults.integer(forKey: "id")

let name = defaults.string(forKey: "username")

socket?.on("messages") { dataArray, ack in

print("Messages ========================", dataArray)

print("Messages ========================", ack)

}

let jsonObject: [String: Any] = [

"sender_id": String(check),

"recipient_id": String(My_Friends.result![y].id!),

"sender": name!,

"recipient": My_Friends.result![y].user_name!,

"text": self.SendMessageInput.text!,

"status": "0",

"type": "1"

]

print("JSON =====================" , jsonObject)

socket?.emit("message", jsonObject)

self.SendMessageInput.text = ""

//self.MessageList.reloadData()

}

}

Did you try what I proposed 3 weeks ago, on Nov 22, 2018 7:25 AM ? Doesn't seem so.


Sorry if I lost the track of your problem, yopu've waited far too long to provide feedback.



create a property to hold the view original position


    var originalFrame: CGRect = .zero

initialize it


    override func viewDidAppear(_ animated: Bool) { 
        super.viewDidAppear(animated) 
        originalFrame = self.sendMessageView.frame 
    }

Restore in keyboardWillHide


            self.sendMessageView.frame = originalFrame // .origin.y +=  keyboardSize.height


Note: in fact, you'd probably better move the whole self.view and not the subviews individually

TexField Disappears
 
 
Q