0

I've been trying to implement this toolbar, where only the 'Next' button is enabled when the top textField is the firstResponder and only the 'Previous' button is enabled when the bottom textField is the firstResponder.

It kind of works, but i need to execute my own code by accessing previous, next and done buttons action methods in other classes(like delegates)

Thanks in advance for your suggestions..

extension UIViewController {

func addInputAccessoryForTextFields(textFields: [UITextField], dismissable: Bool = true, previousNextable: Bool = false) {
for (index, textField) in textFields.enumerated() {

 let toolbar: UIToolbar = UIToolbar()
  toolbar.sizeToFit()
  var items = [UIBarButtonItem]()
  if previousNextable {
    let previousButton = UIBarButtonItem(image: UIImage(named: "Backward Arrow"), style: .plain, target: nil, action: nil)
    previousButton.width = 30
    if textField == textFields.first {
      previousButton.isEnabled = false
    } else {
      previousButton.target = textFields[index - 1]
      previousButton.action = #selector(UITextField.becomeFirstResponder)
    }

    let nextButton = UIBarButtonItem(image: UIImage(named: "Forward Arrow"), style: .plain, target: nil, action: nil)
    nextButton.width = 30
    if textField == textFields.last {
      nextButton.isEnabled = false
    } else {
      nextButton.target = textFields[index + 1]
      nextButton.action = #selector(UITextField.becomeFirstResponder)
    }
    items.append(contentsOf: [previousButton, nextButton])
  }

  let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
  let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: view, action: #selector(UIView.endEditing))
  items.append(contentsOf: [spacer, doneButton])
  toolbar.setItems(items, animated: false)
  textField.inputAccessoryView = toolbar
}
  }
}

I am calling this from other class as :

let field1 = UITextField()
let field2 = UITextField()
addInputAccessoryForTextFields([field1, field2], dismissable: true, previousNextable: true)
Sulthan
  • 128,090
  • 22
  • 218
  • 270
Steve Gear
  • 757
  • 2
  • 16
  • 44

1 Answers1

0

Although I'm not 100% convinced I understand your question, here goes:

From other classes, you want to call the actions of your buttons, but your actions are set to UITextField.becomeFirstResponder and UIView.endEditing.

Rather than call these methods directly, create your own methods the actions should call, and put these calls into those methods.

In addInputAccessoryForTextFields(...) change the previousButton's target and action to:

previousButton.target = self
previousButton.action = #selector(handlePreviousButton)

Now add the new method:

@objc func handlePreviousButton()
{
    // you'll need to associate the previous button to a specific text field 
    // and hang onto that association in your class, such as in a property named textFieldRelatedToPreviousButton.
    self.textFieldRelatedToPreviousButton.becomeFirstResponder()  
}

Now you can call handlePreviousButton() directly from elsewhere in your class, if you wish, or even from other classes.

Update

I just noticed you're extending UIViewController. So you can't add storage by adding a property. You can add storage via objc_setAssociatedObject and then get it via objc_getAssociatedObject, however, to get around this. See this SO or this SO for details on that. So you can, for example, "attach" the textField to your previousButton so that you can access it via the handlePreviousButton() method you add to your extension. And you can pass in the previousButton as a parameter (the sender) to handlePreviousButton() too.

Update 2

Another approach to consider is to use the button's tag property to store the tag value of the related textField. (i.e. each button and its related textField would have the same tag value). So in handlePreviousButton(sender:UIBarButtonItem) you loop through all the UITextField children of your self.view and locate the one whose tag matches sender.tag . Then you can do what you need to that UITextField.

Smartcat
  • 2,834
  • 1
  • 13
  • 25
  • Thanks for the reply @Smartcat. Did not get how to associate the preivous button to a specific text field ("self.textFieldRelatedToPreviousButton"). Can you please provide more info. Thanks – Steve Gear May 12 '18 at 06:41
  • Thanks @Smartcat.. I tried this with var stringProperty:UITextField { get { return objc_getAssociatedObject(self, &AssociatedObjectHandle) as! UITextField } set { objc_setAssociatedObject(self, &AssociatedObjectHandle, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } }for associating but it always return nil. It said ` unexpectedly found nil while unwrapping an Optional value` . How can I fix this ? Thank you – Steve Gear May 12 '18 at 07:15