I have written a function that brings up an alert that requires the user to make a menu choice. That choice will be used to change a button name. The problem is the button name is changed before the user choice is made. It is changed to the previous function result.

I have read that a handler can be used to delay execution of the result, but I can't figure out how to use it.

@IBAction func selectProp(_ sender: Any) {
    propName.setTitle(menulist(title:"propName", message:""), for: .normal)
    print("selected property = ", choice)    //  }
}

func menulist (title: String, message: String)  -> String {
    let title1 = "Select Property"
    let alert = UIAlertController(title: title1, message: message, preferredStyle:UIAlertControllerStyle.alert)
    let k = rentals.count
    if k > 0 {
        for i in 0 ... k-1 {
            alert.addAction(UIAlertAction(title:rentals[i], style: .default, handler: {action in
                choice = rentals[i]
                print("choice=",choice)
            }))
        }

        alert.addAction(UIAlertAction(title: "Cancel", style: .destructive, handler: {action in
            choice =  "Select"
            print("choice=",choice)
        }))

        self.present(alert, animated: true, completion: nil)
    }
    return choice
}

The problem is the button name is changed before the user choice is made and the print statement is executed before the user makes the choice. The results, button change and print, are based on the previous user input choice.

2 Answers

0
Piallen On

I think I see what's happening here. The @IBAction func selectProp() is being called due to an action (.touchUpInside perhaps?), and it is setting the propName before the user has selected their choice using the alert.

What you need to do here is move the propName.setTitle(...) into the handler itself, so it is called at the point in time that the user selects the option via the alert:

alert.addAction(UIAlertAction(title:rentals[i], style: .default, handler: {action in
    propName.setTitle(rentals[i], for: .normal)
    ....

At the moment, the propName title is being set when the user triggers selectProp, not when the user selects their choice via the alert.

Hope that helps!

Let me know if you have any more questions.

1
Community On

I have read that a handler can be used to delay execution of the result, but can't figure out how to use it.

Exactly, such handlers are called closures. Since UIAlertAction uses closure to deliver the result, you need to use closure in your function instead of returning value.

func showMenuList(title: String, message: String, completion: @escaping (_ rental: String?) -> ()) {
    let title = "Select Property"
    let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)

    // loop through all rentals and add action to alert for each of them
    rentals.forEach { (rental) in
        let rentalAction = UIAlertAction(title: rental, style: .default, handler: { _ in
            completion(rental)
        })

        alert.addAction(rentalAction)
    }

    // add cancel action
    let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: { _ in
        completion(nil)
    })
    alert.addAction(cancelAction)

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

Then you can use this function like this:

showMenuList(title: "propName", message: "") { result in
    guard let result = result else {
        // handle Cancel action
        return
    }

    // handle property selected
    print(result)
}