Sean Saoirse
Sean Saoirse

Reputation: 91

Binding a button (UIView) to keyboard that works in multiple textfields (Swift 4.2)

Sorry if any similar question(s) has been answered. But, I just can't seem to figure this one out.

I have reached my goal, to bind the "Log In" button to the keyboard, basically pushing it from the bottom of the screen to the top of the keyboard using an extension below.

picture: Initial view without keyboard.

picture: keyboard has been launched.

My UIView extension:

import UIKit

 extension UIView{
 func bindToKeyboard(){
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
}


@objc func keyboardWillChange(_ notification: NSNotification){
    let duration = notification.userInfo![UIResponder.keyboardAnimationDurationUserInfoKey] as! Double
    let curve = notification.userInfo![UIResponder.keyboardAnimationCurveUserInfoKey] as! UInt
    let beginningFrame = (notification.userInfo![UIResponder.keyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
    let endFrame = (notification.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
    let deltaY = endFrame.origin.y - beginningFrame.origin.y

    UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIView.KeyframeAnimationOptions(rawValue: curve), animations: {
        self.frame.origin.y += deltaY
    }, completion: nil)
   }
}

and I called bindToKeyboard() to loginBtn in the viewDidLoad() of my LoginVC like so:

loginBtn.bindToKeyboard()

The problem here is, after the first tap to the textfield (either email or password field), the button disappears. After the keyboard is closed, the button is actually back to its initial position just like in the first picture. Then calling the keyboard again by tapping one of those textfields, the button works properly. But the second and so forth tap, it does not.

The point of my question:

  1. how can I implement the extension to be able to work properly with multiple textfields/textviews?
  2. If that's not possible, how should I approach this problem?

I am sorry if my explanation and or English is unclear.

Thank you so much.

Upvotes: 1

Views: 1119

Answers (2)

Mohsin Khubaib Ahmed
Mohsin Khubaib Ahmed

Reputation: 1018

I had the exact same problem and initially I tried handling it via the UIKeyboardNotification method but the problem was when the different UITextField was being edited when the Keyboard had changed its state, it won't register for any state change as it was already active. Therefore after much exploring I handled it via creating an accessoryView with a new button that is a duplication of my actual button. So basically, consider you have a UIButton which is sticking at the bottom of the UIViewController and you have 2 UITextField which when switched interchangeably, cannot feel that the UIButton was ever moved elsewhere but remain stuck on the keyboard. This following piece of code explains how to cater this problem:

@IBOutlet weak var signInBtn: UIButton!
@IBOutlet weak var passwordTextField: UITextField!
@IBOutlet weak var emailTextField: UITextField!

var accessoryViewKeyboard:UIView?
var btnAccessory:UIButton?


override func viewDidLoad() {
    super.viewDidLoad()

    passwordTextField.delegate = self
    emailTextField.delegate = self

    accessoryViewKeyboard = UIView(frame: signInBtn.frame)

    //Inputting the "accessoryViewKeyboard" here as the "inputAccessoryView" is of 
    //utmost importance to help the "signInBtn" to show up on tap of different "UITextFields"
    emailTextField.inputAccessoryView = accessoryViewKeyboard
    passwordTextField.inputAccessoryView = accessoryViewKeyboard

    setupBtnWithKeyboard()
}

func setupBtnWithKeyboard() {
    btnAccessory = UIButton(frame: CGRect(x: signInBtn.frame.origin.x, y: signInBtn.frame.origin.y, width: self.view.frame.size.width, height: signInBtn.frame.size.height))
    accessoryViewKeyboard?.addSubview(btnAccessory!)
    btnAccessory?.translatesAutoresizingMaskIntoConstraints = false
    btnAccessory?.frame = CGRect(x: (accessoryViewKeyboard?.frame.origin.x)!,
                                 y: (accessoryViewKeyboard?.frame.origin.y)!,
                                 width: self.view.frame.size.width,
                                 height: (accessoryViewKeyboard?.frame.size.height)!)

    btnAccessory?.backgroundColor = UIColor(red: 31/255, green: 33/255, blue: 108/255, alpha: 1)
    btnAccessory?.setTitle("Sign In", for: .normal)
    btnAccessory?.titleLabel?.font = .systemFont(ofSize: 22)
    btnAccessory?.titleLabel?.textColor = UIColor.white
    btnAccessory?.titleLabel?.textAlignment = .center
    btnAccessory?.isEnabled = true
    btnAccessory?.addTarget(self, action: #selector(SignIn.signInBtnPressed), for: .touchUpInside)

    NSLayoutConstraint.activate([
        btnAccessory!.leadingAnchor.constraint(equalTo:
            accessoryViewKeyboard!.leadingAnchor, constant: 0),
        btnAccessory!.centerYAnchor.constraint(equalTo:
            accessoryViewKeyboard!.centerYAnchor),
        btnAccessory!.trailingAnchor.constraint(equalTo:
            accessoryViewKeyboard!.trailingAnchor, constant: 0),
        btnAccessory!.heightAnchor.constraint(equalToConstant: signInBtn.frame.size.height),
        ])
}

And you're done. This will keep the UIButton always present on the Keyboard. Important thing is no matter how many instances of UITextField you introduce, always input the accessoryViewKeyboard as its inputAccessoryView.

Upvotes: 0

E.Coms
E.Coms

Reputation: 11539

In this animation, if you use frame to control the position of button, the button is supposed to be free of constrains in vertical direction.

    UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIView.KeyframeAnimationOptions(rawValue: curve), animations: {
    self.frame.origin.y += deltaY
}, completion: nil)

I use this animation well after removing all the constraints from UIButton. Otherwise, self.frame.origin.y += deltaY should be replaced with constraint constant.

Good lucky with moving buttons.

Upvotes: 1

Related Questions