Reputation: 654
in my iOS actionsheet, I am showing Names from the JSON dictionary:
[
{ "Name": "Doctor for Disease AAA",
"Doctor_id": "21"
},
{ "Name": "Doctor for Disease BBB",
"Doctor_id": "22"
},
{ "Name": "Doctor for Disease AAA",
"Doctor_id": "25"
}
]
So, on button click delegate, I can get the button index and can fetch the corresponding 'Name' and 'Doctor_id'. This is working fine.
But now it seems like 'UIActionSheet' is deprecated, and I have to use 'UIAlertController'. As I have a large data, I am iterating through my array values and calling the alertcontroller handler (so a single function for all button click). But how can I get the button index from UIAlertController, so that I can fetch the 'Name' and 'Doctor_id' simultaneously.
Please help me.
Upvotes: 10
Views: 7914
Reputation: 1371
Instead of using indices I find it easier and more expressive to use a custom UIAlertAction class
class MyAlertAction:UIAlertAction {
var key:String = ""
convenience init(key:String, title:String, handler:((UIAlertAction) -> ())?) {
self.init(title: title, style: .default, handler: handler)
self.key = key
}
}
in your action handler you get the key like this:
let actionHandler: (UIAlertAction) -> () = { (action) in
if let key = (action as? MyAlertAction)?.key {
//use the key
}
}
finally use your new class to add an action
alert.addAction(MyAlertAction(key:"doctor32", title: "Title", handler: actionHandler))
Upvotes: 1
Reputation: 605
You may call indexOf(element: Self.Generator.Element)
on your collection of actions from your controller.
Something like:
let actionSheetController = UIAlertController(title: "Title", message: "Select Title",
preferredStyle: UIAlertControllerStyle.ActionSheet);
for entry in data {
let myAction = UIAlertAction(title: "title: \(entry)", style: UIAlertActionStyle.Default) { (action) -> Void in
if let alertIndex = actionSheetController.actions.indexOf(action) {
print("actionIndex: \(alertIndex)")
}
}
actionSheetController.addAction(myAction)
}
Upvotes: 4
Reputation: 201
You can solve the problem as following:
let arraySelect = ["NewYork", "Washington", "Seoul", "Tokyo", "Peking", "Sidney", ... ]
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .Alert)
let closure = { (action: UIAlertAction!) -> Void in
let index = alert.actions.indexOf(action)
if index != nil {
NSLog("Index: \(index!)")
}
}
for var i = 0; i < arrayBibleVersions.count; i++ { alert.addAction(UIAlertAction(title: arrayBibleVersions[i][1], style: .Default, handler: closure)) }
alert.addAction(UIAlertAction(title: "cancel", style: .Cancel, handler: {(_) in }))
self.presentViewController(alert, animated: false, completion: nil)
Upvotes: 7
Reputation: 32681
You have multiple possibilities here.
find
to get the UIAlertAction
indexfind
let you find the index of an object in an array. You can use it to find
the index of the action
(that is passed as the parameter of the UIAlertAction
's handler, which is the UIAlertAction
itself) in the alert.actions
array of all actions.
let alert = UIAlertController(title: "Doctors", message: "Choose a doctor", preferredStyle: .ActionSheet)
let closure = { (action: UIAlertAction!) -> Void in
let index = find(alert.actions as! [UIAlertAction], action)
println("Index: \(index)")
}
alert.addAction(UIAlertAction(title: "Doc1", style: .Default, handler: closure))
alert.addAction(UIAlertAction(title: "Doc2", style: .Default, handler: closure))
alert.addAction(UIAlertAction(title: "Doc3", style: .Default, handler: closure))
alert.addAction(UIAlertAction(title: "Doc4", style: .Default, handler: closure))
alert.addAction(UIAlertAction(title: "Cancel", style: .Cancel) { _ in
println("User cancelled.")
})
self.presentViewController(alert, animated: true) {}
Create a closure that takes a parameter of your choice (here an Int
) and return a closure that captures that parameter so you can use it
let alert = UIAlertController(title: "Doctors", message: "Choose a doctor", preferredStyle: .ActionSheet)
let closure = { (index: Int) in
{ (action: UIAlertAction!) -> Void in
println("Index: \(index)")
}
}
alert.addAction(UIAlertAction(title: "Doc1", style: .Default, handler: closure(0)))
alert.addAction(UIAlertAction(title: "Doc2", style: .Default, handler: closure(1)))
alert.addAction(UIAlertAction(title: "Doc3", style: .Default, handler: closure(2)))
alert.addAction(UIAlertAction(title: "Doc4", style: .Default, handler: closure(3)))
alert.addAction(UIAlertAction(title: "Cancel", style: .Cancel) { _ in
println("User cancelled.")
})
self.presentViewController(alert, animated: true) {}
This way you have a function (closure) that generate closures for your UIAlertAction
handlers, all with the same body except that they capture a different object (a different Int
here).
What is really great with this solution is that you can capture anything. You can even capture an hypothetic Doctor
object that represent your doctor, or directly the doctor ID, etc.!
But generally you will add your actions using a for
loop, so why not take advantage of that, plus take advantage of closure and the fact that they capture variables, to make a nice function that will directly tell your the selected doctor's ID?
func testMyAlert() {
let doctors = [
["Name": "Doctor for Disease AAA", "Doctor_id": "21"],
["Name": "Doctor for Disease BBB", "Doctor_id": "22"],
["Name": "Doctor for Disease AAA", "Doctor_id": "25"]
]
chooseDoctor(doctors) { selectedDocID in
if let docID = selectedDocID {
println("User selected doctor with ID \(docID)")
} else {
println("User cancelled, no doctor selected")
}
}
}
func chooseDoctor(doctors: Array<[String:String]>, completion: Int?->Void) {
let alert = UIAlertController(title: "Doctors", message: "Choose a doctor", preferredStyle: .ActionSheet)
for doc in doctors {
let action = UIAlertAction(title: doc["Name"]!, style: UIAlertActionStyle.Default) { _ in
// On selecting this action, get the doctor's ID, convert it to an Int, and return that.
completion(doc["Doctor_id"]?.toInt())
}
alert.addAction(action)
}
alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel) { _ in completion(nil) } )
self.presentViewController(alert, animated: true) {}
}
Upvotes: 17
Reputation: 131471
You add buttons to a UIAlertController by creating one or more UIAlertAction objects and attaching it to the alert controller. Each alert action has a title (used to create it's button) and a closure that gets called when the user taps that button.
Are you saying you want to use the same closure for all of your buttons? I see a number of options
You could write unique closures for each alert action where the closure simply invokes a shared function and passes in a button number.
Alternatively, a UIAlertAction's closure gets passed in the title of the button that was tapped. You could use that to decide which button was tapped. (This is fragile because the button titles might be localized, and are subject to change for UI reasons. It's bad when UI changes break code.)
As a final option you could use associative storage to attach an object to each UIAlertAction and use that to figure out which button was tapped.
My suggestion is option 1. It's simple and clean.
Upvotes: 0
Reputation: 1585
I suggest you pass the closure to call when you need it. Its on demand. Maybe something like this would help.
var alert = UIAlertController(title: "Title", message: "do this", preferredStyle: UIAlertControllerStyle.ActionSheet)
var action = UIAlertAction(title: "Doc1", style: UIAlertActionStyle.Default) { (action) -> Void in
//do some work here
}
But again, i guess you are not showing more than 4 items on the action sheet because it bad for design. closure might be a good idea. Suggest me if i get it wrong, i'd love to help you out. Cheers.
Upvotes: 0