swift custom UIAlertViewDelegate not work - swift

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)
}

Related

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)

Can't dismiss mail view controller

This code works and presents email form all filled out and sends the email just fine, just can't get it to dismiss after sending or trying to cancel.
I am sure I am missing something simple.
I also realize I have nothing in place if there is an error but at this point it works and sends email but i cant get it to dismiss
tried several suggestions I found online and there are other ways to do this but I am trying to understand why this is not working so want this way (or close) to work.... xcode 10.2.1
import UIKit
import MessageUI
class ViewController: UIViewController, MFMailComposeViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func sendEmail(_ sender: Any) {
sendEmail()
}
func sendEmail() {
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients(["xxx#xx.com"])
mail.setSubject("test ")
mail.setPreferredSendingEmailAddress("xxx#jxxx.com")
present(mail, animated: true)
} else {
//show failure alert
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true, completion: nil)
}
}
compiles and sends email fine just cant dismiss the controller
All delegate methods should be in class scope , in your current code didFinishWith which should dimiss the mail is nested inside sendEmail
import UIKit
import MessageUI
class ViewController: UIViewController, MFMailComposeViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func sendEmail(_ sender: Any) {
sendEmail()
}
func sendEmail() {
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients(["xxx#xx.com"])
mail.setSubject("test ")
mail.setPreferredSendingEmailAddress("xxx#jxxx.com")
present(mail, animated: true)
} else {
//show failure alert
}
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true, completion: nil)
}
}

MFMessageComposer cancel button not visible in iOS 11

The cancel button on right top corner after a message composer has been presented is not visible in iOS 11 devices. As shown in the screenshot, cancel button works but is not visible. Once we press on it, the screen dismisses.
I have tried like this:
class ViewController: UIViewController, MFMessageComposeViewControllerDelegate {
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 Messages(_ sender: UIButton) {
if MFMessageComposeViewController.canSendText() == true {
let recipients:[String] = ["1500"]
let messageController = MFMessageComposeViewController()
messageController.messageComposeDelegate = self
messageController.recipients = recipients
messageController.body = "Your_text"
self.present(messageController, animated: true, completion: nil)
} else {
//handle text messaging not available
}
}
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
controller.dismiss(animated: true, completion: nil)
}
}
Added Messages and MessageUI frameworks and imported in view controller.
Screenshot:
Image:

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

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 = ""
}
}

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.