I have a UIView which allow user to save some data with their desirable name before it can be view in the tableView
func saveFilesName() {
let alert = UIAlertController(title: "Save As", message: "Please enter the file name", preferredStyle: .alert)
alert.addTextField(configurationHandler: {(nameField) -> Void in
nameField.placeholder = "Enter the file name"
nameField.textAlignment = .center
})
alert.addTextField(configurationHandler: {(descField) -> Void in
descField.placeholder = "Enter the your description"
descField.textAlignment = .center
})
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (actions: UIAlertAction) -> Void in
}))
alert.addAction(UIAlertAction(title: "Save", style: .default, handler: { (actions: UIAlertAction) -> Void in
let nameFieldData = alert.textFields![0]
let descFieldData = alert.textFields![1]
self.fileName = nameFieldData.text!
self.descData = descFieldData.text!
print(alert.textFields![0])
let saveCSV = self.saveCSVFiles()
print(saveCSV.lastPathComponent)
}))
self.present(alert, animated: true, completion: nil)
}
the problems is when I ran the code above, it will come with this error: "while an existing transition or presentation is occurring; the navigation stack will not be updated." right before the alert will normally pop up then it crash and the alert doesn't work.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
guard let button = sender as? UIBarButtonItem, button === saveButton else {
os_log("The save button was not pressed, cancelling", log: OSLog.default, type: .debug)
return
}
saveFilesName()
if fileName != nil {
let nsurl = String(describing: csvFile)
let name = fileName
let description = descData
let photo = UIImage(contentsOfFile: "defaultImage")
let url = NSURL(fileURLWithPath: nsurl)
csv = CSVData(name: name!, description: description!, photo: photo, url: url)
}
}
since the alert is needed before I can append any data to the class model. Any help would be appreciated
You don't need another IBAction, you can benefit from the user tapping on the Save button. Right?
inside your Save's handler ie here:
alert.addAction(UIAlertAction(title: "Save", style: .default, handler: { (actions: UIAlertAction) -> Void in
let nameFieldData = alert.textFields![0]
let descFieldData = alert.textFields![1]
self.fileName = nameFieldData.text!
self.descData = descFieldData.text!
print(alert.textFields![0])
let saveCSV = self.saveCSVFiles()
print(saveCSV.lastPathComponent)
}))
add the performSegue(withIdentifier: "YOUR_SEGUE_IDENTIFIER", sender: self) right after print(saveCSV.lastPathComponent)
so once you click save, it will dismiss the alert and then perform that segue.
This would trigger your prepare(for segue...)
Related
Here is my code. When I press "Download" button my "someVariable" global variable doesn't change. I tried to debug it in the debugger but couldn't find the issue.
#IBAction func addButtonPressed(_ sender: UIBarButtonItem) {
let textField = UITextField()
let alert = UIAlertController(title: "Download URL", message: "", preferredStyle: .alert)
alert.addTextField { (actionTextField) in
actionTextField.placeholder = "Paste link here"
}
let action = UIAlertAction(title: "Download", style: .default) { (action) in
self.someVariable = textField.text!
}
let secondAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alert.addAction(action)
alert.addAction(secondAction)
present(alert, animated: true, completion: nil)
}
Welcome to Stackoverflow. Your textField in line 2 is what you're getting your someVariable's new value from. I'm supposing you want to get the value from your alert's textField instead.
You can get reference from your alert's textField through your alert's textFields property, like so:
let action = UIAlertAction(title: "Download", style: .default) { (action) in
guard let textField = alert.textFields?.first else { return }
self.someVariable = textField.text!
}
I am having trouble passing back data that is scanned by my custom barcode scanner.
The data is read successfully and I am able to assign the value to a variable. But I cannot pass the data back to the previous view controller to populate a text view.
I am using this below to pass to my barcode VC to store the data inside it
var barcodeScanData: String = ""
I am using prepare for segue below to
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "BarcodeScanVC" {
let desitnationVC = segue.destination as! BarcodeScanVC
desitnationVC.xyz = barcodeScanData
}
}
Below here is where I am attempting to send back the data from my custom barcode scanner
var xyz: String = ""
func launchApp(barcodeScan: String) {
if presentedViewController != nil {
return
}
let alertPrompt = UIAlertController(title: "Barcode Found", message: "\(barcodeScan)", preferredStyle: .actionSheet)
let confirmAction = UIAlertAction(title: "Confirm", style: UIAlertAction.Style.default, handler: { (action) -> Void in
let barcodeData = PartsVCDetail()
self.xyz = barcodeScan
barcodeData.barcodeScanData = self.xyz
print(self.xyz, "This is what I am sending")
print(barcodeData.barcodeScanData, "This is what I am sending it TO" )
self.navigationController?.popViewController(animated: true)
})
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertAction.Style.cancel, handler: nil)
alertPrompt.addAction(confirmAction)
alertPrompt.addAction(cancelAction)
present(alertPrompt, animated: true, completion: nil)
}
The two print lines
print(self.waybill, "This is what I am sending")
print(barcodeData.barcodeScanData, "This is what I am sending it TO"
Show me the correct scan data, however, when I use the last line below:
self.navigationController?.popViewController(animated: true)
The data is lost and I see an empty value in my viewDidAppear on the first view controller:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
print(barcodeScanData, "This is empty but it shouldnt be")
dataFromBarcodeScanner.text = barcodeScanData
}
What am I missing ?
With this code let barcodeData = PartsVCDetail() you are creating a new instance of PartsVCDetail and then setting the property of that instance. As soon as the action ends this instance will be deallocated and you will return to the previous view controller via popViewController.
A common solution to your requirement is a delegation pattern.
You declare a protocol for your delegate to implement
You have the original view controller implement this delegate protocol
You have the original view controller set itself as the second view controller's delegate
The second view controller can invoke the delegate method to pass the data back
Protocol
protocol BarcodeScanDelegate {
func didScan(barcodeData: String)
}
PartsVCDetail
class PartsVCDetail: UIViewController, BarcodeScanDelegate {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let desitnationVC = segue.destination as? BarcodeScanVC {
desitnationVC.delegate = self
}
}
func didScan(barcodeData: String) {
self.barcodeScanData = barcodeData
}
}
BarcodeScanVC
var delegate: BarcodeScanDelegate?
func launchApp(barcodeScan: String) {
guard presentedViewController == nil else {
return
}
let alertPrompt = UIAlertController(title: "Barcode Found", message: "\(barcodeScan)", preferredStyle: .actionSheet)
let confirmAction = UIAlertAction(title: "Confirm", style: UIAlertAction.Style.default, handler: { (action) -> Void in
self.delegate?.didScan(barcodeData: self.xyz)
self.navigationController?.popViewController(animated: true)
})
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertAction.Style.cancel, handler: nil)
alertPrompt.addAction(confirmAction)
alertPrompt.addAction(cancelAction)
present(alertPrompt, animated: true, completion: nil)
}
I've been trying to passing some data to model file to be used in another view controller by using pop-up alert after user tab the save button it suppose to trigger prepare function and unwind to tableview on another view controller.
#IBAction func saveButton(_ sender: UIBarButtonItem) {
saveFilesName()
}
Then,
func saveFilesName() {
let alert = UIAlertController(title: "Save As", message: "Please enter the file name", preferredStyle: .alert)
alert.addTextField(configurationHandler: {(nameField) -> Void in
nameField.placeholder = "Enter the file name"
nameField.textAlignment = .center
})
alert.addTextField(configurationHandler: {(descField) -> Void in
descField.placeholder = "Enter the your description"
descField.textAlignment = .center
})
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (actions: UIAlertAction) -> Void in
}))
alert.addAction(UIAlertAction(title: "Save", style: .default, handler: { (actions: UIAlertAction) -> Void in
let nameFieldData = alert.textFields![0]
let descFieldData = alert.textFields![1]
self.fileName = nameFieldData.text ?? ""
self.descData = descFieldData.text ?? ""
print(alert.textFields![0])
let saveCSV = self.saveCSVFiles()
print(saveCSV.lastPathComponent)
self.performSegue(withIdentifier: "unwindToCSVList", sender: self)
}))
self.present(alert, animated: true, completion: nil)
}
but after I tab the save button it unwind to the table view as intended but the prepare method always return The save button was not pressed, cancelling which mean the prepare method didn't being triggered properly
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if segue.identifier == "unwindToCSVList" {
guard let button = sender as? UIBarButtonItem, button === saveButton else {
os_log("The save button was not pressed, cancelling", log: OSLog.default, type: .debug)
return
}
let nsurl = String(describing: csvFile)
let name = fileName ?? ""
let description = descData ?? ""
let photo = UIImage(contentsOfFile: "defaultImage")
let url = NSURL(fileURLWithPath: nsurl)
print("yeah")
csv = CSVData(name: name, description: description, photo: photo, url: url)
}
}
any suggestion would be appreciated
I have a UIAlertView that is triggered via a UIButton.
The UIAlertView displays two buttons, "Open Email" and "Cancel".
"Cancel" removes the UIAlert from the view. I'm trying to make it so when the user taps "Open Email", their device opens the default email application's compose screen, with an email address already in the "sender" section.
Using Swift 3.
Thanks!
import UIKit
import Kingfisher
class SettingsViewController: UIViewController {
#IBAction func copyrightInfo(_ sender: Any) {
let alert = UIAlertController(title: "Copyright Info", message: "text", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "I understand", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
#IBAction func helpfeedbackAlert(_ sender: Any) {
let alertController = UIAlertController(title: "Help & Feedback", message: "text", preferredStyle: .alert)
let cancel = UIAlertAction(title: "Cancel", style: .destructive, handler: nil)
let openEmail = UIAlertAction(title: "Open Email", style: .default, handler: nil)
alertController.addAction(openEmail)
alertController.addAction(cancel)
self.present(alertController, animated: true, completion: nil)
}
#IBAction func clearCache(_ sender: Any) {
// SDImageCache.shared().clearMemory()
// SDImageCache.shared().clearDisk()
// Clear memory cache right away.
ImageCache.default.clearMemoryCache()
// Clear disk cache. This is an async operation.
ImageCache.default.clearDiskCache()
}
#IBAction func rateApp(_ sender: Any) {
if let url = URL(string: "https://www.google.com") {
if #available(iOS 10.0, *) {
UIApplication.shared.open(url, options: [:]) {
boolean in
// do something with the boolean
}
} else {
// Fallback on earlier versions
}
}
}
#IBAction func purchasePhotos(_ sender: Any) {
if let url = URL(string: "https://google.com") {
if #available(iOS 10.0, *) {
UIApplication.shared.open(url, options: [:]) {
boolean in
// do something with the boolean
}
} else {
// Fallback on earlier versions
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override var prefersStatusBarHidden: Bool {
get {
return true
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
You have to use MFMailComposer to send email, this is how you use it
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients(["rajatpagare#hotmail.com"])
mail.setMessageBody("you email body", isHTML: false)
present(mail, animated: true)
} else {
// email is not added in app
}
Also import MessageUI framework and conform to MFMailComposeViewControllerDelegate
Also you don't need to use openURL like you mentioned in your answer as you are redirecting user from your app to another app, there is no need to do that you can use MFMailComposer.
First, I assume what do you need to add in your code snippet is only this part:
#IBAction func helpfeedbackAlert(_ sender: Any) {
let alertController = UIAlertController(title: "Help & Feedback", message: "text", preferredStyle: .alert)
let cancel = UIAlertAction(title: "Cancel", style: .destructive, handler: nil)
let openEmail = UIAlertAction(title: "Open Email", style: .default, handler: nil)
alertController.addAction(openEmail)
alertController.addAction(cancel)
self.present(alertController, animated: true, completion: nil)
}
Anyway, what do you need is to fill the handler when you create an instance of the UIAlertAction. Referring to the documentation of the init(title:style:handler:):
handler
A block to execute when the user selects the action. This
block has no return value and takes the selected action object as its
only parameter.
So, your openEmail should be like:
let openEmail = UIAlertAction(title: "Open Email", style: .destructive, handler: { (actionSheetController) in
// send your email here...
})
I'm not pretty sure of the mechanism of how do you want to send an email, but I thik you might want to check MFMailComposeViewController, this question should help you to work with it.
Hope it helped.
I got it working with this code:
#IBAction func helpfeedbackAlert(_ sender: Any) {
let alertController = UIAlertController(title: "Help & Feedback", message: "text", preferredStyle: .alert)
let cancel = UIAlertAction(title: "Cancel", style: .destructive, handler: nil)
let openEmail = UIAlertAction(title: "Open Email", style: .default, handler: { (actionSheetController) -> Void in let email = "email#example.com"
let url = NSURL(string: "mailto:\("email#example.com")")
UIApplication.shared.openURL(url as! URL)})
alertController.addAction(openEmail)
alertController.addAction(cancel)
self.present(alertController, animated: true, completion: nil)
}
I have an AlertController with a text field and two button: CANCEL and SAVE. This is the code:
#IBAction func addTherapy(sender: AnyObject)
{
let addAlertView = UIAlertController(title: "New Prescription", message: "Insert a name for this prescription", preferredStyle: UIAlertControllerStyle.Alert)
addAlertView.addAction(UIAlertAction(title: "Cancel",
style: UIAlertActionStyle.Default,
handler: nil))
addAlertView.addAction(UIAlertAction(title: "Save",
style: UIAlertActionStyle.Default,
handler: nil))
addAlertView.addTextFieldWithConfigurationHandler({textField in textField.placeholder = "Title"})
self.presentViewController(addAlertView, animated: true, completion: nil)
}
What I want to do is implement a check on the textfield for disabling the SAVE button when the textfield is empty just like Pictures Application of iOS when you want create a NewAlbum. Please someone can explain me what to do?
There is a much simpler way without using notification center, in swift:
weak var actionToEnable : UIAlertAction?
func showAlert()
{
let titleStr = "title"
let messageStr = "message"
let alert = UIAlertController(title: titleStr, message: messageStr, preferredStyle: UIAlertControllerStyle.alert)
let placeholderStr = "placeholder"
alert.addTextField(configurationHandler: {(textField: UITextField) in
textField.placeholder = placeholderStr
textField.addTarget(self, action: #selector(self.textChanged(_:)), for: .editingChanged)
})
let cancel = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: { (_) -> Void in
})
let action = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: { (_) -> Void in
let textfield = alert.textFields!.first!
//Do what you want with the textfield!
})
alert.addAction(cancel)
alert.addAction(action)
self.actionToEnable = action
action.isEnabled = false
self.present(alert, animated: true, completion: nil)
}
func textChanged(_ sender:UITextField) {
self.actionToEnable?.isEnabled = (sender.text! == "Validation")
}
I would first create the alertcontroller with the save action initially disabled. Then when adding the textfield inculde a Notification to observe its change in the handler and in that selector just toggle the save actions enabled property.
Here is what I am saying:
//hold this reference in your class
weak var AddAlertSaveAction: UIAlertAction?
#IBAction func addTherapy(sender : AnyObject) {
//set up the alertcontroller
let title = NSLocalizedString("New Prescription", comment: "")
let message = NSLocalizedString("Insert a name for this prescription.", comment: "")
let cancelButtonTitle = NSLocalizedString("Cancel", comment: "")
let otherButtonTitle = NSLocalizedString("Save", comment: "")
let alertController = UIAlertController(title: title, message: message, preferredStyle: .Alert)
// Add the text field with handler
alertController.addTextFieldWithConfigurationHandler { textField in
//listen for changes
NSNotificationCenter.defaultCenter().addObserver(self, selector: "handleTextFieldTextDidChangeNotification:", name: UITextFieldTextDidChangeNotification, object: textField)
}
func removeTextFieldObserver() {
NSNotificationCenter.defaultCenter().removeObserver(self, name: UITextFieldTextDidChangeNotification, object: alertController.textFields[0])
}
// Create the actions.
let cancelAction = UIAlertAction(title: cancelButtonTitle, style: .Cancel) { action in
NSLog("Cancel Button Pressed")
removeTextFieldObserver()
}
let otherAction = UIAlertAction(title: otherButtonTitle, style: .Default) { action in
NSLog("Save Button Pressed")
removeTextFieldObserver()
}
// disable the 'save' button (otherAction) initially
otherAction.enabled = false
// save the other action to toggle the enabled/disabled state when the text changed.
AddAlertSaveAction = otherAction
// Add the actions.
alertController.addAction(cancelAction)
alertController.addAction(otherAction)
presentViewController(alertController, animated: true, completion: nil)
}
//handler
func handleTextFieldTextDidChangeNotification(notification: NSNotification) {
let textField = notification.object as UITextField
// Enforce a minimum length of >= 1 for secure text alerts.
AddAlertSaveAction!.enabled = textField.text.utf16count >= 1
}
I am doing this in another project - I got this pattern directly from apple examples. They have a very good example project outlining a few of these patterns in the UICatalog examples: https://developer.apple.com/library/content/samplecode/UICatalog/Introduction/Intro.html
Swift 3.0 Updated Solution given By #spoek
func showAlert()
{
let titleStr = "title"
let messageStr = "message"
let alert = UIAlertController(title: titleStr, message: messageStr, preferredStyle: UIAlertControllerStyle.alert)
let placeholderStr = "placeholder"
alert.addTextField(configurationHandler: {(textField: UITextField) in
textField.placeholder = placeholderStr
textField.addTarget(self, action: #selector(self.textChanged(_:)), for: .editingChanged)
})
let cancel = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: { (_) -> Void in
})
let action = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: { (_) -> Void in
let textfield = alert.textFields!.first!
//Do what you want with the textfield!
})
alert.addAction(cancel)
alert.addAction(action)
self.actionToEnable = action
action.isEnabled = false
self.present(alert, animated: true, completion: nil)
}
func textChanged(_ sender:UITextField) {
self.actionToEnable?.isEnabled = (sender.text! == "Validation")
}
I implemented a subclass of UIAlertController for conveniently adding text fields and associated enabling and disabling of buttons. The basic logic is similar to that Sourabh Sharma but everything is encapsulated in this subclass for tidiness. This should be helpful if your project involves a lot of such alert functionalities.
public class TextEnabledAlertController: UIAlertController {
private var textFieldActions = [UITextField: ((UITextField)->Void)]()
func addTextField(configurationHandler: ((UITextField) -> Void)? = nil, textChangeAction:((UITextField)->Void)?) {
super.addTextField(configurationHandler: { (textField) in
configurationHandler?(textField)
if let textChangeAction = textChangeAction {
self.textFieldActions[textField] = textChangeAction
textField.addTarget(self, action: #selector(self.textFieldChanged), for: .editingChanged)
}
})
}
#objc private func textFieldChanged(sender: UITextField) {
if let textChangeAction = textFieldActions[sender] {
textChangeAction(sender)
}
}
}
To use it, just provide a textChangeAction block when adding the text fields:
alert.addTextField(configurationHandler: { (textField) in
textField.placeholder = "Your name"
textField.autocapitalizationType = .words
}) { (textField) in
saveAction.isEnabled = (textField.text?.characters.count ?? 0) > 0
}
For the full example, see the git page.