I am building a game.
This game has a UITableViewController which serves as a Settings & Options feature.
One of the options that you can select is to switch the opponent from pass and play to an AI (or the other way around).
What I'd like to do is use a UIAlertController to intercept the touch, check for a game in progress, and prompt the user to cancel the change or continue.
I have implemented the code to do this within the override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) function.
My code is as follows:
if indexPath.section == 1 {
// Here we need to detect a game in progress and ask for a confirmation before switching to another mode.
if getGameInProgress() == true {
// Display choice dialog
alert = UIAlertController (title: alertCaption, message: alertMessage, preferredStyle: .Alert)
let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: {(action: UIAlertAction) -> Void in
self.alert.dismissViewControllerAnimated(true, completion: { _ in })
return
})
alert.addAction(cancelAction)
let ok: UIAlertAction = UIAlertAction(title: "Confirm", style: .Default, handler: {(action: UIAlertAction) -> Void in
self.alert.dismissViewControllerAnimated(true, completion: { _ in })
// Do nothing, just let the touch method continue...
})
alert.addAction(ok)
self.presentViewController (alert, animated: true, completion: nil)
}
if indexPath.row == 0 {
// "Pass and Play"
setOpponentPreference("Pass and Play")
}
if indexPath.row == 1 {
// "Virtual Robert"
setOpponentPreference("Virtual Robert")
}
if indexPath.row == 2 {
// "Virtual Nathan"
setOpponentPreference("Virtual Nathan")
}
// No matter which difficulty selected:
gameboard.setDifficulty()
// Create a method to reset the game, but without fading out the menu screen (the current new game method takes you directly to the game. The player may not want that in this instance.)
}
The problem I am having is that the alert controller displays and waits for input, but it does not prevent the rest of the didSelectRowAtIndexPath function form continuing.
How do I receive the users input before handling the touch on the cell?
Hello All you need is put your code inside of your completion section for your "Confirm" option, your code will be execute only when user tap on "Confirm" button of your UIAlertViewController
self.alert.dismissViewControllerAnimated(true, completion: { _ in })
// Do nothing, just let the touch method continue...
})
so your code must be
if indexPath.section == 1 {
// Here we need to detect a game in progress and ask for a confirmation before switching to another mode.
if getGameInProgress() == true {
// Display choice dialog
alert = UIAlertController (title: alertCaption, message: alertMessage, preferredStyle: .Alert)
let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: {(action: UIAlertAction) -> Void in
self.alert.dismissViewControllerAnimated(true, completion: { _ in })
return
})
alert.addAction(cancelAction)
let ok: UIAlertAction = UIAlertAction(title: "Confirm", style: .Default, handler: {(action: UIAlertAction) -> Void in
self.alert.dismissViewControllerAnimated(true, completion: { _ in })
if indexPath.row == 0 {
// "Pass and Play"
setOpponentPreference("Pass and Play")
}
if indexPath.row == 1 {
// "Virtual Robert"
setOpponentPreference("Virtual Robert")
}
if indexPath.row == 2 {
// "Virtual Nathan"
setOpponentPreference("Virtual Nathan")
}
// No matter which difficulty selected:
gameboard.setDifficulty()
// Create a method to reset the game, but without fading out the menu screen (the current new game method takes you directly to the game. The player may not want that in this instance.)
})
alert.addAction(ok)
self.presentViewController (alert, animated: true, completion: nil)
}
}
I hope this helps you, for me works great, regards
Sorry for the bad English. What I understand from you question is that you want to hold the program to continue execution till user select one of the button from UIAlertController.
for that you need to do is perform task inside the action of Ok button or Cancel button.
if getGameInProgress() == true {
// Display choice dialog
alert = UIAlertController (title: alertCaption, message: alertMessage, preferredStyle: .Alert)
let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: {(action: UIAlertAction) -> Void in
self.alert.dismissViewControllerAnimated(true, completion: { _ in })
return
})
alert.addAction(cancelAction)
let ok: UIAlertAction = UIAlertAction(title: "Confirm", style: .Default, handler: {(action: UIAlertAction) -> Void in
//Do something in here like print
println("Confirm Button Pressed")
self.alert.dismissViewControllerAnimated(true, completion: { _ in })
// Do nothing, just let the touch method continue...
})
alert.addAction(ok)
self.presentViewController (alert, animated: true, completion: nil)
}
hope it help.
Related
I would like to add a method to my ViewController that shows a message with text as an alert with a Yes and a No button. The result should be of type Bool (Yes/No).
What I have tried is the following:
func YesNoBox(msg: String) -> Bool
{
var retVal = false
let alert = UIAlertController(title: "", message: msg, preferredStyle: .alert)
let action_yes = UIAlertAction(title: "Yes", style: .default, handler:
{ _ in NSLog("The \"Yes\" alert occured."); retVal = true })
let action_no = UIAlertAction(title: "No", style: .cancel, handler:
{ _ in NSLog("The \"No\" alert occured."); retVal = false })
alert.addAction(action_yes)
alert.addAction(action_no)
self.present(alert, animated: true, completion: nil)
return retVal
}
However, the value of retVal is always false. If I was in C/C++, I guess I could resolve this issue with a pointer, but this is Swift (and I am pretty new to this).
Any idea anyone how I could get this working?
EDIT: The problem that I have is the following. On a ViewController I have a TextField. When I tap on the text field, the app should ask the user whether they want to paste the text from the clipboard. If yes, paste, otherwise give the TextField the focus (i.e. let the cursor blink in it). I tried to do this with 'textFieldShouldBeginEditing' and in this method I display the YesNoBox. The problem is that the TextField never gets the focus after the YesNoBox is closed. And when I use 'becomeFirstResponder()' after the Box call, the app freezes. I don't know what to do?
Use a completion
func yesNoBox(msg: String,completion:#escaping(Bool) -> ())
{
let alert = UIAlertController(title: "", message: msg, preferredStyle: .alert)
let action_yes = UIAlertAction(title: "Yes", style: .default, handler:
{ _ in
NSLog("The \"Yes\" alert occured.");
completion(true)
})
let action_no = UIAlertAction(title: "No", style: .cancel, handler:
{ _ in
NSLog("The \"No\" alert occured.");
completion(false)
})
alert.addAction(action_yes)
alert.addAction(action_no)
self.present(alert, animated: true, completion: nil)
}
call
yesNoBox(msg:"someMessage") { yes in
if yes {
// do yes action
}
else {
// do no action
}
}
2 Callbacks:
This function has 2 completions ( imagine we have a function that uploads an image and notifies the progress with a completion and another 1 to say done )
func uploadImage(data: Data,progress:#escaping(Float) -> (),completion:#escaping(Bool) -> ()) {
// implementation here
}
To call
self.uploadImage(someData) { progress in
print(progress)
}) { done in
print(done)
}
This can be achieved with completion handlers.
func showAlertWithOptions(title: String, message: String, completionHandler: #escaping (Bool) -> Void) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
let action_yes = UIAlertAction(title: "Yes", style: .default, handler: { _ in
completionHandler(true)
})
let action_no = UIAlertAction(title: "No", style: .cancel, handler: { _ in
completionHandler(false)
})
alert.addAction(action_yes)
alert.addAction(action_no)
self.present(alert, animated: true, completion: nil)
}
Now call the function and add any other functions or actions that you want to perform depending on the action selected.
showAlertWithOptions(title: "Any title", message: "Any message") { success in
if success {
NSLog("The \"Yes\" alert occured.")
} else {
NSLog("The \"No\" alert occured.")
}
}
I'm trying to make an app where if the score is 3 the app displays a message that says "you lose" but keeps '3' as the number in the score label until the End Game option in the popup is pressed, at which point the score goes back to 0 for a new game. I am new to swift and am having difficulty and would really appreciate any and all help! I am not sure if making an IBAction for the alert action is the right thing to do or not.
else if rightscorecount == 3 {
let alert = UIAlertController(title: "Game", message: "You Lose!", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "End Game", style: UIAlertActionStyle.default) { UIAlertAction in})
self.present(alert, animated: true, completion: nil)
}
}
#IBAction func test(sender: UIAlertAction) {
rightscorecount = 0
rightscorelabel.text = String(rightscorecount)
}
Try this:
let alertController = UIAlertController.init(title: "Game", message: "You Lose!", preferredStyle: .alert)
alertController.addAction(UIAlertAction.init(title: "End Game", style: .default, handler: { (action) in
// Your handler goes here
self.someFunction()
}))
self.present(alertController, animated: true) {
// Completion block
}
And your function
func someFunction() {
// Function body goes here
}
I have a very simple project: there's only one ViewController and a UIButton. The IBAction for the button is:
var alertViewControllerTextField: UITextField?
var promptController = UIAlertController(title: "Type Something", message: nil, preferredStyle: .alert)
let ok = UIAlertAction(title: "OK", style: .default, handler: { (action) -> Void in
print("\(alertViewControllerTextField?.text)")
})
let cancel = UIAlertAction(title: "Cancel", style: .cancel) { (action) -> Void in
//promptController = nil
}
promptController.addAction(ok)
promptController.addAction(cancel)
promptController.addTextField { (textField) -> Void in
alertViewControllerTextField = textField
}
self.present(promptController, animated: true, completion: nil)
When the app finishes launching, memory usage is 14,4 Mb.
When I click the button it reaches 18,4 Mb (if I click again and again the button, it finally reaches 20 Mb).
Anyway, I thought when I clicked the UIAlertController's cancel or ok button, memory would have returned to 14,4, even slowly, but this is not the case.
I thought to make the UIAlertController an optional to have the chance to assign it a nil on close, but the UIAlertController can't be nil because you can't declare it as an optional. I thought to make it a member and declare it with the weak keyword (no luck).
So, is there any way to reduce the memory usage when I click one of the buttons of a UIAlertController?
PLease Put DispatchQueue this code than try i solved this issue by this
DispatchQueue.global(qos: .userInitiated).async
{
self.present(promptController, animated: true, completion: nil)
}
I have seen elsewhere that you need to declare the alertViewControllerTextField weak so that the actions don't retain it. (Not sure I fully understand this yet.)
So try:
var alertViewControllerTextField: UITextField?
var promptController = UIAlertController(title: "Type Something", message: nil, preferredStyle: .alert)
let ok = UIAlertAction(title: "OK", style: .default, handler: { [weak alertViewControllerTextField](action) -> Void in
print("\(alertViewControllerTextField?.text)")
})
let cancel = UIAlertAction(title: "Cancel", style: .cancel) { [weak promptController] (action) -> Void in
//promptController = nil
}
promptController.addAction(ok)
promptController.addAction(cancel)
promptController.addTextField { (textField) -> Void in
alertViewControllerTextField = textField
}
self.present(promptController, animated: true, completion: nil)
I am trying to create a UIAlertController with two options 'Cancel' and 'Log out'. I want the 'Cancel' button to cancel the alert and the 'Log out' button to perform the segue associated with it, which i have set up in the storyboard.
My code is;
class HomeVC: UIViewController {
#IBAction func SignOutBtn(sender: UIButton) {
let alertController = UIAlertController(title: "Alert",
message: "Are you sure you want to log out?",
preferredStyle: .Alert)
let cancelAction = UIAlertAction(title:"Cancel",
style: .Cancel) { (action) -> Void in
print("You selected the Cancel action.")
}
let submitAction = UIAlertAction(title:"Log out",
style: .Default) { (action) -> Void in
print("You selected the submit action.")
self.presentedViewController
}
alertController.addAction(submitAction)
alertController.addAction(cancelAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
}
Well it seems your'e missing the actions you want to perform inside the blocks.
(also, you may want to dismiss the alert controller, inside the blocks as well.)
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: { (action) -> Void in
print("You selected the Cancel action.")
alertController.dismissViewControllerAnimated(true, completion: nil)
})
let submitAction = UIAlertAction(title: "Log out", style: .Default, handler: { (action) -> Void in
print("You selected the submit action.")
alertController.dismissViewControllerAnimated(true, completion: { () -> Void in
// Perform your custom segue action you need.
})
})
If you have set up a segue from one view to another, in your button handler for log out then you can call self.performSegueWithIdentifier("storyboadIdentifier") which will call the prepareForSeguemethod so you can modify or pass info along the segue if need be.
#pbush25
class HomeVC: UIViewController {
#IBAction func SignOutBtn(sender: UIButton) {
let alertController = UIAlertController(title: "Alert",
message: "Are you sure you want to log out?",
preferredStyle: .Alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: { (action) -> Void in
print("You selected the Cancel action.")
alertController.dismissViewControllerAnimated(true, completion: nil)
})
let submitAction = UIAlertAction(title: "Log out", style: .Default, handler: { (action) -> Void in
print("You selected the submit action.")
alertController.dismissViewControllerAnimated(true, completion: { () -> Void in
self.performSegueWithIdentifier("segue", sender: nil)
})
})
alertController.addAction(submitAction)
alertController.addAction(cancelAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
The issue at hand is the following: I show a UIAlertController. If the user presses delete, the view should go back to the first page, which is of the class class TableViewQuery: PFQueryTableViewController.
import UIKit
class DetailViewController: UIViewController {
var currentObject : PFObject?
#IBAction func pressDelete(sender: AnyObject) {
let deleteUser = UIAlertController(title: "Are you sure?", message: "Are you sure you want to delete the current user?", preferredStyle: UIAlertControllerStyle.Alert)
deleteUser.addAction(UIAlertAction(title: "Yes", style: .Default, handler: { (action: UIAlertAction) -> Void in
// Unwrap current object
if let object = self.currentObject {
object["firstName"] = self.firstName.text
object["lastName"] = self.lastName.text
object.deleteEventually()
}
}))
deleteUser.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: { (action: UIAlertAction) -> Void in
//Do nothing
}))
presentViewController(deleteUser, animated: true, completion: nil)
}
I have tried self.dismissViewControllerAnimated(true, completion: nil), but that just closes the UIAlertController.
I also tried the unwind segue, but then it does that instantly when I press the button, instead of showing the UIAlertController.
I also tried the segueForUnwindingToViewController(<toViewController: UIViewController:UIViewController>, fromViewController: <#T##UIViewController#>, identifier: <#T##String?#>) BUT my toViewController isn't a UIViewController, but a PFQueryTableViewController.
Any ideas on what else I can try?
You should put code into handler of UIAlertAction to respond when user click YES. I guess your are using master detail view controllers. If so, you need to find the right navigation controller to pop. Else, If you are using navigation controller, just pop as the code I commented.
#IBAction func pressDelete(sender: AnyObject) {
let deleteUser = UIAlertController(title: "Are you sure?", message: "Are you sure you want to delete the current user?", preferredStyle: UIAlertControllerStyle.Alert)
deleteUser.addAction(UIAlertAction(title: "Yes", style: .Default, handler: { (action: UIAlertAction) -> Void in
// If you are using master detail view controllers
if let navController = self.splitViewController?.viewControllers[0] as? UINavigationController {
navController.popViewControllerAnimated(true)
}
// If you using navigation controller
// self.navigationController?.popViewControllerAnimated(true)
}))
deleteUser.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: { (action: UIAlertAction) -> Void in
//Do nothing
}))
presentViewController(deleteUser, animated: true, completion: nil)
}
You should use the completion handler for presentViewController. You may need to keep track of any action the user took with the alert view controller. You will have to keep track of your user's choices with the alert view controller actions. Then in the completion handler check the user's choice and then make your navigation accordingly.
var didTapDeleteButton = false
#IBAction func pressDelete(sender: AnyObject) {
let deleteUser = UIAlertController(title: "Are you sure?", message: "Are you sure you want to delete the current user?", preferredStyle: UIAlertControllerStyle.Alert)
deleteUser.addAction(UIAlertAction(title: "Yes", style: .Default, handler: { (action: UIAlertAction) -> Void in
// Unwrap current object
if let object = self.currentObject {
object["firstName"] = self.firstName.text
object["lastName"] = self.lastName.text
object.deleteEventually()
didTapDeleteButton = true
}
}))
deleteUser.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: { (action: UIAlertAction) -> Void in
didTapDeleteButton = false
}))
presentViewController(viewController, animated: true, completion: {
// Make navigation choice here
if didTapDeleteButton == true {
// Navigation option 1
} else {
// Navigation option 2
}
})