I have a button in a simple view with on click function :
#IBAction func watchAlert(sender: AnyObject) {
showAlert()
for i in 1...2{
AudioServicesPlaySystemSound (1005);
sleep(2)
}
}
func showAlert(){
var alert = UIAlertController(title: "Alert", message: "Message", preferredStyle: UIAlertControllerStyle.Alert)
self.presentViewController(alert, animated: true, completion: nil)
alert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: dismissAlert ))
}
func dismissAlert(alertView: UIAlertAction!)
{
println("User click ok button")
}
Even though I am trying to call showAlert() before playing the audio, I could only see the alert getting displayed only after sound gets played. I want the alert to be present first and then sound should play.
Any help is appreciated.
Take out the sleep command (you must never sleep the main thread - the Watchdog process will kill you dead, and besides, you are wrongly freezing the whole interface). Instead: show the alert, then use delayed performance (as in my delay utility here) to play a sound.
(There are other problems with your code; in particular, you will also need to find a new way to play the sound twice, since you must not use sleep; this, too, can be accomplished using delay - nested.)
Related
I'm new to Swift programming, but can't find an answer to my problem, which is...
When I present a simple UIAlertController with a UIAlertAction handler, I am expecting the alert to display until the user responds, then the handler is executed, before continuing with the remaining code.
Unexpectedly, it seems to finish off the code block before displaying the alert and executing the handler.
I've searched Stackoverflow, and re-read the Apple Developer Documentation for UIAlertController and UIAlertAction, but I can't figure out why the code doesn't pause until the user responds.
I've tried putting the UIAlertController code in its own function, but the alert still appears to be displaying out of sequence. I'm thinking maybe there needs to be a delay to allow the Alert to draw before the next line of code executes(?).
#IBAction func buttonTapped(_ sender: Any) {
let alert = UIAlertController(title: "Ouch", message: "You didn't have to press me so hard!", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Sorry", style: .default, handler: { _ in
self.handleAlert()
}))
self.present(alert, animated: true, completion: nil)
print("Should be printed last!")
}
func handleAlert() {
print("UIAlertAction handler printed me")
}
In the code above I am expecting the debug console to display:
UIAlertAction handler printed me
Should be printed last!
But instead it displays:
Should be printed last!
UIAlertAction handler printed me
Instead of adding a seperate function, can you put it within the alert action itself like this...
let alert = UIAlertController(title: "Ouch", message: "You didn't have to press me so hard!", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Sorry", style: .default, handler: { action in
// code for action goes here
}))
self.present(alert, animated: true)
UIAlertController is designed to run asynchronously (that is why it has you pass a block of code to execute when the action is performed instead of giving a return value)
So to fix your code, put the code you want to run after an action is chosen in another function, then call that function at the end of each UIAlertAction handler.
private var currentlyShowingAlert = false
#IBAction func buttonTapped(_ sender: Any) {
if currentlyShowingAlert {
return
}
let alert = UIAlertController(title: "Ouch", message: "You didn't have to press me so hard!", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Sorry", style: .default, handler: { _ in
self.handleAlert()
self.alertCleanup()
}))
self.present(alert, animated: true, completion: nil)
currentlyShowingAlert = true
}
func handleAlert() {
print("UIAlertAction handler printed me")
}
func alertCleanup() {
print("Should be printed last!")
currentlyShowingAlert = false
}
Be careful when doing things like pushing view controllers (or anything where the calls will stack up) in direct response to a button press.
When the main thread is busy, the button can be pressed multiple times before the first buttonTapped call happens, in that case buttonTapped could be called many times in a row, currentlyShowingAlert will prevent that issue.
I have a game which displays an alert whenever a player wins. However after restarting the game and the same alert shows up multiple 'cancel' buttons show up. just like showed in the picture. any ideas what it could be,
var alertX = UIAlertController(title: "Winner", message: "X Has Won", preferredStyle:.alert)
func AlertPlayer1() {
alertX.addAction(UIAlertAction(title:"CLOSE",style: UIAlertAction.Style.destructive, handler: { (action) in self.alertX.dismiss(animated: true, completion: nil)}))
self.present(alertX, animated:true, completion:nil)
}
I have simply then just called the function whenever somebody wins
Please update your code as following to fix issue.
func AlertPlayer1() {
var alertX = UIAlertController(title: "Winner", message: "X Has Won", preferredStyle:.alert)
alertX.addAction(UIAlertAction(title:"CLOSE",style: UIAlertAction.Style.destructive, handler: { (action) in
self.alertX.dismiss(animated: true, completion: nil)
}))
self.present(alertX, animated:true, completion:nil)
}
You are creating alert instance single time, but this method AlertPlayer1 call multiple time from somewhere in your code which are adding multiple close button.
Note: As per I already told you, you method calling multiple time. So this alert also try to present multiple time, but at a time your can present only one view controller in window/screen. So it will show you warning in console.
I make call on button click. My code:
if let phoneCallURL:NSURL = NSURL(string: "tel://\(phoneNumber)") {
let application:UIApplication = UIApplication.shared
if (application.canOpenURL(phoneCallURL as URL)) {
application.openURL(phoneCallURL as URL);
}
else {
let alertController = UIAlertController(title: NSLocalizedString("CALL_C_TITLE", comment: "Call disallowed"), message: NSLocalizedString("CALL_C_DESC", comment: "This device cannot call"), preferredStyle: UIAlertControllerStyle.actionSheet)
alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "OK"), style: UIAlertActionStyle.default, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
}
This code work fine, but i want to run some code after user end call. How I can do this?
When the call is ended and your application regains focus, it will receive the UIApplicationDidBecomeActive (docs) notification. You can register your ViewController as an observer for this and respond accordingly.
Keep in mind that you will receive this notification for other event sequences too (returning from background, dismissing notifications, etc.), so you may want to set a flag when starting the call and check for that flag in the notification handler if you only want to run code after having made the call.
This is my code:
func pullToRefresh() {
if Reachability().connectionStatus() == .Online {
self.homewebview.reload()
} else {
self.refreshControl.endRefreshing()
let alert = UIAlertView(title: "No Internet connection", message: "Please check your Internet connection.", delegate: nil, cancelButtonTitle: "Okay")
alert.show()
}
}
When internet connection is not available and the user pulls to refresh an alert should be shown and the animation should stop. That works great on iOS 9. But on iOS 10 Beta 2 the animation doesn't disappear. The user have to pull up to make it disappear. It that an iOS 10 bug or am I doing something wrong?
P.S. I already tried to add assert(NSThread.isMainThread()) but that didn't help.
I've just encountered this issue and for those using UIAlertController, you may wish to try something similar to the following:
[self presentViewController:alert animated:TRUE completion:^{
[self.refreshControl endRefreshing];
}];
This enables the loading spinner to disappear in the background as soon as the alert has been displayed.
Swift Equivalent:
present(alert, animated: true) {
self.refreshControl.endRefreshing()
}
I also met the same issue. Just work around: please change like below
let alert = UIAlertView(title: "No Internet connection", message: "Please check your Internet connection.", delegate: nil, cancelButtonTitle: "Okay")
alert.show()
self.refreshControl.endRefreshing()
I search in Google and some guys talk about interfere when showing other view but not know the root cause
I think when we are presenting the UIAlertController at the same time refreshController is trying to stop itself. That is why it is not getting time to stop refreshing and stays sometimes. So in my case I put endRefreshing code after 1 second and its working smooth now.
Here is the code which I have used:
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.refreshControl.endRefreshing()
}
It had similar issues and solved it this way. I have a function to handle all fails (no connectivity, api errors) which is called as a notification, but I will simplify it here.
First I am calling endRefreshing asynchronously (try without, you should see some errors in the console). Then I am delaying the alert 1 second, giving the refresh control enough time to end.
func updatedStatusFailed(notification: NSNotification) {
DispatchQueue.main.async {
self.refresher.endRefreshing()
}
self.helpers.delay(1.0, closure: {
let alert = UIAlertController(title: "Error", message: "Error Description", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
})
}
My delay function comes from a helpers class because I use it a few times in my app.
func delay(_ delay:Double, closure:#escaping ()->()) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
In many cases you may be calling an api when the view loads/appears and have the pull to refresh logic. In this case you might want to add this line so that you only delay by a second if the refresher is refreshing:
let delayTime:Double = self.refresher.isRefreshing ? 1.0 : 0.0
UPDATE: 20/1/16
Although my somewhat hacky answer works, in the end I opted to have a section in my tableView to display errors, rather than the alert. It was a lot more code and complexity, but the UX is a lot better.
I have an alert that pops up when the user runs out of time. Right now it just vibrates but I also want it to play an alarm sound. Is there a way to play whatever the user has set as the system default alarm sound is? Also, is it possible to have the alarm keep going until the user clicks the dismiss button?
Here is my code so far:
func dismissAlert(alert:UIAlertAction!)
{
self.dismissViewControllerAnimated(true, completion: nil)
}
let outOfTimeAlert = UIAlertController(title: "Out Of Time", message: "Better luck next time", preferredStyle: UIAlertControllerStyle.Alert)
let cancelAlert: UIAlertAction = UIAlertAction(title: "Dismiss", style: .Cancel, handler: dismissAlert)
outOfTimeAlert.addAction(cancelAlert)
presentViewController(outOfTimeAlert, animated: true, completion: nil)
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate)
Thanks for the help!