UIAlertController with two buttons with styles set:
UIAlertActionStyle.Cancel
UIAlertActionStyle.Default
in iOS 8.2, the Cancel button is non-bold and Default is bold.
In iOS 8.3 they have switched round
You can see it Apple's own apps e.g., Settings > Mail > Add Account > iCloud > enter invalid data, then it shows like this on 8.3:
Unsupported Apple ID
Learn More (bold)
OK (non-bold)
whereas it was the other way round for 8.2.
Any workaround to make it like 8.2 again. Why has it changed?
From iOS 9 you can set the preferredAction value to the action which you want the button title to be bold.
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
let OKAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
alert.addAction(cancelAction)
alert.addAction(OKAction)
alert.preferredAction = OKAction
presentViewController(alert, animated: true) {}
The OK button which is on the right will be in bold font.
This is an intentional change to the SDK. I have just had a response from Apple to this radar on the issue, stating that:
This is an intentional change - the cancel button is to be bolded in alerts.
I can't find anything in the various change logs mentioning this, unfortunately.
So, we'll need to make changes to our apps in places to make some things make sense.
Since iOS 9, UIAlertController has a property called preferredAction. preferredAction has the following declaration:
var preferredAction: UIAlertAction? { get set }
The preferred action for the user to take from an alert. [...] The preferred action is relevant for the UIAlertController.Style.alert style only; it is not used by action sheets. When you specify a preferred action, the alert controller highlights the text of that action to give it emphasis. (If the alert also contains a cancel button, the preferred action receives the highlighting instead of the cancel button.) [...] The default value of this property is nil.
The Swift 5 / iOS 12 sample code below shows how to display a UIAlertController that will highlight the text of a specified UIAlertAction using preferredAction:
let alertController = UIAlertController(title: "Title", message: "Message", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
let okAction = UIAlertAction(title: "OK", style: .default, handler: { action in
print("Hello")
})
alertController.addAction(cancelAction)
alertController.addAction(okAction)
alertController.preferredAction = okAction
present(alertController, animated: true, completion: nil)
I just checked in iOS 8.2: a first added button is non-bold and a second added button is bold. With this code a cancel button will be bold:
[alertController addAction:[UIAlertAction actionWithTitle:#"Ok"
style:UIAlertActionStyleDefault
handler:nil]];
[alertController addAction:[UIAlertAction actionWithTitle:#"Cancel"
style:UIAlertActionStyleCancel
handler:nil]];
And with this code a default button will be bold:
[alertController addAction:[UIAlertAction actionWithTitle:#"Cancel"
style:UIAlertActionStyleCancel
handler:nil]];
[alertController addAction:[UIAlertAction actionWithTitle:#"Ok"
style:UIAlertActionStyleDefault
handler:nil]];
I can't check in iOS 8.3 now but this behavior can be a reason.
Some words about objective-c and preferredAction for alertActions. If you use preferredAction you BOUTH alertAction must set as style:UIAlertActionStyleDefault. If some one will be set as style:UIAlertActionStyleCancel, preferredAction will be ignored
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:#"All you base"
message:#"Are belong to us!" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* alertActionShowYes = [UIAlertAction actionWithTitle:#"YES!" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
NSLog(#"I serve for my emperor!");
}];
UIAlertAction* alertActionNo = [UIAlertAction actionWithTitle:#"NO!" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
NSLog(#"NOOOO it's not true!!");
}];
[alertController addAction:alertActionShowYes];
[alertController addAction:alertActionNo];
alertController.preferredAction = alertActionShowYes;
[alertController setPreferredAction:alertActionShowYes];
[self presentViewController:alertController animated:YES completion:nil];
Related
I have a strange visual bug that only affects iOS 9 devices:
My app’s login UIViewController runs off and gets an OAuth token when you hit the button, much like you’d expect. If the response from my API returns a specific status code, I pop up a UIAlertView saying they need to reset their password (this is if they’ve been flagged as such on the server end). The email and password fields for login resignFirstResponder once you hit the button, standard stuff.
On iOS 9 only, if you hit the reset path, the second you tap OK on that alert view, the keyboard pops back up, for maybe 800ms, then dismisses again. It’s almost as if something was queued to present it, but the presence of the alert blocked it until you hit OK – it’s absolutely instantaneous after hitting okay on the alert.
It seems a really tricky one to debug. I’ve added symbolic breakpoints to becomeFirstResponder and it isn’t called anywhere near this process occurring.
Any other ideas for how I can look at debugging / fixing this? It doesn’t affect iOS 7 and iOS 8, only iOS 9.
I faced this problem about 30 minutes ago.
The UIAlertView has been deprecated since iOS9 was released.
We solved this issue by using the UIAlertController, like this:
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:#"Alert Title!" message:#"This is an alert message." preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* ok = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:nil];
[alertController addAction:ok];
[self presentViewController:alertController animated:NO completion:nil];
This should fix your problem.
If animated = YES, you may get the same issue as before. This is a bug with iOS9.
Let me know how it goes, and if this fixes your problem.
Here is an extension to handle this in swift 3
extension UIViewController {
func presentOk(with title: String, and message: String, handler: ((UIAlertAction) -> Void)?) {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: handler))
OperationQueue.main.addOperation {
self.view.endEditing(true)
self.present(alert, animated: true, completion: nil)
}
}
}
The key is to hide the keyboard and present the controller in the main queue.
Usage
presentOk(with: "My app title", and: "this is the alert message", handler: nil)
I have something like the following code. The Action sheet runs doSomething OK when it appears for the first time (in a button IBAction), but when it appears the second time, nothing happens the Action sheet just disappear without calling do something. Any idea?
#implementation ...
- (void) setActions {
UIAlertAction *opt1 = [UIAlertAction actionWithTitle:#"Option 1" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self doSomething:#"opt1"];}];
UIAlertAction *opt2 = [UIAlertAction actionWithTitle:#"Option 2" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self doSomething:#"opt2"];}];
UIAlertAction *opt3 = ...
self.opt1 = opt1;
self.opt2 = opt2;
self.opt3 = opt3;
- (void) showActionSheet {
...
UIAlertController *selectAS = [UIAlertController alertControllerWithTitle:#"Select Options"
message:#"msg" preferredStyle:UIAlertControllerStyleActionSheet];
if (xyz) {
[selectAS addAction:self.opt1];
[selectAS addAction:self.opt2];
}
else{
[selectAS addAction:self.opt1];
[selectAS addAction:self.opt3];
}
[self presentViewController:selectqAS
animated:YES completion:nil];
}
- (void) doSomething: (NSString *) opt{
....
}
Glad we got you up and running. My guess is your methods are getting lost in translation. You have methods intertwining each other which can be causing the confusion, specifically with self.opt1. per my comment, now that iOS8 has introduced UIAlertController, it comes with completion handlers, you should plan accordingly to that: something like the following :
-(IBAction)showActionSheet {
UIAlertController *selectAS = [UIAlertController alertControllerWithTitle:#"Select Options" message:#"msg" preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *opt1 = [UIAlertAction actionWithTitle:#"Option 1" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
//Don't have to call another method, just put your action 1 code here. This is the power of completion handlers creating a more structured outline
}];
UIAlertAction *opt2 = [UIAlertAction actionWithTitle:#"Option 2" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
//Don't have to call another method, just put your action 2 code here. This is the power of completion handlers creating a more structured outline
}];
UIAlertAction *opt3 = ...
if (xyz) {
[selectAs addAction:opt1];
[selectAs addAction:opt2];
} else {
[selectAs addAction:opt1];
[selectAs addAction:opt3];
}
[self presentViewController:selectAs animated:YES completion:nil];
}
Much more cleaner and actually uses the UIAlertController for it's intended purposes, no other method calls needed.
Versions of iOS prior to 8 allowed me to create a UIActionsheet which would show a group of buttons, some space, and then a cancel button. Something like this:
However in iOS 8 when I try and create the same look I end up with something that looks like this:
The code, in iOS 8 looks like this:
UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[alertVC.view setTintColor:[UIColor copperColor]];
UIAlertAction* notifyViaPush = [UIAlertAction
actionWithTitle:#"Send Alert to my phone"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
[alertVC dismissViewControllerAnimated:YES completion:nil];
}];
UIAlertAction* notifyViaEmail = [UIAlertAction
actionWithTitle:#"Notify me by email"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
[alertVC dismissViewControllerAnimated:YES completion:nil];
}];
UIAlertAction* cancel = [UIAlertAction
actionWithTitle:#"Cancel"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action)
{
[alertVC dismissViewControllerAnimated:YES completion:nil];
}];
[alertVC addAction:notifyViaPush];
[alertVC addAction:notifyViaEmail];
[alertVC addAction:cancel];
[self presentViewController:alertVC animated:YES completion:nil];
How can I group my buttons and have some space between the actions and cancel button using UIAlertController?
The line alertVC.view.tintColor = [UIColor copperColor]; is causing problem, it makes the whole view of alert controller the same color, in your first picture the cancel button has white background. To fix this, move this line to the end of the function, that is, after you have added all actions.
For me it worked when I set the style to UIAlertActionStyleCancel on the UIAlertAction.
In the code below, action is a UIAlertAction object and controller is a UIAlertController with style Action sheet.
UIAlertAction *action2 = [UIAlertAction actionWithTitle:#"No" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
self.tabBarView.selectedIndex = 1;
[self.tabBarView setSelectedIndex:self.tabBarView.selectedIndex];
}];
[controller addAction:action];
[controller addAction:action2];
Swift Answer
Set the style: parameter of the action button that you want to separate to .cancel instead of .default.
like this style: .cancel
or more specifically this
// **style: on this is set to .cancel**
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) in }
It's usually the button that you named "Cancel"
I'm not sure about how setting the button text colors the way the op said he did it in his answer affects the way the actions get separated. I use the actions "titleTextColor" to change the text color and it has no effect on the spacing the op is asking about.
cancelAction.setValue(UIColor.green, forKey: "titleTextColor")
Here's the code in 4 steps
func presentActionSheet() {
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
// 1. style: is set to .default on both of these which will keep them grouped
let blockAction = UIAlertAction(title: "Block", style: .default) { (action) in }
let reportAction = UIAlertAction(title: "Report", style: .default) { (action) in }
// 2. style: is set to .cancel which will separate it from the other two actions
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) in }
// 3. set the colors for the action text
blockAction.setValue(UIColor.brown, forKey: "titleTextColor")
reportAction.setValue(UIColor.purple, forKey: "titleTextColor")
cancelAction.setValue(UIColor.green, forKey: "titleTextColor")
// 4. add the buttons to the action sheet and make sure the cancel button is last
actionSheet.addAction(blockAction)
actionSheet.addAction(reportAction)
actionSheet.addAction(cancelAction)
present(actionSheet, animated: true, completion: nil)
}
Because of a glitch with iOS 8 where the keyboard does not popup automatically with UIAlertView's, my only alternative is to upgrade to UIAlertController. However, the didDismissWithButtonIndex was flexible enough to do what I had to do whereas their proposed new solution is not (as seen in the example below). Without didDismissWithButtonIndex, I cannot upgrade to UIAlertController. Is there a similar solution other than:
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancelButtonTitle style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
}];
UIAlertAction *otherAction = [UIAlertAction actionWithTitle:otherButtonTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
}];
[alertController addAction:cancelAction];
[alertController addAction:otherAction];
No, the way you did it is the only way you can. I recommend extracting common behavior between the two actions to a separate method and calling that method from the blocks.
After a calculation, I want to display a pop up or alert box conveying a message to the user. Does anyone know where I can find more information about this?
Yup, a UIAlertView is probably what you're looking for. Here's an example:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"No network connection"
message:#"You must be connected to the internet to use this app."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
If you want to do something more fancy, say display a custom UI in your UIAlertView, you can subclass UIAlertView and put in custom UI components in the init method. If you want to respond to a button press after a UIAlertView appears, you can set the delegate above and implement the - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex method.
You might also want to look at the UIActionSheet.
Different people who come to this question mean different things by a popup box. I highly recommend reading the Temporary Views documentation. My answer is largely a summary of this and other related documentation.
Alert (show me an example)
Alerts display a title and an optional message. The user must acknowledge it (a one-button alert) or make a simple choice (a two-button alert) before going on. You create an alert with a UIAlertController.
It is worth quoting the documentation's warning and advice about creating unnecessary alerts.
Notes:
See also Alert Views, but starting in iOS 8 UIAlertView was deprecated. You should use UIAlertController to create alerts now.
iOS Fundamentals: UIAlertView and UIAlertController (tutorial)
Action Sheet (show me an example)
Action Sheets give the user a list of choices. They appear either at the bottom of the screen or in a popover depending on the size and orientation of the device. As with alerts, a UIAlertController is used to make an action sheet. Before iOS 8, UIActionSheet was used, but now the documentation says:
Important: UIActionSheet is deprecated in iOS 8. (Note that UIActionSheetDelegate is also deprecated.) To create and manage action sheets in iOS 8 and later, instead use UIAlertController with a preferredStyle of UIAlertControllerStyleActionSheet.
Modal View (show me an example)
A modal view is a self-contained view that has everything it needs to complete a task. It may or may not take up the full screen. To create a modal view, use a UIPresentationController with one of the Modal Presentation Styles.
See also
Presenting View Controllers from Other View Controllers
Modal Contexts
Popover (show me an example)
A Popover is a view that appears when a user taps on something and disappears when tapping off it. It has an arrow showing the control or location from where the tap was made. The content can be just about anything you can put in a View Controller. You make a popover with a UIPopoverPresentationController. (Before iOS 8, UIPopoverController was the recommended method.)
In the past popovers were only available on the iPad, but starting with iOS 8 you can also get them on an iPhone (see here, here, and here).
See also
View Controllers: Popovers
Notifications
Notifications are sounds/vibrations, alerts/banners, or badges that notify the user of something even when the app is not running in the foreground.
See also
Local and Remote Notification Programming Guide
Simple, interactive notifications in iOS 8
A note about Android Toasts
In Android, a Toast is a short message that displays on the screen for a short amount of time and then disappears automatically without disrupting user interaction with the app.
People coming from an Android background want to know what the iOS version of a Toast is. Some examples of these questions can he found here, here, here, and here. The answer is that there is no equivalent to a Toast in iOS. Various workarounds that have been presented include:
Make your own with a subclassed UIView
Import a third party project that mimics a Toast
Use a buttonless Alert with a timer
However, my advice is to stick with the standard UI options that already come with iOS. Don't try to make your app look and behave exactly the same as the Android version. Think about how to repackage it so that it looks and feels like an iOS app.
Since the release of iOS 8, UIAlertView is now deprecated; UIAlertController is the replacement.
Here is a sample of how it looks in Swift 5:
let alert = UIAlertController(title: "Hello!", message: "Message", preferredStyle: .alert)
let alertAction = UIAlertAction(title: "OK!", style: .default) { (sender: UIAlertAction) -> Void in
// ... Maybe handle "OK!" being tapped.
}
alert.addAction(alertAction)
// Show.
present(alert, animated: true) { () -> Void in
// ... Maybe do something once showing is complete.
}
As you can see, the API allows us to implement callbacks for both the action and when we are presenting the alert, which is quite handy!
For older Swift version:
let alert = UIAlertController(title: "Hello!", message: "Message", preferredStyle: UIAlertControllerStyle.alert)
let alertAction = UIAlertAction(title: "OK!", style: UIAlertActionStyle.default)
{
(UIAlertAction) -> Void in
}
alert.addAction(alertAction)
present(alert, animated: true)
{
() -> Void in
}
Since iOS 8.0, you will need to use UIAlertController as the following:
-(void)alertMessage:(NSString*)message
{
UIAlertController* alert = [UIAlertController
alertControllerWithTitle:#"Alert"
message:message
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction
actionWithTitle:#"OK" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[alert addAction:defaultAction];
[self presentViewController:alert animated:YES completion:nil];
}
Where self in my example is a UIViewController, which implements "presentViewController" method for a popup.
For Swift 3 & Swift 4 :
Since UIAlertView is deprecated, there is the good way for display Alert on Swift 3
let alertController = UIAlertController(title: NSLocalizedString("No network connection",comment:""), message: NSLocalizedString("connected to the internet to use this app.",comment:""), preferredStyle: .alert)
let defaultAction = UIAlertAction(title: NSLocalizedString("Ok", comment: ""), style: .default, handler: { (pAlert) in
//Do whatever you want here
})
alertController.addAction(defaultAction)
self.present(alertController, animated: true, completion: nil)
Deprecated :
This is the swift version inspired by the checked response :
Display AlertView :
let alert = UIAlertView(title: "No network connection",
message: "You must be connected to the internet to use this app.", delegate: nil, cancelButtonTitle: "Ok")
alert.delegate = self
alert.show()
Add the delegate to your view controller :
class AgendaViewController: UIViewController, UIAlertViewDelegate
When user click on button, this code will be executed :
func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int) {
}
Although I already wrote an overview of different kinds of popups, most people just need an Alert.
How to implement a popup dialog box
class ViewController: UIViewController {
#IBAction func showAlertButtonTapped(_ sender: UIButton) {
// create the alert
let alert = UIAlertController(title: "My Title", message: "This is my message.", preferredStyle: UIAlertController.Style.alert)
// add an action (button)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
// show the alert
self.present(alert, animated: true, completion: nil)
}
}
My fuller answer is here.
Here is C# version in Xamarin.iOS
var alert = new UIAlertView("Title - Hey!", "Message - Hello iOS!", null, "Ok");
alert.Show();