Reputation: 91
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:
I am sorry if my explanation and or English is unclear.
Thank you so much.
Upvotes: 1
Views: 1119
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
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