问题
I've got a scenario where something may need to be renamed and I'd like to use UIAlertController
to collect the new name. I'd like this to be robust for several changes at once (although it's unlikely, I'd like to support it).
The issue is that I need the loop to continue only when the results of the current UIAlertController
are in, as it is, the loop continues immediately and of course can't present more than one at once. I've been stuck on this all day and need help.
I need some kind of method that uses the completion hander to give the green light to the next loop iteration.
This is a simplified version of the problem, the changeNames func is in a UIViewController
.
var names = ["Bob","Kate"]
func changeNames(){
for name in names {
let alert = UIAlertController(title: "Rename \(name)",
message: "What would you like to call \(name)?",
preferredStyle: .alert)
alert.addTextField(configurationHandler: { (textField) in
textField.text = name
})
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { [weak alert] (_) in
print("User changed \(name) to \(alert!.textFields![0].text)")
//Don't continue the loop until now
}))
self.present(alert, animated: true)
}
}
Thanks
回答1:
Looping is hard with asynchronous code, I'd rather go with recursion:
func showChangeAlerts(for texts: [String]) {
guard let value = texts.first else { return }
let alert = UIAlertController(title: "Rename \(value)",
message: "What would you like to call \(value)?",
preferredStyle: .alert)
alert.addTextField(configurationHandler: { (textField
textField.text = value
})
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { [weak alert, weak self] (_) in
let newValue = alert?.textFields![0].text ?? ""
print("User changed \(value) to \(newValue)")
self?.showChangeAlerts(for: Array(texts.dropFirst()))
}))
self.present(alert, animated: true)
}
You pass in an array of strings, and this method will work through them until all have been processed.
EDIT
Ok, just saw that Ron Nabuurs beat me with a very similar solution :) The main difference is that my method doesn't use the additional iterator parameter, but "consumes" strings from the provided array until it's empty. But the principle is the same.
回答2:
I have not tested this because I don't have a mac at the moment. And my Swift is rusty, but the general Idea is there :P. Recursion is the thing to do here:
var names = ["Bob","Kate"]
func changeNames(names: [String], iterator: Int = 0){
if (names.count >= iterator) {
let alert = UIAlertController(title: "Rename \(names[iterator])",
message: "What would you like to call \(names[iterator])?",
preferredStyle: .alert)
alert.addTextField(configurationHandler: { (textField) in
textField.text = names[iterator]
})
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { [weak alert] (_) in
print("User changed \(names[iterator]) to \(alert!.textFields![0].text)")
changeNames(names, iterator + 1)
}))
self.present(alert, animated: true)
}
}
来源:https://stackoverflow.com/questions/49732371/how-to-use-uialertcontroller-in-for-loop