How to make the cancel button in alert cancel the action? - swift

I made an alert as you can see, but the app adds the item, no matter if I'm clicking the "NO" button og the "Yes, I'm sure" button when the alert pops up.
My goal is to make the "NO" action, cancel the action so the input won't be added anyway. Can you tell me how to?
import UIKit
class SecondViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var input: UITextField!
#IBAction func addItem(_ sender: Any)
{
createAlert(title: "That's a good grail!", message: "Are you sure you want to add this grail?")
if (input.text != "")
{
list.append(input.text!)
input.text = ""
}
}
override func viewDidLoad()
{
super.viewDidLoad()
self.input.delegate = self
}
//HIDE KEYBOARD:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
//PRESSES RETURN KEY:
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
input.resignFirstResponder()
return true
}
func createAlert (title:String, message:String)
{
let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
//CREATING OK BUTTON
let OKAction = UIAlertAction(title: "Yes, I'm sure!", style: .default) { (action:UIAlertAction!) in
// Code in this block will trigger when OK button tapped.
print("Ok button tapped");
}
alertController.addAction(OKAction)
// Create Cancel button
let cancelAction = UIAlertAction(title: "No!", style: .cancel) { (action:UIAlertAction!) in
print("Cancel button tapped");
}
alertController.addAction(cancelAction)
// Present Dialog message
self.present(alertController, animated: true, completion:nil)
}
}
EDIT:
The code looks like this now, thanks:
import UIKit
class SecondViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var input: UITextField!
#IBAction func addItem(_ sender: Any)
{
createAlert(title: "That's a good grail!", message: "Are you sure you want to add this grail?")
}
override func viewDidLoad()
{
super.viewDidLoad()
self.input.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//HIDE KEYBOARD:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
//PRESSES RETURN KEY:
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
input.resignFirstResponder()
return true
}
func createAlert (title:String, message:String)
{
let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
//CREATING OK BUTTON
let OKAction = UIAlertAction(title: "Yes, I'm sure!", style: .default) { (action:UIAlertAction!) in
// Code in this block will trigger when OK button tapped.
print("Ok button tapped");
if (self.self.input.text != "")
{
list.append(self.input.text!)
self.input.text = ""
}
}
alertController.addAction(OKAction)
// Create Cancel button
let cancelAction = UIAlertAction(title: "No!", style: .cancel) { (action:UIAlertAction!) in
print("Cancel button tapped");
}
alertController.addAction(cancelAction)
// Present Dialog message
self.present(alertController, animated: true, completion:nil)
}
}

simply just put the code for adding the item to the OK closure:
class SecondViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var input: UITextField!
#IBAction func addItem(_ sender: Any)
{
createAlert(title: "That's a good grail!", message: "Are you sure you want to add this grail?")
}
override func viewDidLoad()
{
super.viewDidLoad()
self.input.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//HIDE KEYBOARD:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
//PRESSES RETURN KEY:
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
input.resignFirstResponder()
return true
}
func createAlert (title:String, message:String)
{
let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
//CREATING OK BUTTON
let OKAction = UIAlertAction(title: "Yes, I'm sure!", style: .default) { (action:UIAlertAction!) in
// Code in this block will trigger when OK button tapped.
if (input.text != "")
{
list.append(input.text!)
input.text = ""
}
print("Ok button tapped");
}
alertController.addAction(OKAction)
// Create Cancel button
let cancelAction = UIAlertAction(title: "No!", style: .cancel) { (action:UIAlertAction!) in
print("Cancel button tapped");
}
alertController.addAction(cancelAction)
// Present Dialog message
self.present(alertController, animated: true, completion:nil)
}
}

You're adding the input.text anyway in your addItem(_ :) method after you show your UIAlertController.
So if you want to avoid the input.text is added always and only when the OK button is tapped you should include in the closure for the action when you created it and remove it after the presentation of the UIAlertController
let OKAction = UIAlertAction(title: "Yes, I'm sure!", style: .default) { [weak self] _ in
guard let selfStrong = self else {
return
}
// Code in this block will trigger when OK button tapped.
if (selfStrong.input.text != "") {
selfStrong.list.append(input.text!)
selfStrong.input.text = ""
}
}
For the cancel button, you don't need any action closure unless you want to do something when the cancel button is tapped too.
I hope this helps you.

You are not appending the item on click of "Yes, I'm sure" button. Remove the below code inside #IBAction func addItem(_ sender: Any) method and put it inside OKAction handler block.
if (input.text != "")
{
list.append(input.text!)
input.text = ""
}
Do like this:
#IBAction func addItem(_ sender: Any)
{
createAlert(title: "That's a good grail!", message: "Are you sure you want to add this grail?")
}
Inside method: func createAlert (title:String, message:String) (put append code here)
let OKAction = UIAlertAction(title: "Yes, I'm sure!", style: .default) { (action:UIAlertAction!) in
// Code in this block will trigger when OK button tapped.
print("Ok button tapped");
if (input.text != "")
{
list.append(input.text!)
input.text = ""
}
}

