After upgrading to macOS Ventura I can no longer have multi-line alert views with a scroll bar. The dialog will not show and the app freezes.
let myAlertTest = UIAlertController(title: "TITLE", message: "If this line is too long, the app will freeze instead of showing scroll bars. Worked fine with Monterey", preferredStyle: UIAlertController.Style.alert)
myAlertTest.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
self.present(myAlertTest, animated: true, completion: nil)
Related
My situation is that I want to change the background color of the alerts as well as the text. I've seen some examples, but they don't appear to be working anymore. Here is what I've tried:
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: btnTitle, style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
let subview :UIView = alert.view.subviews.last! as UIView
let alertContentView = subview.subviews.last! as UIView
alertContentView.backgroundColor = UIColor.black
I have also tried to do this:
alert.view.backgroundColor = UIColor.black
and also putting them in the completion handlers, but it doesn't appear to be working like previous versions. What is appearing instead is the alert with the usual rounded corners and outside the rounded corners, the black background appears in the back to make the alert a square box (normally its rounded and the part where the black is showing would be transparent). The color just won't go over the alert's white color.
you have to change the color(alert.view.backgroundColor = UIColor.green) before you do self.present(alert, animated: true, completion: nil)
It Works for me like that.. i have used green and red
I'd like to create a very common effect as in the picture below :
Explanation : the effect I'd like to accomplish consists in a view that appears (slides in) at the bottom of the screen when user clicks a button : you can still see the screen behind this view, but it applies a "dark layer" (black view with let's say 60% opacity) on the top of it. When user clicks on Block or Report absue (as for this example) it would perform the respective actions. Now when user clicks on cancel, and also when he clicks anywhere on the "dark layer" it would bring him back to the screen.
What I tried : presenting a new view controller (but it would use more data than necessary), using a overlay layer but I even didnt get close to that effect that we're usually seeing on apps. I'm not sure but I'd say that the best way to get that effect is using views ?
Does anyone have an idea please ?
Thanks and have a good day,
J
You are looking for a native element called UIActionSheet. It has become the part of UIAlertController, and does exactly what you are looking for.
Here is a little help how to set up one:
// Create you actionsheet - preferredStyle: .actionSheet
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
// Create your actions - take a look at different style attributes
let reportAction = UIAlertAction(title: "Report abuse", style: .default) { (action) in
// observe it in the buttons block, what button has been pressed
print("didPress report abuse")
}
let blockAction = UIAlertAction(title: "Block", style: .destructive) { (action) in
print("didPress block")
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) in
print("didPress cancel")
}
// Add the actions to your actionSheet
actionSheet.addAction(reportAction)
actionSheet.addAction(blockAction)
actionSheet.addAction(cancelAction)
// Present the controller
self.present(actionSheet, animated: true, completion: nil)
It should produce the following output:
I am trying to get a UIAlterController to display (as soon as the scene is set up) with the following buttons:
Accept - won't do anything, just close the UIAlertController
Leave to Menu - should transition to the MenuScene
My code looks as follows (I put the alert controller inside an action so it first displays the scene and after 0.5 seconds it should start displaying the alert):
//make action
var actionBlock = SKAction.runBlock({
//alert controller
var alert = UIAlertController(title: "Welcome", message: "Are you sure you want to proceed?", preferredStyle: UIAlertControllerStyle.Alert)
//action with no handler
var acceptAction = UIAlertAction(title: "Yes", style: UIAlertActionStyle.Cancel, handler: nil)
//action thats supposed to transition to MenuScene (AFTER BEING TAPPED!!)
var leaveAction = UIAlertAction(title: "Back to Menu", style: UIAlertActionStyle.Destructive, handler: {_ in self.backToMenu()})
//add action to the alert controller
alert.addAction(acceptAction)
alert.addAction(leaveAction)
//add alert controller to view
self.view?.window?.rootViewController?.presentViewController(alert, animated: true, completion: nil)
}
//wait for 0.5s action
var wait = SKAction.waitForDuration(0.5)
//run the action
self.runAction(SKAction.sequence([wait, actionBlock]))
and I placed it right into the DidMoveToView function. When I run the code, it immediately transitions back to the MenuScene without even waiting for the user to tap either one of the actions, and then the alert is still on the screen (but on the MenuScene).
I am really confused and I've been on this for several hours but I just can't figure out what my mistake is, and how to make it work..
Anybody help please? Any code that works would be really appreciated!!!
This question already has answers here:
Prevent dismissal of UIAlertController
(5 answers)
Closed 8 years ago.
I am trying to use new UIAlertController introduced in iOS 8. Everything works great except the fact that UIAlertAction always end up dismissing the alert controller in its callback. Following is my code:
let alert = UIAlertController(title: "New Group", message: "Enter Group name", preferredStyle: UIAlertControllerStyle.Alert);
alert.addTextFieldWithConfigurationHandler({ [weak self] (nameField: UITextField!) in
nameField.becomeFirstResponder();
nameField.delegate = self;
return;
})
alert.addAction(UIAlertAction(title: "Done", style: .Default, handler: { action in
println("Done Entering");
}));
alert.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: nil));
self.presentViewController(alert, animated: true, completion: nil);
Now, when I click "Done" button, the controls enters the callback method and then alert is dismissed even though I don't have any statement to dismiss the alert. Is this behavior by default? If yes, how can I make sure that in some cases the alert stays on the screen (depending upon my conditions)? Am I missing something here?
I would really appreciate any help regarding this.
Yes, alert buttons always dismiss the alert and there is no way to configure them otherwise. If you want that behavior, you'll have to write your own alert. I've written SDCAlertView, which looks very much like a normal alert, but has some added functionality, including prevention of dismissing an alert when a button is tapped.
However, it doesn't use the UIAlertController API yet and it looks a little bit different (most users won't notice) on iOS 8 than the UIAlertController alert.
EDIT: It now has support for the UIAlertController-like API.
Using Xcode6-Beta5 I've built a Universal Swift app with a Deployment Target of 7.0.
I built a test app which has both UIActionSheet and UIAlertController - a button selects which one is called at run-time.
To summarize first:
1. Putting 8.0 stuff into the app causes launch failure in 7.1 devices, even without executing the 8.0 code. The 8.0 calls pollute the app.
2. Neither UIActionSheet nor UIAlertController work at all on iPad 8.0 devices.
Running in the Simulator:
All iPhone 8.0 targets can display both UIActionSheet and UIAlertController successfully.
All iPhone 7.1 targets fail to launch with symbol not found errors for UIAlertAction or UIAlertController. This is a dyld_fatal_error.
All iPad 7.1 targets fail to even launch, the same way the iPhone 7.1 targets fail.
If I comment out the UIActionController and UIActionAlert code then the app works on 7.1 devices running UIActionSheet.
In all iPad 8.0 targets running UIActionSheet display nothing and generates about 80 lines of NSLayoutConstraint problems.
In all iPad 8.0 targets running UIActionController throws a SIGABRT into AppDelegate with:
2014-08-15 10:21:51.799 AlertControllerTest[1828:246107] * Terminating app due to uncaught exception 'NSGenericException', reason: 'UIPopoverPresentationController (<_UIAlertControllerActionSheetRegularPresentationController: 0x78651780>) should have a non-nil sourceView or barButtonItem set before the presentation occurs.'
Are these still broken in Xcode6-Beta5 or am I doing something wrong?
Is it supposed to be possible to run on 7.1 apps built with 8.0 functions as long as the 8.0 functions are not invoked? Earlier versions used to work that way.
Before someone asks to see the source code (now revised for best with GM):
#IBAction func tapHereTapped(sender: AnyObject) {
if sheetOrControllerButton.selectedSegmentIndex == 0 {
var actionSheet = UIActionSheet()
actionSheet.addButtonWithTitle("Choice 1")
actionSheet.addButtonWithTitle("Choice 2")
actionSheet.addButtonWithTitle("Choice 3")
actionSheet.addButtonWithTitle("Choice 4")
actionSheet.addButtonWithTitle("Cancel")
if UIDevice.currentDevice().userInterfaceIdiom == UIUserInterfaceIdiom.Pad {
actionSheet.addButtonWithTitle("Dummy")
let systemVersion = UIDevice.currentDevice().systemVersion
if NSString(string: UIDevice.currentDevice().systemVersion).doubleValue >= 8 {
//actionSheet.cancelButtonIndex = 4
//actionSheet.cancelButtonIndex = 5
} else {
actionSheet.cancelButtonIndex = 4
}
} else {
actionSheet.cancelButtonIndex = 4
}
actionSheet.title = "Pick a Number"
actionSheet.delegate = self
actionSheet.showInView(self.view)
} else {
var actionSheet:UIAlertController
if sheetOrControllerButton.selectedSegmentIndex == 1 {
actionSheet = UIAlertController(title: "Pick a Number", message: "Why?", preferredStyle: UIAlertControllerStyle.Alert)
} else {
actionSheet = UIAlertController(title: "Pick a Number", message: "Why?", preferredStyle: UIAlertControllerStyle.ActionSheet)
if UIDevice.currentDevice().userInterfaceIdiom == UIUserInterfaceIdiom.Pad {
var button = sender as UIButton
actionSheet.popoverPresentationController?.sourceRect = button.bounds
actionSheet.popoverPresentationController?.sourceView = button.viewForBaselineLayout()
}
}
actionSheet.addAction(UIAlertAction(title: "Choice 1", style: UIAlertActionStyle.Default, handler: { (action :UIAlertAction!)in
println("Picked 1")
}))
actionSheet.addAction(UIAlertAction(title: "Choice 2", style: UIAlertActionStyle.Default, handler: { (action :UIAlertAction!)in
println("Picked 2")
}))
actionSheet.addAction(UIAlertAction(title: "Choice 3", style: UIAlertActionStyle.Default, handler: { (action :UIAlertAction!)in
println("Picked 3")
}))
if UIDevice.currentDevice().userInterfaceIdiom == UIUserInterfaceIdiom.Pad {
actionSheet.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Default, handler: { (action :UIAlertAction!)in
println("Canceled")
}))
} else {
actionSheet.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: { (action :UIAlertAction!)in
println("Canceled")
}))
}
self.presentViewController(actionSheet, animated: true, completion: nil)
}
}
func actionSheet(theActionSheet: UIActionSheet!, clickedButtonAtIndex buttonIndex: Int) {
if (buttonIndex == 0) {
println("Picked 1")
} else if buttonIndex == 1 {
println("Picked 2")
} else if buttonIndex == 2 {
println("Picked 3")
} else if buttonIndex == 3 {
println("Picked 4")
} else if buttonIndex == 4 {
println("Canceled")
} else {
println("What??")
}
}
I get the same behavior on physical iPhone 4 - 7.1, iPad 2 - 7.1 All 8.0 testing was in Simulator - I haven't put 8.0 on any device yet not wanting to brick it.
Thanks all.
UPDATED for Beta6:
Note - this is still Simulator testing.
We now have complete work-arounds to build a Universal app that will work on 7.1 and 8.0.
iPhone - Test for iOS version and use UIActionSheet or UIAlertController as needed. Both .ActionSheet and .Alert styles work on 8.0.
iPad 7.1 - UIActionSheet now "works", but you need to add a dummy extra button - it fails to display the last button added.
iPad 8.0 - UIAlertController .Alert works, but looks bad, and .ActionSheet still crashes as does UIActionSheet.
UPDATED for Beta7:
No changes. iPhones work as expected. iPads still broken as above.
UPDATED for GM:
Some improvement for iPad 8.0. (Or else I didn't figure it out before.)
iPhones still work correctly. Including the iPhone 6 and iPhone 6 Plus in the simulator.
iPad 7.1 - UIActionSheet still needs dummy button. The cancelButtonIndex will darken the text to indicate that it's the cancel button, but it does not offset it.
iPad 8.0 - UIActionSheet doesn't crash, but don't use it. If the cancelButtonIndex is not -1 then there are problems with which buttons at the end of the list are displayed. It might still need a dummy button. But now the cancelButtonIndex must be set to the dummy button (which will not display). But even worse, the clickedButtonAtIndex is called twice, once with the clicked button and once with the cancelButtonIndex. It's as though it's detecting a bogus when the action sheet closes.
iPad 8.0 - UIAlertController with .Alert is still OK.
iPad 8.0 - UIAlertController with .ActionSheet can now work if the popoverPresentationController sourceRect and sourceView are set. (This was possibly the case earlier, too, but I'm not going to retest the older betas.). But, there's one more gotcha -- UIAlertActionStyle.Cancel causes the button to disappear, so don't use it.
UPDATED for 6.0.1:
iPad 7.1 - UIActionSheet still needs dummy button.
iPad 8.0 - UIAlertController with .Alert is OK. The button with UIAlertActionStyle.Cancel is automatically placed at the bottom of the list of buttons.
iPad 8.0 - UIAlertController with .ActionSheet still needs the popoverPresentationController values set. And UIAlertActionStyle.Cancel still fails.
Summary - no improvements yet:
iPhone - all versions work fine - can use UIActionSheet and UIAlertController as .Alert and .ActionSheet.
iPad 7.1 - UIActionSheet works with dummy.
iPad 8.0 - UIAlertController works as .Alert and .ActionSheet. Don't use UIActionSheet.