Problem
I have an application that has a user registration view. It has many UITextField, and many of these have a picker with a toolbar embedded to close the picker i.e:
myTextField.inputView = myPicker
myTextField.inputAccessoryView = myToolbar
Essentially I want to reuse these text fields in different parts of my application, so I thought of subclassing UITextField, something like PickerUITextField.
Attempt
I've tried something like this:
class PickerUITextField: UITextField {
let picker = UIPickerView()
let toolbar = UIToolbar()
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func setup() {
withToolbar()
self.inputView = self.picker
self.inputAccessoryView = self.toolbar
}
private func withToolbar() {
toolbar.barStyle = UIBarStyle.default
toolbar.isTranslucent = true
let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
target: nil, action: nil)
let doneButton = UIBarButtonItem(title: "Done", style: .done,
target: self, action: #selector(removeToolBar))
toolbar.setItems([space, doneButton], animated: false)
toolbar.isUserInteractionEnabled = true
toolbar.sizeToFit()
}
#objc func removeToolBar() {
self.resignFirstResponder()
}
}
Question
However, how can I detect in the view controller that the user has pressed the "Done" button of my PickerUITextField? In other words:
class UserRegistrationViewController: UIViewController {
#IBOutlet weak var country: PickerUITextField!
// I want this to be triggered whenever the country picker closes
func didSelectCountry() {
print("User selected \(country.text!)")
}
}
Thank you for the help.
You can create a closure in PickerUITextField to perform done button action.
class PickerUITextField: UITextField {
let picker = UIPickerView()
let toolbar = UIToolbar()
var doneBtnAction:(() -> Void)?
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func setup() {
withToolbar()
self.inputView = self.picker
self.inputAccessoryView = self.toolbar
}
private func withToolbar() {
toolbar.barStyle = UIBarStyle.default
toolbar.isTranslucent = true
let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
target: nil, action: nil)
let doneButton = UIBarButtonItem(title: "Done", style: .done,
target: self, action: #selector(removeToolBar))
toolbar.setItems([space, doneButton], animated: false)
toolbar.isUserInteractionEnabled = true
toolbar.sizeToFit()
}
#objc func removeToolBar() {
doneBtnAction?()
self.resignFirstResponder()
}
}
And in your view controller, you can assign a closure. It will be called when you tap the done button.
class UserRegistrationViewController: UIViewController {
#IBOutlet weak var country: PickerUITextField!
override func viewDidLoad() {
super.viewDidLoad()
country.doneBtnAction = { [weak self] in
print("User selected \(self?.country.text!)")
}
}
}
You can use a protocol/delegate:
protocol PickerUITextFieldDelegate: class {
func didSelectCountry()
}
class PickerUITextField: UITextField {
// UITextField already have a 'delegate' we need a different name
weak var pickerDelegate: PickerUITextFieldDelegate?
let picker = UIPickerView()
let toolbar = UIToolbar()
#objc func removeToolBar() {
self.resignFirstResponder()
self.pickerDelegate?.didSelectCountry()
}
}
// We need to implement the PickerUITextFieldDelegate delegate here:
class UserRegistrationViewController: UIViewController, PickerUITextFieldDelegate {
#IBOutlet weak var country: PickerUITextField!
// Don't forget to set the delegate!
override func viewDidLoad() {
super.viewDidLoad()
self.country.pickerDelegate = self
}
// This will now be triggered by the delegate
func didSelectCountry() {
print("User selected \(country.text!)")
}
}
Related
I want to pass text of textField in custom class to ViewController and populate it to array when BarButtonItem of DatePicker is tapped. I used a callback as completion handler, but it caught EXC-BAD-ACCESS. What made this error and how could I pass text to my ViewController?
Custom class of textField
class HourDatePicker: UITextField {
var datePicker = UIDatePicker()
override init(frame: CGRect) {
super.init(frame: frame)
commominit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commominit()
}
func commominit(){
text = ""
datePicker.datePickerMode = .dateAndTime
datePicker.minuteInterval = 30
datePicker.locale = Locale(identifier: "ja")
datePicker.addTarget(self, action: #selector(setText), for: .valueChanged)
setText()
inputView = datePicker
inputAccessoryView = customPicker()
}
#objc func setText(){
let f = DateFormatter()
f.dateStyle = .full
f.timeStyle = .short
f.locale = Locale(identifier: "ja")
textColor = .black
text = "\(f.string(from: datePicker.date))"
}
private func customPicker() -> UIToolbar {
let toolbar = UIToolbar()
toolbar.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: 40)
let space = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: self, action: nil)
space.width = 100
let flexSpaceItem = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
let todayButtonItem = UIBarButtonItem(title: "today", style: .done, target: self, action: #selector(setToday))
let selectButtonItem = UIBarButtonItem(title: "select", style: .done, target: self, action: #selector(tellCalenderText))
let toolbarItem = [space, flexSpaceItem, todayButtonItem, selectButtonItem]
toolbar.setItems(toolbarItem, animated: true)
}
return toolbar
}
#objc func tellCalenderText(completion: ((_ titleText: String) -> Void)){
//I want to pass text here.
if text != "" {
guard let titleText = text else {return}
completion(titleText)
} else {
return
}
}
ViewController
class Calender1ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
private let selectDate = HourDatePicker()
private var keepDate: [String] = []
#IBOutlet weak var timeTextView: UITextView!
#IBOutlet weak var dateText: HourDatePicker!
#IBOutlet weak var calenderTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.calenderTableView.delegate = self
self.calenderTableView.dataSource = self
selectDate.tellCalenderText {[weak self] (titleText) in
self?.bringDate(title: titleText)
}
}
func bringDate(title: String){
print("title: \(title)")
self.keepDate.append(title)
timeTextView.text.append(contentsOf: "\(title)\n")
}
Thank you.
This is error log.
error log
First, I think you're getting a bit messed up by having:
private let selectDate = HourDatePicker()
and having:
#IBOutlet weak var dateText: HourDatePicker!
and then making use of selectDate inside viewDidLoad()...
Give this a try. I only made a few changes, and tried to include enough comments to make it clear. I think you'll find this a much more straight-forward way of getting your custom HourDatePicker class to pass information back to the view controller:
class HourDatePicker: UITextField {
var datePicker = UIDatePicker()
override init(frame: CGRect) {
super.init(frame: frame)
commominit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commominit()
}
func commominit(){
text = ""
datePicker.datePickerMode = .dateAndTime
datePicker.minuteInterval = 30
datePicker.locale = Locale(identifier: "ja")
datePicker.addTarget(self, action: #selector(setText), for: .valueChanged)
setText()
inputView = datePicker
inputAccessoryView = customPicker()
}
#objc func setToday(){
datePicker.date = Date()
}
#objc func setText(){
let f = DateFormatter()
f.dateStyle = .full
f.timeStyle = .short
f.locale = Locale(identifier: "ja")
textColor = .black
text = "\(f.string(from: datePicker.date))"
}
private func customPicker() -> UIToolbar {
let toolbar = UIToolbar()
toolbar.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: 40)
let space = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: self, action: nil)
space.width = 100
let flexSpaceItem = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
let todayButtonItem = UIBarButtonItem(title: "today", style: .done, target: self, action: #selector(setToday))
let selectButtonItem = UIBarButtonItem(title: "select", style: .done, target: self, action: #selector(tellCalenderText))
let toolbarItem = [space, flexSpaceItem, todayButtonItem, selectButtonItem]
toolbar.setItems(toolbarItem, animated: true)
return toolbar
}
// "callback" closure
var tellController: ((String) ->())?
// triggered by "select" bar button tap
#objc func tellCalenderText() -> Void {
// get text from self
guard let t = text else {
return
}
// execute the callback closure
tellController?(t)
}
// #objc func tellCalenderText(completion: ((_ titleText: String) -> Void)){
// //I want to pass text here.
// if text != "" {
// guard let titleText = text else {return}
// completion(titleText)
// } else {
// return
// }
//
// }
}
//ViewController
class Calender1ViewController: UIViewController {
// not needed
//private let selectDate = HourDatePicker()
private var keepDate: [String] = []
// UITextView connected via Storyboard
#IBOutlet weak var timeTextView: UITextView!
// UITextField set to HourDatePicker, connected via Storyboard
#IBOutlet weak var dateText: HourDatePicker!
#IBOutlet weak var calenderTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// set the callback closure for the HourDatePicker
dateText.tellController = { [weak self] (titleText) in
self?.bringDate(title: titleText)
}
// not needed
//selectDate.tellCalenderText {[weak self] (titleText) in
// self?.bringDate(title: titleText)
//}
}
func bringDate(title: String){
print("title: \(title)")
self.keepDate.append(title)
timeTextView.text.append(contentsOf: "\(title)\n")
}
}
I have a class for constructing UIBarButtonItems:
enum KeyboardToolbarButton: Int {
case done = 0
case cancel
case back, backDisabled
case forward, forwardDisabled
func createButton(target: Any?, action: Selector?) -> UIBarButtonItem {
var button: UIBarButtonItem!
switch self {
case .back:
button = UIBarButtonItem(title: "<=", style: .plain, target: target, action: action)
case .backDisabled:
button = UIBarButtonItem(title: "<=", style: .plain, target: target, action: action)
button.isEnabled = false
case .forward:
button = UIBarButtonItem(title: "=>", style: .plain, target: target, action: action)
case .forwardDisabled:
button = UIBarButtonItem(title: "=>", style: .plain, target: target, action: action)
button.isEnabled = false
case .done:
button = UIBarButtonItem(title: "DONE", style: .plain, target: target, action: action)
case .cancel:
button = UIBarButtonItem(title: "CANCEL", style: .plain, target: target, action: action)
}
button.tag = rawValue
return button
}
static func detectType(barButton: UIBarButtonItem) -> KeyboardToolbarButton? {
return KeyboardToolbarButton(rawValue: barButton.tag)
}
}
Class for constructing KeyboardToolbar from KeyboardToolbarButton:
class KeyboardToolbar {
weak var toolBarDelegate: KeyboardToolbarDelegate?
var textField: UITextField!
init(textField: UITextField) {
self.textField = textField
self.textField.autocorrectionType = .no
self.textField.inputAssistantItem.leadingBarButtonGroups = []
self.textField.inputAssistantItem.trailingBarButtonGroups = []
}
func setup(leftButtons: [KeyboardToolbarButton], rightButtons: [KeyboardToolbarButton]) {
let leftBarButtons = leftButtons.map { (item) -> UIBarButtonItem in
return item.createButton(target: self, action: #selector(self.buttonTapped(sender:)))
}
let rightBarButtons = rightButtons.map { (item) -> UIBarButtonItem in
return item.createButton(target: self, action: #selector(self.buttonTapped(sender:)))
}
let groupLeading: UIBarButtonItemGroup = UIBarButtonItemGroup.init(barButtonItems: leftBarButtons, representativeItem: nil)
let groupTrailing: UIBarButtonItemGroup = UIBarButtonItemGroup.init(barButtonItems: rightBarButtons, representativeItem: nil)
textField.inputAssistantItem.leadingBarButtonGroups.append(groupLeading)
textField.inputAssistantItem.trailingBarButtonGroups.append(groupTrailing)
}
#objc func buttonTapped(sender: UIBarButtonItem) {
if let type = KeyboardToolbarButton.detectType(barButton: sender) {
print(type)
toolBarDelegate?.keyboardToolbar(button: sender, type: type, tappedIn: self)
}
}
}
And delegate:
protocol KeyboardToolbarDelegate: class {
func keyboardToolbar(button: UIBarButtonItem, type: KeyboardToolbarButton, tappedIn toolbar: KeyboardToolbar)
}
Here's how I use KeyboardToolbar:
class ViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
addButtons(for: textField, setLeftButtons: [.back, .forward], andRightButtons: [.done])
}
private func addButtons(for textField: UITextField, setLeftButtons leftButtons: [KeyboardToolbarButton] = [], andRightButtons rightButtons: [KeyboardToolbarButton] = []) {
let toolbar = KeyboardToolbar(textField: textField)
toolbar.toolBarDelegate = self
toolbar.setup(leftButtons: leftButtons, rightButtons: rightButtons)
}
}
extension ViewController: KeyboardToolbarDelegate {
func keyboardToolbar(button: UIBarButtonItem, type: KeyboardToolbarButton, tappedIn toolbar: KeyboardToolbar) {
print("Tapped button type: \(type)")
}
}
Here's how it works (this feature available only on iPad)
So, the problem is that #objc func buttonTapped(sender: UIBarButtonItem) never calls. So, #selector(self.buttonTapped(sender:) does not connect to handler. How to fix it?
Update:
According to the answer of Taras Chernyshenko, I added KeyboardToolbar as a member of ViewController:
class ViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
var toolbar: KeyboardToolbar!
override func viewDidLoad() {
super.viewDidLoad()
addButtons(for: textField, setLeftButtons: [.back, .forward], andRightButtons: [.done])
}
private func addButtons(for textField: UITextField, setLeftButtons leftButtons: [KeyboardToolbarButton] = [], andRightButtons rightButtons: [KeyboardToolbarButton] = []) {
toolbar = KeyboardToolbar(textField: textField)
toolbar.toolBarDelegate = self
toolbar.setup(leftButtons: leftButtons, rightButtons: rightButtons)
}
}
Problem is in you design. In KeyboardToolbar class in func setup(leftButtons:, rightButtons:) function you doing next:
let leftBarButtons = leftButtons.map { (item) -> UIBarButtonItem in
return item.createButton(target: self, action: #selector(self.buttonTapped(sender:)))
}
here you are setting action target to KeyboardToolbar class.
Next in addButtons(for textField:, setLeftButtons leftButtons:, andRightButtons rightButtons:) of ViewController you setup your buttons like
let toolbar = KeyboardToolbar(textField: textField)
toolbar.toolBarDelegate = self
toolbar.setup(leftButtons: leftButtons, rightButtons: rightButtons)
but after this function toolbar is dealocated, so actions can't reach their target.
To simple fix, store toolbar into class property
class ViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
var toolbar = KeyboardToolbar?
override func viewDidLoad() {
super.viewDidLoad()
addButtons(for: textField, setLeftButtons: [.back, .forward], andRightButtons: [.done])
}
private func addButtons(for textField: UITextField, setLeftButtons leftButtons: [KeyboardToolbarButton] = [], andRightButtons rightButtons: [KeyboardToolbarButton] = []) {
let toolbar = KeyboardToolbar(textField: textField)
toolbar.toolBarDelegate = self
toolbar.setup(leftButtons: leftButtons, rightButtons: rightButtons)
self.toolbar = toolbar
}
}
I have created a subclass of UILabel, based on an example from here: UILabel doesn't show inputView. I am trying to create an instance of the label inside a class that subclasses UITableViewCell. The issue I am having is that to create an instance of DatePickerLabel it requires an NSCoder.
let dp = DatePickerLabel(coder: NSCoder)
I have this in my class that subclasses UITableViewCell but it doesn't seem to ever be triggered, leading to a null pointer when I run it (I tried using a variable and then assigning it inside this code):
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
print("triggered")
}
Any help is greatly appreciated, DatePickerLabel shown below!
class DatePickerLabel: UILabel {
private let _inputView: UIView? = {
let picker = UIDatePicker()
return picker
}()
private let _inputAccessoryToolbar: UIToolbar = {
let toolBar = UIToolbar()
toolBar.barStyle = UIBarStyle.default
toolBar.isTranslucent = true
toolBar.sizeToFit()
return toolBar
}()
override var inputView: UIView? {
return _inputView
}
override var inputAccessoryView: UIView? {
return _inputAccessoryToolbar
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(doneClick))
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
_inputAccessoryToolbar.setItems([ spaceButton, doneButton], animated: false)
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(launchPicker))
self.addGestureRecognizer(tapRecognizer)
}
override var canBecomeFirstResponder: Bool {
return true
}
#objc private func launchPicker() {
becomeFirstResponder()
}
#objc private func doneClick() {
resignFirstResponder()
}
}
Cheers!
In the question you linked to, the initialisation function was initWithCoder because that it used to load views from a storyboard or nib. You are not doing that.
So, change your init to a “plain” one, like:
init() {
super.init()
...
}
I'm trying to add a "Done" button to a text field.
The black UIToolbar is displaying, but the "Done" button is not.
In the attributes inspector, 'Return key' is set to 'Done' and 'Auto-enable Return Key' is enabled.
The UITextField is connected to the delegate
This is inside a UITableViewCell:
import UIKit
class itemTableViewCell: UITableViewCell, UITextFieldDelegate {
#IBOutlet var itemName: UILabel!
#IBOutlet var itemInput: UITextField!
var textFieldIsBeingEdited: Bool = false
override func awakeFromNib() {
super.awakeFromNib()
itemInput.delegate = self
self.addDoneButtonToKeyboard()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func addDoneButtonToKeyboard(){
let doneToolbar: UIToolbar = UIToolbar(frame: CGRectMake(0, 0, 320, 50))
doneToolbar.barStyle = UIBarStyle.BlackTranslucent
let flexSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: nil, action: nil)
let done: UIBarButtonItem = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.Done, target: itemInput, action: Selector("doneButtonAction"))
let items = NSMutableArray()
items.addObject(flexSpace)
items.addObject(done)
doneToolbar.sizeToFit()
itemInput.inputAccessoryView = doneToolbar
}
func doneButtonAction()
{
self.itemInput.resignFirstResponder()
}
func textFieldShouldReturn(textField: UITextField!) -> Bool {
textField.resignFirstResponder()
return true
}
}
you forget to add the items to your let doneToolbar: UIToolbar
doneToolbar.items = items as [AnyObject]
brief answer
items.addObject(flexSpace)
items.addObject(done)
doneToolbar.items = items as [AnyObject]
doneToolbar.sizeToFit()
itemInput.inputAccessoryView = doneToolbar
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.
RegistrationNameView.swift
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
}
}
}
RegistrationNameViewController.swift
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)
}
}
RegistrationNavigationController.swift
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.
RegistrationNameViewController.swift
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