Related

Trying to create a drop-down menu that displays options when the button is pressed

I am trying to create a drop down menu, that when you press the "Select" button two options drop down. I want these options to be a Timesheet button and Expense report button. I can't get it to work, here is my code.
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var SelectBTN: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func handleSelection(_ sender: UIButton) {
SelectBTN.ForEach { (button) in
UIView.animate(withDuration: 0.3, animations: {
button.isHidden = !button.isHidden
self.view.layoutIfNeeded()
})
}
}
#IBAction func cityTapped(_ sender: UIButton) {
}
#IBAction func ERBTNpressed(_ sender: UIButton) {
self.performSegue(withIdentifier: "ERSegue", sender: self)
}
#IBAction func TSBTNpressed(_ sender: UIButton) {
self.performSegue(withIdentifier: "TSSegue", sender: self)
}
}
You could set a menu as a primary action for a button in viewDidLoad:
SelectBTN.showsMenuAsPrimaryAction = true
SelectBTN.menu = buttonMenu()
In the menu you could add actions:
func buttonMenu() -> UIMenu {
let ERAction = UIAction(title: "Expense report", image: nil) { (_) in
self.performSegue(withIdentifier: "ERSegue", sender: self)
}
let TSAction = UIAction(title: "Timesheet", image: nil) { (_) in
self.performSegue(withIdentifier: "TSSegue", sender: self)
}
return UIMenu(title: "", options: .displayInline, children: [ERAction, TSAction])
}
You could also add images to actions and specify attributes (i.e. .destructive for Delete action)

How do I observe user editing a textField in an UIAlertController?

