Load keyboard programmatically with textField and send button using swift

Viewed 106

I am new to swift and am trying to achieve a rather simple task. I am creating an iPad application wherein I want to open the keyboard programmatically.

  1. Keyboard should have a textField bar on top to record what's typed using the keyboard.
  2. There should be a button right next to the textField.( as shown in the image )

enter image description here

I tried to achieve the same but doing this :

lazy var textFieldPanel: UIView = {
        let view = UIView(frame: CGRect(x: 0.0, y: self.view.bounds.height, width: self.view.bounds.width, height: 50.0))
        self.view.addSubview(view)
        view.backgroundColor = UIColor.white
        view.borderColor = UIColor.blue
        view.borderWidth = 2.0
        
        let fieldBottomConstraint = NSLayoutConstraint(item: view, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: 0.0)
        customTextFieldBottomConstraint = fieldBottomConstraint
        
        view.translatesAutoresizingMaskIntoConstraints = false
        self.view.addConstraint(.init(item: view, attribute: .left, relatedBy: .equal, toItem: self.view, attribute: .left, multiplier: 1.0, constant: 0.0))
        self.view.addConstraint(.init(item: view, attribute: .right, relatedBy: .equal, toItem: self.view, attribute: .right, multiplier: 1.0, constant: 0.0))
        self.view.addConstraint(fieldBottomConstraint)
        self.view.addConstraint(.init(item: view, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50.0))
        
        return view
    }()

    lazy var customTextField: UITextField = {
        let field = UITextField(frame: CGRect(x: 0.0, y: 0.0, width: self.view.bounds.width - 300 , height: 50.0))
        textFieldPanel.addSubview(field)
        
        field.translatesAutoresizingMaskIntoConstraints = false
        self.view.addConstraint(.init(item: field, attribute: .left, relatedBy: .equal, toItem: textFieldPanel, attribute: .left, multiplier: 1.0, constant: 8.0))
        self.view.addConstraint(.init(item: field, attribute: .right, relatedBy: .equal, toItem: textFieldPanel, attribute: .right, multiplier: 1.0, constant: 8.0))
        self.view.addConstraint(.init(item: field, attribute: .top, relatedBy: .equal, toItem: textFieldPanel, attribute: .top, multiplier: 1.0, constant: 2.0))
        self.view.addConstraint(.init(item: field, attribute: .bottom, relatedBy: .equal, toItem: textFieldPanel, attribute: .bottom, multiplier: 1.0, constant: 2.0))
        let button = UIButton(frame: CGRect(x: self.view.bounds.width - 350, y: 0.0, width: 200 , height: 50.0))
        button.backgroundColor = UIColor.clear
        button.setTitle("Send", for: .normal)
        button.borderWidth = 2.0
        button.borderColor = UIColor.blue
        button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
        textFieldPanel.addSubview(button)
        
        button.translatesAutoresizingMaskIntoConstraints = false
        return field
    }()

and then calling it in the IBAction like :

customTextField.becomeFirstResponder()
customTextFieldBottomConstraint?.constant = -360.0

The issue that I am facing is the following :

  1. The view is comopletely distorted. ( as shown ) enter image description here
  2. Send button View is distorted as it can be seen in the image.
  3. While hiding the keyboard, the textField view still stays which I want it to be removed.
  4. The view is not shifting up when the keyboard launches.

Can anyone help me as to what am I doing wrong here. Any help would be appreciated.

1 Answers

I'll start with better code readability suggestions for you:

  1. Instead of creating constraints with init, there's a much cleaner way to do it:
.init(item: view, attribute: .left, relatedBy: .equal, toItem: self.view, attribute: .left, multiplier: 1.0, constant: 0.0)

// vs 

view.leftAnchor.constraint(equalTo: self.view.leftAnchor),
  1. Instead of self.view.addConstraint(...) for each constraint, you can easily activate a list of them like this:
NSLayoutConstraint.activate([
    view.leftAnchor.constraint(equalTo: self.view.leftAnchor),
    view.rightAnchor.constraint(equalTo: self.view.rightAnchor),
    fieldBottomConstraint,
    view.heightAnchor.constraint(equalToConstant: 50),
])

Now to your mistakes.

  1. You're creating your views with a frame. It's totally pointless when you're using constraints, as auto layout will override original frames with constraint calculated ones.
  2. You're providing no constraints to your button, that's why it's totally misplaced
  3. You need to attach text field right to button left instead of superview right
  4. If you wanna your view to be hidden when there's no keyboard, you need to set fieldBottomConstraint constant to your container height

So your fixed code constraints code will look like:

var customTextFieldBottomConstraint: NSLayoutConstraint!
lazy var textFieldPanel: UIView = {
    let view = UIView(frame: CGRect(x: 0.0, y: self.view.bounds.height, width: self.view.bounds.width, height: 50.0))
    self.view.addSubview(view)
    view.backgroundColor = UIColor.white
    
    let fieldBottomConstraint = view.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: -100)
    customTextFieldBottomConstraint = fieldBottomConstraint
    
    view.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        view.leftAnchor.constraint(equalTo: self.view.leftAnchor),
        view.rightAnchor.constraint(equalTo: self.view.rightAnchor),
        fieldBottomConstraint,
        view.heightAnchor.constraint(equalToConstant: 50),
    ])
    return view
}()

lazy var customTextField: UITextField = {
    let field = UITextField()
    field.translatesAutoresizingMaskIntoConstraints = false
    textFieldPanel.addSubview(field)
    
    let button = UIButton()
    button.backgroundColor = UIColor.clear
    button.setTitle("Send", for: .normal)
    button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
    button.translatesAutoresizingMaskIntoConstraints = false
    textFieldPanel.addSubview(button)
    
    NSLayoutConstraint.activate([
        field.leftAnchor.constraint(equalTo: textFieldPanel.leftAnchor),
        field.topAnchor.constraint(equalTo: textFieldPanel.topAnchor),
        field.bottomAnchor.constraint(equalTo: textFieldPanel.bottomAnchor),
        button.widthAnchor.constraint(equalToConstant: 200),
        button.leftAnchor.constraint(equalTo: field.rightAnchor),
        button.rightAnchor.constraint(equalTo: textFieldPanel.rightAnchor),
        button.topAnchor.constraint(equalTo: textFieldPanel.topAnchor),
        button.bottomAnchor.constraint(equalTo: textFieldPanel.bottomAnchor),
    ])
    return field
}()

And the last thing. You're adding a constant to your constraint when expect keyboard to appear. But keyboard size is different on different devices and even with different system settings

I suggest you using my KeyboardNotifier. This helper class will update your constraint constant according to keyboard appearance/disappearance. No need to update it during becomeFirstResponder anymore. Initialize it like this:

KeyboardNotifier(
    parentView: view,
    constraint: customTextFieldBottomConstraint
)
Related