I am using NSNotificationCenter to shift the main view frame up when a keyboard is shown.
However, I only want this to work for when one UITextField is selected.
Here is what I have so far:
func getKeyboardHeight(notification: NSNotification) -> CGFloat {
let userInfo = notification.userInfo
let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue
return keyboardSize.CGRectValue().height
}
func keyboardWillShow(notification: NSNotification) {
self.view.frame.origin.y -= getKeyboardHeight(notification)
}
func keyboardWillHide(notification: NSNotification) {
self.view.frame.origin.y += getKeyboardHeight(notification)
}
func subscribeToKeyboardNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
}
func unsubscribeToKeyboardNotifications() {
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
}
func subscribeToKeyboardHide() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)
}
func unsubscribeToKeyboardHide() {
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
In ViewWillAppear()
self.subscribeToKeyboardNotifications()
self.subscribeToKeyboardHide()
In ViewWillDisappear()
self.unsubscribeToKeyboardNotifications()
self.unsubscribeToKeyboardHide()
How do I make the view shift up when I am only selecting one specific UITextField?
Try this
if yourTextfield.isEditing{}
Or this
if yourTextfield.isFirstResponder{}
To check is this is your textfield
You can create the following extension
extension UIView {
/**
Finds the first responder
:returns: the first responder, or nil if nothing found.
*/
private func findFirstResponder() -> UIView? {
if isFirstResponder() { return self }
else {
for view in subviews as! [UIView] {
if let responder = view.findFirstResponder() {
return responder
}
}
return nil
}
}
}
Then call it like this in your keyboardWillShow
let textField = UIApplication.sharedApplication().keyWindow?.findFirstResponder() as? UITextField
You can now use textField currently active.
Related
so I have a small app where I choose an image, get it into an image view
and I have 2 textfield to insert a funny phrase
(Meme editor app)
my problem is since the bottom textfield is covered when the keyboard is shown I had to shift the view upwards every time the keyboard is shown for the bottom texfield and I succeed in doing that, what goes wrong is that every time I re-tap the beginning or the end of an existing text in the text filed the view shifts up again in undesirable behavior
here is a small GIF that shows what happens exactly
here is my code so far:
Function to get Kyboard height:
func getKeyboardHeight(_ notification:Notification) -> CGFloat {
let userInfo = notification.userInfo
let keyboardSize = userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! NSValue // of CGRect
return keyboardSize.cgRectValue.height
}
Function to shift the view up in the condition that bottom textfield is what the user taps
#objc func keyboardWillShow(_ notification:Notification) {
if bottomTextField.isFirstResponder{
view.frame.origin.y -= getKeyboardHeight(notification)
}
}
Function to return the view to its normal position when the user finishes editing the bottom text field
#objc func keyboardWillHide(_ notification:Notification) {
view.frame.origin.y = 0
}
Functions to add and remove observers of keyboard notifications
func subscribeToKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
}
func subscribekeyboardWillHide() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
func unsubscribeFromKeyboardNotifications() {
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
}
func unsubscribekeyboardWillHide() {
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
}
and where I call them
override func viewWillAppear(_ animated: Bool) {
super .viewWillAppear(true)
cameraButton.isEnabled = UIImagePickerController.isSourceTypeAvailable(.camera)
subscribeToKeyboardNotifications()
subscribekeyboardWillHide()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
unsubscribeFromKeyboardNotifications()
unsubscribekeyboardWillHide()
}
if you could be kind to provide a simple explanation for your solution I would appreciate it
This is what I do while managing keyboard. It causes no problems.
Declare in your UIViewController class
private let notificationCenter = NotificationCenter.default
then in
override func viewDidLoad() {
super.viewDidLoad()
notificationCenter.addObserver(self, selector: #selector(adjustForKeyboard), name: UIResponder.keyboardWillHideNotification, object: nil)
notificationCenter.addObserver(self, selector: #selector(adjustForKeyboard), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
}
This is the adjust for keyboard function
#objc private func adjustForKeyboard(notification: Notification) {
guard let userInfo = notification.userInfo else { return }
guard let keyboardScreenEndFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return }
let keyboardViewEndFrame = view.convert(keyboardScreenEndFrame, from: view.window)
if notification.name == UIResponder.keyboardWillHideNotification {
scrollView.contentInset = UIEdgeInsets.zero
} else {
scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardViewEndFrame.height, right: 0)
}
scrollView.scrollIndicatorInsets = scrollView.contentInset
}
Deinit here -
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(true)
notificationCenter.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
notificationCenter.removeObserver(self, name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
}
Every time you change the cursor location, UIResponder.keyboardWillShowNotification notification triggered, thats why every time you tap the textfield it moves up one keyboard height more.
You can use UIResponder.keyboardDidShowNotification notification instead of UIResponder.keyboardWillShowNotification. This one is not triggered when cursor location changes.
I have a view which contains 4 textfields and a button .. this view is not the main view .. the problem is that the keyboard covers the textfields down and the button!
i tried this code but its not working:
// Move the text field in a pretty animation!
func moveTextField(_ textField: UITextField, moveDistance: Int, up: Bool) {
let moveDuration = 0.3
let movement: CGFloat = CGFloat(up ? moveDistance : -moveDistance)
if textField.tag>=2{
UIView.beginAnimations("animateTextField", context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(moveDuration)
self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
UIView.commitAnimations()}
}
and i have set the tag numbers for them from 0 to 4...
and i have set the delegate in viewDidLoad:
fname.delegate=self
email.delegate=self
mobile1.delegate=self
mobile2.delegate=self
but this didn't work still the keyboard will cover the textfields down and the button! why? and how to solve it?
UPDATE:
I think i figured out the problem ... i tried this:
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange(notification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange(notification:)), name: NSNotification.Name.UIKeyboardDidHide, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange(notification:)), name: NSNotification.Name.UIKeyboardDidChangeFrame, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardDidHide, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardDidChangeFrame, object: nil)
}
#objc func keyboardWillChange(notification: Notification){
guard let keyboardRect = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else{
return
}
if activeTF.tag >= 2 {
if notification.name == Notification.Name.UIKeyboardDidShow || notification.name == Notification.Name.UIKeyboardDidChangeFrame {
view.frame.origin.y = -keyboardRect.height
}else{
view.frame.origin.y = 0
}
}
}
with:
func textFieldShouldReturn(_ textField: UITextField) -> Bool
{
textField.resignFirstResponder()
return true
}
both funcs works perfectly... but when i change it to:
func textFieldShouldReturn(_ textField: UITextField) -> Bool
{
// Try to find next responder
if let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField {
nextField.becomeFirstResponder()
} else {
// Not found, so remove keyboard.
textField.resignFirstResponder()
}
//do not add a line break
return false
}
only the maketextfieldreturn will work and keyboardwillchange will not work! Why?
I have six UITextFields in my view and I want to decide whether the view has to move or not. How can I check which TextField is selected before moving my view?
Thats my Code for showing the keyboard and moving the view:
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
self.view.frame.origin.y -= keyboardSize.height - 85
}
}
func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
self.view.frame.origin.y += keyboardSize.height - 85
}
}
If you want to know which UITextField is selected, you can use textFieldDidBeginEditing and textFieldDidEndEditing
func textFieldDidBeginEditing(textField: UITextField!) {
currentSelectedTextField = textField
}
func textFieldDidEndEditing(textField: UITextField!) {
currentSelectedTextField = nil
}
For your reference about how to manage keyboard when selecting a UITextField: https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
I have login and registration forms in my application. When user tap any textField I calculate the difference in height of keyboard and textField and scroll my view if needed.
For this I've added two observers in loginVC:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)
func keyboardWillShow(notification: NSNotification) {
println("keyb show")
keyboardIsShown = true
keyboardHeight = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue().height
calculateAnimationValue()
}
func keyboardWillHide(notification: NSNotification) {
println("keyb hide")
keyboardIsShown = false
animateViewMoving(false, moveValue: animationValue)
animationValue = 0
}
func textFieldDidBeginEditing(textField: UITextField) {
pressedTextField = textField
textField.becomeFirstResponder()
if keyboardIsShown {
calculateAnimationValue()
}
}
func calculateAnimationValue() {
if keyboardHeight != nil {
var windowHeight = self.view.frame.height
var textFieldY = pressedTextField.frame.origin.y
var windowWithoutKeyboard = windowHeight - keyboardHeight - 44
if textFieldY > windowWithoutKeyboard {
if animationValue < (textFieldY - windowWithoutKeyboard) {
animationValue = animationValue + textFieldY - windowWithoutKeyboard
animateViewMoving(true, moveValue: animationValue)
}
}
}
println(animationValue)
}
func animateViewMoving (up: Bool, moveValue: CGFloat){
var movementDuration: NSTimeInterval = 0.3
var movement:CGFloat = (up ? -moveValue : moveValue)
UIView.beginAnimations("animateView", context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(movementDuration)
self.view.frame = CGRectOffset(self.view.frame, 0, movement)
UIView.commitAnimations()
}
But I want to do the same in registrationVC.
How can I use keyboardWillShow, keyboardWillHide in another VC?
I've tried to add the same observers but then the application fires these methods in loginVC not in registrationVC.
Can someone help me?
UPDATE: ------------------------
class LogInViewController: UIViewController, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWillShow(notification: NSNotification) {
println("keyb show")
}
func keyboardWillHide(notification: NSNotification) {
println("keyb hide")
}
}
class RegistrationViewController: UIViewController, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWillShow(notification: NSNotification) {
println("keyb show2")
}
func keyboardWillHide(notification: NSNotification) {
println("keyb hide2")
}
deinit {
println("deinit")
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
}
If I touch textField's on loginVC and segue to registerVC it works well and prints "keyb show2".
But if didn't touch textField's on loginVC and segue to registerVC and touch textField it prints "keyb show". Which is called from previous controller. What I am doing wrong?
I have a container UIView that contains a UICollectionView and a UIView, both of which have UITextFields in them. I used NSNotificationCenter to move the container view when the keyboard pops up.
However, it only seems to work for the UICollectionView but not the sub UIView, as shown in the screenshots:
works fine for the text fields in UICollectionView:
but doesn't work for the text field in the UIView:
EDIT: related code:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
kbHeight = keyboardSize.height
self.animateDurationView(true)
}
}
}
func keyboardWillHide(notification: NSNotification) {
self.animateDurationView(false)
}
func animateDurationView(up: Bool) {
var movement = (up ? -kbHeight : kbHeight)
UIView.animateWithDuration(0.3, animations: {
self.durationView.frame = CGRectOffset(self.durationView.frame, 0, movement)
})
}