0

I am trying to add a UIBarButtonItem that triggers my textFieldShouldReturn, but I get this error:

Argument of '#selector' does not refer to an '@objc' method, property, or initializer

UIBarButtonItem

func addToolBar(textField: UITextField) {
    let toolBar = UIToolbar()
    toolBar.barStyle = .default
    let doneButton = UIBarButtonItem(
            title: "Done",
            style: .done,
            target: self,
            action: #selector(textFieldShouldReturn(textField))) //** ERROR **
    toolBar.isUserInteractionEnabled = true
    toolBar.sizeToFit()
    textField.inputAccessoryView = toolBar
}

textFieldShouldReturn:

@objc func textFieldShouldReturn(_ textField: UITextField) {
  print("hi there!")
}

The error suggests that I should need to add objc to my textFieldShouldReturn, but I already have that. I also already tried the following:

Does anyone understand how I can set up my UIBarButtonItem to trigger my textFieldShouldReturn?

Dennis Vennink
  • 1,083
  • 1
  • 7
  • 23
Rbar
  • 3,740
  • 9
  • 39
  • 69

2 Answers2

3

You have several problems here.

  1. The textFieldShouldReturn method is a delegate method of UITextFieldDelegate. It should only be called by a UITextField. It has one parameter - the text field.
  2. The UIBarButtonItem target/selector needs to be a method specific to handling the button being pressed. The selector needs to have one of two possible signatures. That can be a method with no parameter or a method with one parameter that will be the UIBarButtonItem that triggered the event.
  3. You are trying to call a mismatched UITextFieldDelegate method as the selector to your UIBarButtonItem. That simply won't work.

If you want both the text field delegate and the button selector to perform the same action, then have each of the two corresponding methods call a 3rd, common method.

func textFieldShouldReturn(_ textField: UITextField) {
    processReturn()
}

@objc func barButtonAction(_ button: UIBarButtonItem) {
    processReturn()
}

func processReturn() {
    // Do whatever is needed
}

And setup the bar button item with:

let doneButton = UIBarButtonItem(
        title: "Done",
        style: .done,
        target: self,
        action: #selector(barButtonAction)

If your processReturn method needs a reference to the text field then add that parameter. The trick will be obtaining a reference to the text field from the barButtonAction method. It can't be passed as a parameter to that method so you will need to have a property that references the text field.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • Thanks @rmaddy! This worked for me and I was able to get the reference to the textField as well too. – Rbar Nov 02 '17 at 05:51
0

Here using this you can call textFieldSholdReturn method from a button Action :

var textField : UITextField!



 override func viewDidLoad() {
        super.viewDidLoad()

        //Fixed space
        let fixed = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: self, action: nil)
        fixed.width = 10

        //Text
        let text = UIBarButtonItem(title: "My Title", style: UIBarButtonItemStyle.plain, target: self, action: nil)
        text.setTitleTextAttributes([
            NSAttributedStringKey.font : UIFont.systemFont(ofSize: 23.0),
            NSAttributedStringKey.foregroundColor : UIColor.white], for: UIControlState.normal)

        //TextField
        textField = UITextField(frame: CGRect(x: 0, y: 0, width: 150, height: 30))
        textField.delegate = self
        textField.textColor = UIColor.blue
        let border = CALayer()
        let width : CGFloat = 2.0
        border.borderColor = UIColor.white.cgColor
        border.frame = CGRect(x: 0, y: textField.frame.size.height-width, width: textField.frame.size.width, height: textField.frame.size.height)
        border.borderWidth = width
        textField.layer.addSublayer(border)
        textField.layer.masksToBounds = true
        let textFieldButton = UIBarButtonItem(customView: textField)

        //Search Button
        let search = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.search, target: self, action: #selector(ViewController.callExplictly(sender:)))

        //Flexible Space
        let flexible = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: self, action: nil)

        //Toolbar
        let toolbar = UIToolbar(frame: CGRect(x: 0, y: 20 , width: view.frame.width, height: 50))
        toolbar.sizeToFit()
        toolbar.barTintColor = UIColor.orange
        toolbar.isTranslucent = false
        toolbar.tintColor = UIColor.white
        toolbar.items = [fixed, text, fixed, textFieldButton, flexible, search]
        view.addSubview(toolbar)
    }

You need to use a method as selector Not just directly calling Delgate method of textfield

-- Adding Selector to a UIBarButton :

 //Search Button
 let search = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.search, target: self, action: #selector(ViewController.callExplictly(sender:)))

Here required Method to call the delegate Method

@objc func callExplictly(sender: UIBarButtonItem){
        print("its called")
        if let textField = textField, let textFieldDelegate = textField.delegate {
            if textFieldDelegate.textFieldShouldReturn!(textField) {
                textField.endEditing(true)
            }
        }
 //here let textField --- Here I am just using. reference to TextField used  = textField --- this is textField declared and being used in Bar 
 //I am calling textField.endEditing(true) to dismiss opened Keyboard as Done button refer that My action required on TF is completed now I can Dismiss keyboard too.
 // As here textFieldDelegate.textFieldShouldReturn!  -- (!) this symbolises as textFieldShouldReturn is a delegate method of TextField and I am accessing that using a reference which is not of textField Type So I am using ! as optional so, it dont jump over the function and call it

    }

 func textFieldDidBeginEditing(_ textField: UITextField) {

        view.backgroundColor = UIColor.yellow

    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        view.backgroundColor = UIColor.cyan

        //Hide the keyboard
        textField.resignFirstResponder()
    }

Here is Delegate method which is called

 func textFieldShouldReturn(_ textField: UITextField) -> Bool {

        view.backgroundColor = UIColor.white
        print("Called")

        //Hide the keyboard
        textField.resignFirstResponder()
        return true
    }

Also check my code file for your reference :

Link : https://drive.google.com/open?id=0Bz8kF1Gedr7fcTdEX29WX1JvYlU
iOS Geek
  • 4,825
  • 1
  • 9
  • 30
  • 1
    1. Where does `textField` come from inside the `callExplicitly` method? 2. Why is there a `!` when calling `textFieldDelegate.textFieldShouldReturn!(textField)`? 3. Why are you calling `endEditing`? You will have already resigned the first responder. – rmaddy Nov 02 '17 at 05:02
  • Did you checked my Code File I shared here ? if let textField // I am creating a reference = textField // this TF is used in Bar – iOS Geek Nov 02 '17 at 05:05
  • Please check my updated Answer here I Posted some more comments there – iOS Geek Nov 02 '17 at 05:13
  • @iOSGeek This didn't work for me, but as always, thanks for a response. As a side note, it seems as though linking to a personal google drive file would be bad practice as it easily could lead SO users to a potentially malicious file.. just food for thought – Rbar Nov 02 '17 at 05:58