I am trying to update a button's constraints when the keyboard is shown and hidden by adding/subtracting the keyboard's height from the constraint's constant.
I had this working previously, but after some re-factoring, it's stopped working. Previously, keyboardWillShow:
and keyboardWillHide:
were implemented exactly as shown below. I've since tried to use setNeedsUpdateConstraints
and setNeedsLayout
to try to force a refresh, to no avail.
When doing some simple print()
debugging, buttonHorizontalConstraint.constant
does get updated, but the changes just aren't reflected visually.
class RegistrationNameView: UIView {
let questionLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFontOfSize(21.0)
label.text = "Hey, what's your name?"
label.textAlignment = .Center
label.textColor = UIColor.lightGrayColor()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let nameField: UITextField = {
let field = UITextField()
field.autocorrectionType = .No
field.font = UIFont.boldSystemFontOfSize(28.0)
field.placeholder = "Full name"
field.returnKeyType = .Next
field.textAlignment = .Center
field.translatesAutoresizingMaskIntoConstraints = false
return field
}()
let nextButton: UIButton = {
let button = UIButton()
button.setTitle("Continue", forState: .Normal)
button.setTitleColor(UIColor.whiteColor(), forState: .Normal)
button.titleLabel?.font = UIFont.boldSystemFontOfSize(17.0)
button.backgroundColor = UIColor(red: 0.263, green: 0.910, blue: 0.847, alpha: 1)
button.layer.cornerRadius = Global.UISizes.CornerRadius
button.translatesAutoresizingMaskIntoConstraints = false
button.contentEdgeInsets = UIEdgeInsetsMake(16.0, 0, 16.0, 0)
return button
}()
var buttonHorizontalConstraint = NSLayoutConstraint()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.whiteColor()
// Add subviews
self.addSubview(questionLabel)
self.addSubview(nameField)
self.addSubview(nextButton)
nameField.becomeFirstResponder()
// Constraint helpers
let spacer = Global.UISizes.SpacingUnit
let margins = self.layoutMarginsGuide
let layoutConstraints: [NSLayoutConstraint] = {
var constraints = [NSLayoutConstraint]()
// Title Label Constraints
constraints.append(
questionLabel.bottomAnchor.constraintEqualToAnchor(nameField.topAnchor, constant: -(spacer * 2)))
constraints.append(questionLabel.leadingAnchor.constraintEqualToAnchor(margins.leadingAnchor))
constraints.append(questionLabel.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor))
// Description Label Constraints
constraints.append(nameField.topAnchor.constraintEqualToAnchor(margins.centerYAnchor, constant: spacer * -12))
constraints.append(nameField.leadingAnchor.constraintEqualToAnchor(margins.leadingAnchor))
constraints.append(nameField.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor))
// Sign Up Button Constraints
self.buttonHorizontalConstraint = nextButton.bottomAnchor.constraintEqualToAnchor(margins.bottomAnchor, constant: -(spacer * 2))
constraints.append(self.buttonHorizontalConstraint)
constraints.append(nextButton.leadingAnchor.constraintEqualToAnchor(margins.leadingAnchor))
constraints.append(nextButton.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor))
return constraints
}()
NSLayoutConstraint.activateConstraints(layoutConstraints)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo! as NSDictionary).objectForKey(UIKeyboardFrameBeginUserInfoKey)?.CGRectValue.size {
self.buttonHorizontalConstraint.constant -= keyboardSize.height
}
}
func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo! as NSDictionary).objectForKey(UIKeyboardFrameBeginUserInfoKey)?.CGRectValue.size {
self.buttonHorizontalConstraint.constant += keyboardSize.height
}
}
}
class RegistrationNameViewController: UIViewController {
var nameView: RegistrationNameView! { return self.view as! RegistrationNameView }
override func viewDidLoad() {
super.viewDidLoad()
let nameView = RegistrationNameView(frame: CGRectZero)
nameView.nextButton.addTarget(self, action: "nextStep:", forControlEvents: .TouchUpInside)
self.view = nameView
}
func nextStep(sender: AnyObject) {
print("going to the next step \(sender)")
let credentialsViewController = RegistrationCredentialsViewController()
self.navigationController?.pushViewController(credentialsViewController, animated: true)
}
}
class RegistrationNavigationController: UINavigationController, UINavigationControllerDelegate {
var nameViewController: RegistrationNameViewController = RegistrationNameViewController()
var credViewController: RegistrationCredentialsViewController = RegistrationCredentialsViewController()
override init(rootViewController: UIViewController) {
super.init(rootViewController: rootViewController)
self.delegate = self
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
let type = String(viewController.dynamicType)
switch type {
case "RegistrationNameViewController":
// Add keyboard notifications to RegistrationNameViewController
NSNotificationCenter.defaultCenter().addObserver(nameViewController.view,
selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(nameViewController.view,
selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)
case "RegistrationCredentialsViewController":
// Remove keyboard notifications to RegistrationNameViewController before
// registering for new notifications
NSNotificationCenter.defaultCenter().removeObserver(nameViewController.view)
// Add keyboard notifications to RegistrationCredentialsViewController
NSNotificationCenter.defaultCenter().addObserver(credViewController.view,
selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(credViewController.view,
selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)
default:
print("Default")
}
}
}
Thank you for the help!
I solved this on my own after some digging.
I believe the problem is that the NSNotificationCenter
observer registrations were happening on a background thread, which caused the methods that actually impacted UI to not actually change the UI.
Instead of registering observers in navigationController:willShowViewController:animated:
, I register them in viewWillAppear
and unregister them in viewWillDisappear
. This happens in RegistrationNameViewController
instead of RegistrationNavigationController
.
override func viewWillAppear(animated:
NSNotificationCenter.defaultCenter().addObserver(self.view,
selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self.view,
selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)
(self.view as! RegistrationNameView).nameField.becomeFirstResponder()
}
override func viewWillDisappear(animated: Bool) {
NSNotificationCenter.defaultCenter().removeObserver(self.view)
}
This makes navigationController:willShowViewController:animated:
unnecessary, and it can be removed from RegistrationNavigationController.swift