I have an UIAlertController with a textField. Since I can't create an outlet for the textField I have to find another way to get ahold of a function that observes user input in realtime.
I have made some attempts, but not successfully. This thread explain how to add a target to the textField:
How do I check when a UITextField changes?
textfield.addTarget(self, action: #selector(ViewControllerr.textFieldDidChange(_:)), for: UIControl.Event.editingChanged)
I tried this, but don't quite understand the #selector input. Does it want my UIViewController? or my UIAlertController I tried both but got the same error message:
Value of type 'UIViewController' has no member 'textFieldDidChange'
Same with UIAlertController.
I also tried setting my textField to delegate to access functions of that class:
textField.delegate = self as? UITextFieldDelegate
But I guess this isn't the correct approach as I still couldn't access any functions except by doing:
textField.delegate?.textFieldDidBeginEditing?(textField)
But that didn't give me anything either.
I also tried adding an extension like this:
extension UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
print("🦋EDITING")
}// became first responder
func textFieldDidEndEditing(_ textField: UITextField) {
print("🦋EDITING")
}
}
But for some reason I can't access them through textView
If I extend the class like this:
extension PickNumberViewController: UITextFieldDelegate {...}
I get the same error message
Value of type 'UITextField' has no member 'textFieldDel...'
which makes no sense since I set textField to UITextFieldDelegate not UITextField.
Let me know if you would like to see my code as well.
You can try to get a reference to it with self.textF = alert.textFields?[0]
class ViewController: UIViewController {
var textF:UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
//1. Create the alert controller.
let alert = UIAlertController(title: "Some Title", message: "Enter a text", preferredStyle: .alert)
alert.addTextField { (textField) in
textField.text = "Some default text"
}
let tex = alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { [weak alert] (_) in
}))
// 4. Present the alert.
self.present(alert, animated: true, completion: nil)
self.textF = alert.textFields?[0]
self.textF.addTarget(self, action: #selector(self.textFieldDidChange), for: UIControl.Event.editingChanged)
}
}
#objc func textFieldDidChange() {
print(textF.text)
}
}
You don't need to hold a reference to the text field. You can add target or set delegate within the addTextField closure like this.
class ViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let alert = UIAlertController(title: "Title", message: "Message", preferredStyle: .alert)
alert.addTextField { textField in
textField.delegate = self
textField.addTarget(self, action: #selector(self.textChanged(_:)), for: .editingChanged)
textField.placeholder = "Enter name"
}
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
present(alert, animated: true, completion: nil)
}
#objc func textChanged(_ sender: UITextField) {
print("Text changed - ", sender.text!)
}
}
extension ViewController: UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
print("EDITING")
}
func textFieldDidEndEditing(_ textField: UITextField) {
print("EDITING")
}
}
iOS13+ One option is to use UIAction
extension UIAlertController {
public func addTextField(_ configuration: #escaping ((UITextField) -> Void),
onEditingDidBegin: ((UITextField) -> Void)? = nil,
onEditingChanged: ((UITextField) -> Void)? = nil,
onEditingDidEnd: ((UITextField) -> Void)? = nil) {
self.addTextField { textField in
configuration(textField)
if let onEditingDidBegin {
textField.addAction(UIAction { action in
onEditingDidBegin(action.sender as! UITextField)
}, for: .editingDidBegin)
}
if let onEditingChanged {
textField.addAction( UIAction { action in
onEditingChanged(action.sender as! UITextField)
}, for: .editingChanged)
}
if let onEditingDidEnd {
textField.addAction( UIAction { action in
onEditingDidEnd(action.sender as! UITextField)
}, for: .editingDidEnd)
}
}
}
Use like so:
let alert = UIAlertController(title: "Enter Text", message: nil, preferredStyle: .alert)
alert.addTextField { textField in
textField.text = "Some text"
textField.placeholder = "Placeholder"
} onEditingDidBegin: { textField in
textField.selectAll(nil) // selects all text
} onEditingChanged: { textField in
if let text = textField.text {
textField.textColor = text.count > 5 ? .red : .label
}
} onEditingDidEnd: { textField in
if let text = textField.text {
if text.count > 5 {
textField.text = "This is too long"
}
}
}
You shouldn't use onEditingDidEnd for your final validation though. Put that in the UIAlertAction for your "OK" button and pull out the textFields:
let okAction = UIAlertAction(title: "OK", style: .destructive) { [weak self, weak alert] _ in
if let self,
let alert,
let textField = alert.textFields?.first {
// validate (here, 'self', is a view controller)
self.label.text = textField.text
}
}
alert.addAction(okAction)

Save Login Swift

How can you do so when closing the application do not ask me again to enter email and password? I read about userdefaults and keychainwrapper, but I can not apply any. I do test after test and they ask me again to enter the credentials.
This is the login code:
class LogInViewController: UIViewController, UITextFieldDelegate
{
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var passwordField: UITextField!
#IBAction func Cancel(_ sender: UIBarButtonItem) {
dismiss(animated: true, completion: nil)
}
#IBAction func LogIn(_ sender: Any)
{
if self.emailField.text == "" ||
self.passwordField.text == ""
{
let alertController = UIAlertController(title: "Error", message: "Por favor introduce email y contraseña", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertController.addAction(defaultAction)
self.present(alertController, animated: true, completion: nil)
} else
{
Auth.auth().signIn(withEmail: self.emailField.text!, password: self.passwordField.text!) { (user, error) in
if error == nil
{
let vc = self.storyboard?.instantiateViewController(withIdentifier: "HandleViewSegue")
self.present(vc!, animated: true, completion: nil)
} else
{
let alertController = UIAlertController(title: "Error", message: error?.localizedDescription, preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertController.addAction(defaultAction)
self.present(alertController, animated: true, completion: nil)
}
}
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool
{
emailField.resignFirstResponder()
passwordField.resignFirstResponder()
return (true)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
self.view.endEditing(true)
self.emailField.delegate = self
self.passwordField.delegate = self
}
}

swift custom UIAlertViewDelegate not work

I am an android developer.
I want to use custom UIAlertViewDelegate in my code.
demo like
import UIKit
class ViewController: UIViewController,UIAlertViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int){
println(buttonIndex)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func onClick1(sender: UIButton) {
// define here not work
let delegate = MyUIAlertViewDelegate()
let alert = UIAlertView(title: "title", message: "message", delegate: delegate, cancelButtonTitle: "cancel", otherButtonTitles: "other","hello")
alert.alertViewStyle = UIAlertViewStyle.Default
alert.show()
}
}
// define here works
// let delegate = MyUIAlertViewDelegate()
class MyUIAlertViewDelegate : UIViewController, UIAlertViewDelegate {
func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int){
println("click" + String(buttonIndex))
}
}
but it not work.
why?
I noticed when you are creating a UIAlertView called "alert" you set message to message, for demo purposes this wont work unless message is a variable with a string value.
Also you are calling alert.show() in nothing! it needs to be called by something or somewhere, so in the example I have done below it is in viewDidAppear.
Please copy and paste this code if you need to it works perfectly, and please don't hesitate to ask any questions.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidAppear(animated: Bool) {
alert.show()
}
}
class MyUIAlertViewDelegate : NSObject, UIAlertViewDelegate {
func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int) {
print("click" + String(buttonIndex))
}
}
let delegate = MyUIAlertViewDelegate()
let alert = UIAlertView(title: "warning", message: "This is a message", delegate: delegate, cancelButtonTitle: "Cancel")
Try like this
Subclass as viewcontroller ,instead of nsobject
class ViewController: UIViewController ,UIAlertViewDelegate{
}
Add a button in storyboard or create yourself.Then
in button action add the given code.
var alert = UIAlertView(title: " Your Title", message: "your message", delegate: self, cancelButtonTitle: "Ok", otherButtonTitles: "Cancel")
alert.alertViewStyle = UIAlertViewStyle.Default
alert.show()
Then
func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int) {
println("click",buttonIndex)
if buttonIndex == 0 {
println("ok")
}else {
println("Cancel")
}
}
}
You can customize as you need
Hope it helps
I've been using the following many times actually, you can try this as well.
func displayAlert(title: String, message: String) {
var alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction((UIAlertAction(title: "OK", style: .Default, handler: { (action) -> Void in
self.dismissViewControllerAnimated(true, completion: nil)
})))
self.presentViewController(alert, animated: true, completion: nil)
}

Swift UIButton background image not being set by UIImagePicker

I am able to select an Image from the PhotoLibrary but the background image of my UIButton does not change. The println in didFinishPickingImage and imagePickerControllerDidCancel does not show in the console so I do not think those functions are being called.
class AddTeamTableViewController: UITableViewController, UITextFieldDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate, UIAlertViewDelegate {
var picker:UIImagePickerController? = UIImagePickerController()
#IBOutlet var teamNumber: UITextField!
#IBOutlet var schoolName: UITextField!
#IBOutlet var teamImage: UIButton!
#IBAction func cancelButton(sender: AnyObject) {
dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func addTeam(sender: AnyObject) {
var newTeam = Team()
var onlineTeam = PFObject(className: "Team")
// add new team to the list
newTeam.name = teamNumber.text
newTeam.schoolName = schoolName.text
teamList.append(newTeam)
// add online
onlineTeam["name"] = newTeam.name
onlineTeam["fromUser"] = PFUser.currentUser()
onlineTeam["schoolName"] = newTeam.schoolName
onlineTeam.save()
//close the view
dismissViewControllerAnimated(true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return 1
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
self.view.endEditing(true)
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true;
}
#IBAction func pickImage(sender: AnyObject) {
var image = UIImagePickerController()
image.delegate = self
var alert:UIAlertController = UIAlertController(title: "Choose Image", message: nil, preferredStyle: UIAlertControllerStyle.ActionSheet)
var cameraAction = UIAlertAction(title: "Camera", style: UIAlertActionStyle.Default) {
UIAlertAction in
self.openCamera()
}
var galleryAction = UIAlertAction(title: "Gallery", style: UIAlertActionStyle.Default) {
UIAlertAction in
self.openGallery()
}
var cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel) {
UIAlertAction in
}
alert.addAction(cameraAction)
alert.addAction(galleryAction)
alert.addAction(cancelAction)
self.presentViewController(alert, animated: true, completion: nil)
/*
alert.delegate = self
alert.message = "Choose Image Source"
alert.addButtonWithTitle("Camera")
alert.addButtonWithTitle("Photo Library")
alert.show()
*/
//image.sourceType = UIImagePickerControllerSourceType.Camera
//image.allowsEditing = false
//self.presentViewController(image, animated: true, completion: nil)
}
func openCamera() {
if(UIImagePickerController .isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera)) {
picker!.sourceType = UIImagePickerControllerSourceType.Camera
self.presentViewController(picker!, animated: true, completion: nil)
} else {
openGallery()
}
}
func openGallery() {
picker!.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
self.presentViewController(picker!, animated: true, completion: nil)
}
func imagePickerController(picker: UIImagePickerController!, didFinishPickingImage image: UIImage!, editingInfo: [NSObject : AnyObject]!) {
println("Image selected")
self.dismissViewControllerAnimated(true, completion: nil)
teamImage.setBackgroundImage(image, forState: UIControlState.Normal)
}
func imagePickerControllerDidCancel(picker: UIImagePickerController!) {
println("picker cancel")
}
}
The button doesn't seem initialized
let teamImage: UIButton = UIButton(frame: CGRect(x: 0, y: 0, width: 380, height: 300))
let imageTest = UIImage(named: "Hypnotoad")
teamImage.setTitle("HypnotoadTitle", forState: .Normal)
teamImage.setBackgroundImage(imageTest, forState: .Normal)
tested this in a Playground and works ok.
I figured it out. I had 2 different variables of type UIImagePickerController and I was saving to the wrong one. I deleted the first var picker declaration and combined the code to just one UIImagePickerController and it works as intended.