Elhoej
Elhoej

Reputation: 751

How to pass a variable with a #selector function?And what to pass to textField: UITextField?

So I have an UIAlertController, with 2 textfields, I then want to monitor whenever those textfields are edited to see if they meet a certain criteria.

In this case, I want my alert button to be inactive as long as both textfields input is shorter than a number I want to pass to the function.

So I added this target to both textfields:

textField.addTarget(self, action: #selector(self.handleTextFieldEdit), for: .editingChanged)

inside handleTextFieldEdit I have this:

func handleTextFieldEdit(_ textField: UITextField, number: Int)
{
    let number = number
    let alertController:UIAlertController = self.presentedViewController as! UIAlertController;
    let textField: UITextField  = alertController.textFields![0]
    let confirmField: UITextField = alertController.textFields![1]
    let addAction: UIAlertAction = alertController.actions[0];

    if ((textField.text?.characters.count)! > number && (confirmField.text?.characters.count)! > number)
    {
        addAction.isEnabled = true
    }
    else if ((textField.text?.characters.count)! <= number || (confirmField.text?.characters.count)! <= number )
    {
        addAction.isEnabled = false
    }
}

Now my first problem is I can't seem to pass any variables with a #selector func.

My second problem is I don't know what to pass to _ textField: UITextField

I have tried both UITextField and textField (which is what I declared my UITextFields to).

The error that comes up is: Cannot convert value of type 'UITextField'.Type to expected argument type 'UITextField'.

EDIT: All of my code works whenever I don't pass a variable with the function btw. The problem is I want the minimum value to be changed whenever I call the function.

Upvotes: 3

Views: 2680

Answers (5)

user7014451
user7014451

Reputation:

Here's some tested code. Basically, I've subclassed UIAlertViewController, adding functions to add UITextFields and a UIAlertAction.

The text fields are created by passing a tuple (Int,String) containing the minimum length and placeholder values. The OK button is disabled upon creation.

Internally, I've added your handleTextFieldEdit() function, but refactored. It sets a Bool in a second array - you could simply use a single array but this is simply an example - and then calls a new function to set the okButton.isEnabled property.

In my UIViewController:

public func showAlert(_ title:String, _ message:String) {

    // use the subclassed UIAlertViewController
    //   - add two text fields with minimum length and placeholders
    //   - add an OK button

    let alertVC = MyAlertController(
        title: "MyTitle",
        message: "MyMessage",
        preferredStyle: .alert)
    alertVC.addTextFields([(5,"Enter First Name"),(10,"Enter Last Name")])
    alertVC.addOkAction()

    present(
        alertVC,
        animated: true,
        completion: nil)
}

And my subclassed UIAlertViewController:

class MyAlertController: UIAlertController {

    var textMinLengths = [Int]()
    var textMinLengthsReached = [Bool]()
    var okAction:UIAlertAction!

    // add UITextFields:
    //   - append textMinLengths[]
    //   - set placeholder
    //   - append "false" to textMinLengthsReached[]
    //   - set tag value to array index
    //   - add target action handleTextFieldEdit()

    func addTextFields(_ textConfigs:[(Int,String)]) {
        var tagValue:Int = 0
        for textConfig in textConfigs {
            textMinLengths.append(textConfig.0)
            textMinLengthsReached.append(false)
            self.addTextField { (textField : UITextField!) -> Void in
                textField.placeholder = textConfig.1
                textField.tag = tagValue
                textField.addTarget(self, action: #selector(self.handleTextFieldEdit(_:)), for: .editingChanged)            }
            tagValue += 1
        }
    }

    // add a *disabled* OK button

    func addOkAction() {
        okAction = UIAlertAction(
            title: "OK",
            style: .cancel,
            handler: { action -> Void in
        })
        self.addAction(okAction)
        okAction.isEnabled = false
    }

    // every keystroke, check the character count against the minimum, setting if it's been reached,
    // always checking whether to show alert action afterwards

    func handleTextFieldEdit(_ sender: UITextField) {
        if (sender.text?.characters.count)! >= textMinLengths[sender.tag] {
            textMinLengthsReached[sender.tag] = true
        } else {
            textMinLengthsReached[sender.tag] = false
        }
        showAlertAction()
    }

    // loop through textMinLengthsReached, setting okButton.isEnabled

    func showAlertAction() {
        var isEnabled = true
        for textMinLengthReached in textMinLengthsReached {
            if !textMinLengthReached {
                isEnabled = false
                break
            }
        }
        okAction.isEnabled = isEnabled
    }
}

Upvotes: 1

Dmitry A.
Dmitry A.

Reputation: 598

in order to track changes in text field some object must be delegate of this text field

for example when you create AlertController with text field inside you should assign delegate to it.

somewhere inside of method of some YourViewController:

self.alertView = UIAlertController.init(title: "Your title", message: Your message", preferredStyle: .alert)
self.alertView.addTextField(configurationHandler: { textfield in
textfield.font = UIFont.fontProximaNovaLight(18)
textfield.textColor = UIColor.colorAppLightGray2()
textfield.keyboardType = .default
textfield.delegate = self 
})

Extending your viewController:

extension YourViewController: UITextFieldDelegate {

    func textField(_ textField: UITextField,
                   shouldChangeCharactersIn range: NSRange,
                   replacementString string: String) -> Bool {

        let text = textField.text?.replacingCharacters( in: range.toRange(textField.text!), with: string)
        // some test verification
        // for example you can use your function to verify textfield against value
        // validate (textfield, number)



        if text?.characters.count > 0 {
            self.alertView.actions[0].isEnabled = true
        } else {
            self.alertView.actions[0].isEnabled = false
        }

        return true
    }
}

Upvotes: 0

John D.
John D.

Reputation: 558

Subclass UITextField and add another property to be used for your length requirement. Then you can read it as part of the sender.

Upvotes: 0

Duncan C
Duncan C

Reputation: 131408

You're trying to add an EXTRA parameter (an int) to an IBAction selector. You can't do that. IBActions have one of 3 selectors:

@IBAction func actionNoParams() {
}

@IBAction func actionWSender(_ sender: Any) {
}

@IBAction func actionWSenderAndEvent(_ sender: Any, forEvent event: UIEvent) {
}

Those are your only options. You can't add additional parameters, or change the type of either of the possible parameters.

The system calls your function when the control needs to invoke the target/action.

(Any time you add a target/action to a control you must use one of the function signatures above, even if you don't label your function with the @IBAction keyword (Which you should really be doing, BTW.)

Upvotes: 2

iOS Developer
iOS Developer

Reputation: 437

Try below code

textField.addTarget(self, action: #selector(self.handleTextFieldEdit(sender:)), for: .editingChanged)

func handleTextFieldEdit(sender: UITextField) {
}

Upvotes: 0

Related Questions