Couldn't find a clear and informative explanation for this.
After searching a while on a subject I didn't
find a clear explanation , even in it's class reference
UIAlertController Reference
It is ok, but not clear enough for me.
So after collecting some peaces I decided to make my own explanation
(Hope it helps)
So here it goes:
UIAlertView is deprecated as pointed out :
UIAlertView in Swift
UIAlertController should be used in iOS8+
so to create one first we need to instantiate it,
the Constructor(init) gets 3 parameters:
2.1 title:String -> big-bold text to display on the top of alert's dialog box
2.2 message:String -> smaller text (pretty much explains it's self)
2.3 prefferedStyle:UIAlertControllerStyle -> define the dialog box style, in most cases: UIAlertControllerStyle.Alert
Now to actually show it to the user, we can use showViewController or presentViewController and pass our alert as parameter
To add some interaction with a user we can use:
4.1
UIAlertController.addAction to create buttons
4.2
UIAlertController.addTextField to create text fields
Edit note: code examples below, updated for swift 3 syntax
Example 1: Simple Dialog
#IBAction func alert1(sender: UIButton) {
//simple alert dialog
let alert=UIAlertController(title: "Alert 1", message: "One has won", preferredStyle: UIAlertControllerStyle.alert);
//show it
show(alert, sender: self);
}
Example 2: Dialog with one input textField & two buttons
#IBAction func alert2(sender: UIButton) {
//Dialog with one input textField & two buttons
let alert=UIAlertController(title: "Alert 2", message: "Two will win too", preferredStyle: UIAlertControllerStyle.alert);
//default input textField (no configuration...)
alert.addTextField(configurationHandler: nil);
//no event handler (just close dialog box)
alert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.cancel, handler: nil));
//event handler with closure
alert.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.default, handler: {(action:UIAlertAction) in
let fields = alert.textFields!;
print("Yes we can: "+fields[0].text!);
}));
present(alert, animated: true, completion: nil);
}
Example 3: One customized input textField & one button
#IBAction func alert3(sender: UIButton) {
// one input & one button
let alert=UIAlertController(title: "Alert 3", message: "Three will set me free", preferredStyle: UIAlertControllerStyle.alert);
//configured input textField
var field:UITextField?;// operator ? because it's been initialized later
alert.addTextField(configurationHandler:{(input:UITextField)in
input.placeholder="I am displayed, when there is no value ;-)";
input.clearButtonMode=UITextFieldViewMode.whileEditing;
field=input;//assign to outside variable(for later reference)
});
//alert3 yesHandler -> defined in the same scope with alert, and passed as event handler later
func yesHandler(actionTarget: UIAlertAction){
print("YES -> !!");
//print text from 'field' which refer to relevant input now
print(field!.text!);//operator ! because it's Optional here
}
//event handler with predefined function
alert.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.default, handler: yesHandler));
present(alert, animated: true, completion: nil);
}
Hope It helps, and good luck ;-)
An instance of the UIAlertController can be presented modally on screen just as any other UIViewController using the presentViewController:animated:completion: method. What makes the UIAlertController instance differentiate between working as an ActionSheet or as an AlertView is the style parameter you pass when creating it.
No more delegation
If you have used a UIActionSheet or UIAlertView, you know that the way to get a callback from it is for a class (in most cases the ViewController) to implement the UIActionSheetDelegate or UIAlertViewDelegate protocol. There are some open source projects that replaced this delegation pattern with block based callbacks, but the official APIs were never updated. UIAlertController does not use delegation. Instead, it has a collection of UIAlertAction items, that use closures (or blocks if you are using Objective-C) to handle user input.
For Action Sheet
#IBAction func showActionSheet(sender: AnyObject) {
//Create the AlertController
let actionSheetController: UIAlertController = UIAlertController(title: "Action Sheet", message: "Swiftly Now! Choose an option!", preferredStyle: .ActionSheet)
//Create and add the Cancel action
let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .Cancel) { action -> Void in
//Just dismiss the action sheet
}
actionSheetController.addAction(cancelAction)
//Create and add first option action
let takePictureAction: UIAlertAction = UIAlertAction(title: "Take Picture", style: .Default) { action -> Void in
//Code for launching the camera goes here
}
actionSheetController.addAction(takePictureAction)
//Create and add a second option action
let choosePictureAction: UIAlertAction = UIAlertAction(title: "Choose From Camera Roll", style: .Default) { action -> Void in
//Code for picking from camera roll goes here
}
actionSheetController.addAction(choosePictureAction)
//Present the AlertController
self.presentViewController(actionSheetController, animated: true, completion: nil)
}
For AlertView with Text Field
#IBAction func showAlert(sender: AnyObject) {
//Create the AlertController
let actionSheetController: UIAlertController = UIAlertController(title: "Alert", message: "Swiftly Now! Choose an option!", preferredStyle: .Alert)
//Create and add the Cancel action
let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .Cancel) { action -> Void in
//Do some stuff
}
actionSheetController.addAction(cancelAction)
//Create and an option action
let nextAction: UIAlertAction = UIAlertAction(title: "Next", style: .Default) { action -> Void in
//Do some other stuff
}
actionSheetController.addAction(nextAction)
//Add a text field
actionSheetController.addTextFieldWithConfigurationHandler { textField -> Void in
//TextField configuration
textField.textColor = UIColor.blueColor()
}
//Present the AlertController
self.presentViewController(actionSheetController, animated: true, completion: nil)
}
Some of the syntax has changed since the original responses. Here is some sample code that alerts the user if they are not signed in to iCloud.
CKContainer.default().accountStatus { (accountStatus, error) in
switch accountStatus {
case .available:
print("iCloud Available")
case .noAccount:
print("No iCloud account")
//simple alert dialog
let alert=UIAlertController(title: "Sign in to iCloud", message: "This application requires iClound. Sign in to your iCloud account to write records. On the Home screen, launch Settings, tap iCloud, and enter your Apple ID. Turn iCloud Drive on. If you don't have an iCloud account, tap Create a new Apple ID", preferredStyle: UIAlertControllerStyle.alert);
//no event handler (just close dialog box)
alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil));
//show it
self.present(alert, animated: true, completion: nil)
case .restricted:
print("iCloud restricted")
case .couldNotDetermine:
print("Unable to determine iCloud status")
}
}
Related
I have written code for an alert to appear when the input in one of my UITextFields is less than 1050. It successfully appears when the inputs satisfies that, but after I press "OK" it instantly re-appears.
Below is the code in the viewDidLoad function:
override func viewDidLoad(){
super.viewDidLoad()
alert = UIAlertController(title: "Error", message: "Please enter an exit width value greater than 1050", preferredStyle: UIAlertControllerStyle.Alert)
let okay = UIAlertAction(title: "OK", style: UIAlertActionStyle.Destructive, handler: valueCalc)
alert.addAction(okay)
}
Then I have in my valueCalc function (which is called when a button is tapped):
#IBAction func valueCalc(sender: AnyObject){
if(Int(mmText.text!)! < 1050){ //mmText is an UITextField
self.presentViewController(alert, animated: true, completion: nil)
}
}
According to your line of code
let okay = UIAlertAction(title: "OK", style: UIAlertActionStyle.Destructive, handler: valueCalc)
Your handler name valueCalc is called when you press OK.
Again the value is calculated which when come out be less then the specified characters shows back you the alert.
Instead of that, replace this line in your code -
let okay = UIAlertAction(title: "OK", style: UIAlertActionStyle.Destructive, handler: handlerMethod)
and add this method to your code
func handlerMethod() {
//handle your action here after ok is pressed for e.g if you wanna just dismiss the alert then write
dismissViewControllerAnimated(true, completion: nil)
}
You have the handler argument for your UIAlertAction set to valueCalc. Therefore, whenever the user taps "OK", the method valueCalc gets run again, and since the value is (presumably) still the same, the alert is presented right back again.
Try this
override func viewDidLoad(){
super.viewDidLoad()
alert = UIAlertController(title: "Error", message: "Please enter an exit width value greater than 1050", preferredStyle: UIAlertControllerStyle.Alert)
let okay = UIAlertAction(
title: "OK",
style: UIAlertActionStyle.Destructive) { (action) in }
}
#IBAction func valueCalc(sender: AnyObject){
if(Int(mmText.text!)! < 1050){ //mmText is an UITextField
self.presentViewController(alert, animated: true, completion: nil)
}
Edit: Thank you for the replies, yes I needed an alert and not an action sheet! I have implemented this new code and it works, but is there a reason why it segues to the next view before the user can enter a ride title?
Also it throws this message in the debug console, should I be concerned?
2016-02-16 12:30:21.675 CartoBike[687:128666] Presenting view controllers on detached view controllers is discouraged .
#IBAction func stopAction(sender: UIButton) {
let alert = UIAlertController(title: "Ride Stopped", message: "Give a title to your ride", preferredStyle: .Alert)
let saveAction = UIAlertAction(title: "Save", style: .Default,
handler: { (action:UIAlertAction) -> Void in
// Allow for text to be added and appended into the RideTableViewController
let textField = alert.textFields!.first
rideContent.append(textField!.text!)
})
let cancelAction = UIAlertAction(title: "Cancel",
style: .Default) { (action: UIAlertAction) -> Void in
}
alert.addTextFieldWithConfigurationHandler {
(textField: UITextField) -> Void in
}
alert.addAction(saveAction)
alert.addAction(cancelAction)
// Save the ride
saveRide()
// Automatically segue to the Ride details page
self.performSegueWithIdentifier("ShowRideDetail", sender: nil)
presentViewController(alert, animated: true, completion: nil)
timer.invalidate()
self.stopLocation()
}
Overview:
I am working my way through my first real app. The basic logic is a home screen to start a new ride or view previous rides. Starting a ride will open a new view with a map to record a bicycle ride based on the users location, this can be stopped and save the ride and immediately switch to a new view to see a map of the ride. Alternatively from the home screen the user can select previous rides and view a list of their old rides in a table view and select one and transition to a detailed view with a map of their ride.
Problem: When adding a UIAlertAction I would like there to be a save and cancel feature. In addition, I would like the user to be able to add a custom title by typing it in via a text field. The input from the text field will be appended to a global variable called rideContent that is tied to the creation of new cells in the table view to store multiple bike rides by unique title.
Research:
I have reviewed the questions titled "How to add a TextField to UIAlertView in Swift" & "Writing handler for UIAlertAction" and still can't seem to discern what I have done wrong. Ideally the alert within this screenshot of an app from the raywenderlich site is what I would like it to look like. I am not sure if what I am trying to do is even possible since there a so many view controllers involved. Granted I am new to swift and I'm sure I am missing something obvious!
Currently getting an error of
"Type of expression is ambiguous without more context", see screenshot: here
Here is the UIAlertController code:
// The timer pauses & the location stops updating when the the stop button is pressed
#IBAction func stopAction(sender: UIButton) {
var inputTextField: UITextField?
let actionSheetController = UIAlertController (title: "Ride Stopped", message: "Add a title to your ride", preferredStyle: UIAlertControllerStyle.ActionSheet)
// Add a cancel action
actionSheetController.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil))
// Add a save action
actionSheetController.addAction(UIAlertAction(title: "Save", style: UIAlertActionStyle.Default, handler: {
(actionSheetController) -> Void in
//Add a text field --- Getting an error on this line
actionSheetController.addTextFieldWithConfigurationHandler { textField -> Void in
// you can use this text field
inputTextField = textField
// Append the ride title to the table view
rideContent.append(textField!.text!)
// Update when user saves a new ride to store the ride in the table view for permanent storage
NSUserDefaults.standardUserDefaults().setObject(rideContent, forKey: "rideContent")
// Save the ride
self.saveRide()
// Automatically segue to the Ride details page
self.performSegueWithIdentifier("ShowRideDetail", sender: nil)
}}))
//present actionSheetController
presentViewController(actionSheetController, animated: true, completion: nil)
timer.invalidate()
self.stopLocation()
}
Thank you stack overflow for any help you may offer!
You're using an .ActionSheet while the tutorial you showed is using an .Alert
Action Sheets can have buttons but not text fields.
"Alerts can have both buttons and text fields, while action sheets only support buttons."
NSHipster
Use your tableview Array when Add Button is pressed and "Add alertview textfirld to tableview array and then reload tableview".
#IBAction func btnAdd(_ sender: Any) {
let alertController = UIAlertController(title: "Add Category", message: "", preferredStyle: .alert)
let saveAction = UIAlertAction(title: "Add", style: .default, handler: { alert -> Void in
let firstTextField = alertController.textFields![0] as UITextField
self.categories.add(firstTextField.text!)
self.tblCatetgory.reloadData()
})
let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: {
(action : UIAlertAction!) -> Void in })
alertController.addTextField { (textField : UITextField!) -> Void in
textField.placeholder = "Enter Category!!!"
}
alertController.addAction(saveAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
}
I have created a UIAlertView. The main content is loaded with JSON and I want that when I click "Completo" button, the content change to another variable value completo. When I hit Mapa I need to open a map. And when I click Atrás it should close the alert (right now all the buttons close it).
var tabla: String = "" // JSON Data
...
#IBAction func Itinerario(sender: AnyObject) {
let alert = UIAlertView(title: "Itinerario de Procesión", message: (tabla), delegate: nil, cancelButtonTitle: nil, otherButtonTitles: "Completo", "Mapa", "Atrás")
alert.show()
}
Also I wanted to change buttons color.
I am getting this advice: /Users/Javi/Desktop/ElPenitente/El Penitente/Domingo R.swift:87:21: 'UIAlertView' was deprecated in iOS 9.0: UIAlertView is deprecated. Use UIAlertController with a preferredStyle of UIAlertControllerStyleAlert instead
So what the error means is that you should use a UIAlertController instead.
Usage is quite straightforward:
let alert = UIAlertController(title: "Title", message: "Message", preferredStyle: UIAlertControllerStyle.Alert)
let action1 = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil)
let action2 = UIAlertAction(title: "Action", style: UIAlertActionStyle.Default) { (action) in
// Do something here when action was pressed
}
alert.addAction(action1)
alert.addAction(action2)
self.presentViewController(alert, animated: true, completion: nil)
Where action1 has a system style .Cancel and no handler, and action2 has a default style and a handler.
Changing the color of the actions can be done using the tintColor attribute of the views, see this answer.
I cannot find the name of the class for this 'popover'. Apple uses it a lot in their applications.
I've searched for popover, NSAlert, custom hidden/visible views, and many more.
What is it called?
This is UIAlertController. Before ios7 it is know as UIActionSheet
A UIAlertController object displays an alert message to the user. This class replaces the UIActionSheet and UIAlertView classes for displaying alerts.
#IBAction func showActionSheetTapped(sender: AnyObject) {
//Create the AlertController
let actionSheetController: UIAlertController = UIAlertController(title: "Action Sheet", message: "Swiftly Now! Choose an option!", preferredStyle: .ActionSheet)
//Create and add the Cancel action
let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .Cancel) { action -> Void in
//Just dismiss the action sheet
}
actionSheetController.addAction(cancelAction)
//Create and add first option action
let takePictureAction: UIAlertAction = UIAlertAction(title: "Take Picture", style: .Default) { action -> Void in
//Code for launching the camera goes here
}
actionSheetController.addAction(takePictureAction)
//Create and add a second option action
let choosePictureAction: UIAlertAction = UIAlertAction(title: "Choose From Camera Roll", style: .Default) { action -> Void in
//Code for picking from camera roll goes here
}
actionSheetController.addAction(choosePictureAction)
//Present the AlertController
self.presentViewController(actionSheetController, animated: true, completion: nil)
}
Output:
May be it will help you.
Apple's iOS UX guidelines show the following example:
...where:
When the most likely button performs a nondestructive action, it
should be on the right in a two-button alert. The button that cancels
this action should be on the left.
When the most likely button performs a destructive action, it should
be on the left in a two-button alert. The button that cancels this
action should be on the right.
Source: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/Alerts.html
However, it does not seem possible to implement this guidance with the current iOS APIs (testing on iOS 9 with Swift). Here's one attempt. But I've tried varying the order of adding the alert actions, and changing which action is .Default vs. .Cancel style. No combination appears to work.
#IBAction func showAlertAction(sender: AnyObject)
{
let title = "An Alert that Offers Two Alternatives Is Easy for People to Use"
let alertViewController = UIAlertController(title: title, message: nil, preferredStyle: UIAlertControllerStyle.Alert)
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: { (alert :UIAlertAction!) -> Void in
print("'Cancel' tapped")
})
alertViewController.addAction(cancelAction)
let safeChoiceAction = UIAlertAction(title: "Safe Choice", style: UIAlertActionStyle.Default, handler: { (alert :UIAlertAction!) -> Void in
print("'Safe Choice' tapped")
})
alertViewController.addAction(safeChoiceAction)
self.presentViewController(alertViewController, animated: true, completion: nil)
}
Result: