Loop Over Multiple Uialertcontroller'S

Loop over multiple UIAlertController's

You are almost there. Having a buffer of alert messages is the correct idea. But instead of showing all the alerts immediately, you should move the showAlert() call to the handler of the UIAlertAction. So if one alert is dismissed, the next will be shown.

Something like this:

var errors : [NSError]!

override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)

let error1 = NSError(domain: "Test1", code: 1, userInfo: [NSLocalizedFailureReasonErrorKey : "Test1 reason."])
let error2 = NSError(domain: "Test2", code: 2, userInfo: [NSLocalizedFailureReasonErrorKey : "Test2 reason."])
let error3 = NSError(domain: "Test3", code: 2, userInfo: [NSLocalizedFailureReasonErrorKey : "Test3 reason."])

errors = [error1, error2, error3]

showError() // show an alert if errors are queued
}

func showError() {
if let error = errors.first {
let alert = UIAlertController(title: error.domain, message: error.localizedDescription, preferredStyle: .Alert)
let okayAction = UIAlertAction(title: "OK", style: .Default) { action in
self.errors.removeAtIndex(0) // remove the message of the alert we have just dismissed

self.showError() // show next alert if there are more errors queued
}
alert.addAction(okayAction)
presentViewController(alert, animated: true, completion: nil)
}
else {
println("All alerts shown")
}
}

Word of advice: Dismissing multiple alerts is very annoying. Maybe you could create a dedicated full screen viewController that shows all the error messages in a UITableView or something. This of course depends on the number of alert messages a typical user will see. If it's regularly more than three I would use a modal UIViewController which shows all messages at a glance.

How to use UIAlertController in For loop

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.

How do I present multiple alert controllers in a for loop in swift

Swift only lets you present one view controller at a time, so you'd have to do something like this:

var blocks: [Block] = [Block.init(one: "1", two: "12"),
Block.init(one: "2", two: "22"),
Block.init(one: "3", two: "32")]

func nextMessage(index: Int) {
if index < blocks.count {
let codeBlock = blocks[index]
self.presentMessage(index: index, title: codeBlock.stringInput1, message: codeBlock.stringInput2)
}
}
func presentMessage(index: Int, title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)

alert.addAction(UIAlertAction(title: "Done", style: .default, handler: { action in
self.nextMessage(index: index + 1)
}))

self.present(alert, animated: true)
}

Calling presentMessage from the first item in the array will start it:

self.presentMessage(index: 0, title: blocks[0].stringInput1, message: blocks[0].stringInput2)

This is the quick and dirty Block class I created, but posting any
related code like this in the future would be helpful for those
answering questions.

class Block {
var stringInput1: String
var stringInput2: String

init(one: String, two: String) {
stringInput1 = one
stringInput2 = two
}
}

Blocking a Loop Using UIAlertController

Use Recursive Function rather than loop

Example

var name:[String] = ["abcd","efgh","ijkl","mnop","qrst","uvwx"]
self.friendReuqest(name, index: 0)

func friendReuqest(name:[String],index:Int)
{
if index < name.count
{
let alertController = UIAlertController(title: name[index], message: "Would you like to accept this friend request?", preferredStyle: UIAlertControllerStyle.Alert)

let cancelAction: UIAlertAction = UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Cancel){ (action: UIAlertAction!) -> Void in
// do stuff here when press cancel
}

let confirmAction: UIAlertAction = UIAlertAction(title: "Add", style: UIAlertActionStyle.Default) { (action: UIAlertAction!) -> Void in
self.friendReuqest(name, index: (index + 1))
}

alertController.addAction(cancelAction)
alertController.addAction(confirmAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
}

Pause for loop until getting the response of an UIAlertController in Swift

You need to use a completion handler, since the user input happens asynchronously, so you cannot return it using a synchronous return.

Unrelated to your issue, but you should be using String interpolation rather than + to concanate Strings.

func showPopUp(name:String, genderCompletion: @escaping (String)->()) {
let alert = UIAlertController(title: "What are you \(name)?", message: nil, preferredStyle: .alert)

alert.addAction(UIAlertAction(title: "Boy", style: .default, handler: { action in
genderCompletion("Boy")
}))

alert.addAction(UIAlertAction(title: "Girl", style: .default, handler: { action in
genderCompletion("Girl")
}))

self.present(alert, animated: true)
}

override func viewDidLoad() {
super.viewDidLoad()
let characters: [String] = ["John", "Tom", "Martha"]
for ch in characters{
showPopUp(name: ch, genderCompletion: { gender in
print("\(ch) is a \(gender)")
})
}
}

Multiple alerts on top of each other in swift

To show alerts after one another, i would suggest to add this recursive code,

Add a class variable for messages and fill the array for all the true conditions. After that you have to just call the showAlert method that will handle showing all the messages one by one.

class YourClass {

var messages: [String] = []

func yourMethod() {
for p in json! {
if self.checkDaysPassed(postDate: p["uploadedTime"] as! String) > 1 {
messages.append("This item has been unused for a day")
}
}
self.showAlert()
}

private func showAlert() {
guard self.messages.count > 0 else { return }

let message = self.messages.first

func removeAndShowNextMessage() {
self.messages.removeFirst()
self.showAlert()
}

let alert = UIAlertController(title: "Sell this item", message: message, preferredStyle: .alert)
alert.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
alert.addAction(UIAlertAction(title: "Yes", style: .default){ (action) in
print("pressed yes")
removeAndShowNextMessage()

})
alert.addAction(UIAlertAction(title: "No", style: .cancel){ (action) in
print("pressed no")
removeAndShowNextMessage()
})

UIApplication.shared.delegate?.window??.rootViewController?.present(alert, animated: true)
}
}


Related Topics



Leave a reply



Submit