segue and passing data from uialertcontroller - ios

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

Related

Prepare for segue function can't pass data to another VC Swift,

I'm trying to pass data from the alert text field to another VC's variable.
And here's my alert controller.
let alert = UIAlertController(title: "Download URL", message: "", preferredStyle: .alert)
let action = UIAlertAction(title: "Download", style: .default) { (action) in
guard let textField = alert.textFields?.first else {return}
self.ayb = textField.text ?? "Blank"
UserDefaults.standard.set(self.ayb, forKey: "urlString")
self.performSegue(withIdentifier: "segAdd", sender: self)
}
let secondAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alert.addTextField { (actionTextField) in
actionTextField.placeholder = "Paste link here"
}
alert.addAction(action)
alert.addAction(secondAction)
present(alert, animated: true, completion: nil)
}
And here's my prepare function for passing data.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segAdd" {
let destinationVC = tabBarController?.viewControllers?[0] as? BrowserWebViewController
destinationVC?.urlFromDownloads = self.ayb
print("The value of destinationVC?.urlFromDownloads is \(destinationVC?.urlFromDownloads)")
}
}
And in the console, it writes "The value of destinationVC?.urlFromDownloads is \Optional("Text I typed in textField")".
But in BrowserWebViewController my "urlFromDownloads" is = ""(which is default).
Note: Segue's name is true.
First of all rather than declaring an extra property or saving the value to UserDefaults you can pass the string in the sender parameter
let action = UIAlertAction(title: "Download", style: .default) { (action) in
guard let textField = alert.textFields?.first else {return}
self.performSegue(withIdentifier: "segAdd", sender: textField.text!)
}
Your way to determine the destination view controller is wrong. Ask the segue for the destination. And you can force downcast the type. The code must not crash if the segue is designed correctly.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segAdd" {
let destinationVC = segue.destination as! BrowserWebViewController
destinationVC.urlFromDownloads = sender as! String
print("The value of destinationVC?.urlFromDownloads is \(destinationVC.urlFromDownloads)")
}
}

how to compare entered text against a string in terms of a password to trigger a new segue

I've got a application that is running and has segues that have questions on them and passes the data from one segue to another.
I need to add a password to my final button that triggers the the event of passing the data to the last view controller and showing the data to the person that holds the password.
I have the code that passes the code to the last page and I have the code that passes the alert to the user to enter the password.
my issue is if I have the thing for the segue before the alert the page loads and it doesn't matter if the pass word is entered or not. if the thing for the segue is after the alert nothing happens no matter what's entered in the text box.
#IBAction func DataReveal(_ sender: UIButton)
{
ID = ID2.text
date = date2.text
Answer1 = answ1.text
Answer2 = answ2.text
switch sender.tag
{
case 1: send = "Thanks"
default: print("No Selection")
}
let alertController = UIAlertController(title: "Password check", message: "Enter Password to see collected data", preferredStyle: .alert)
alertController.addTextField
{
textField in
textField.placeholder = "Enter Password"
textField.isSecureTextEntry = true
}
let confirmActionBtn = UIAlertAction(title: "OK", style: .default)
{
[weak alertController] _ in
guard let alertController = alertController, let textField = alertController.textFields?.first else
{
return
}
print("Password is \(String(describing: textField.text))")
//Just compare Entered String in textfield with your original password password
//if password is matched push or segue your required controller
}
alertController.addAction(confirmActionBtn)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
self.performSegue(withIdentifier: "Link6", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "Link6", let destination = segue.destination as? DataviewController
{
destination.Answer2 = self.Answer2
destination.Answer1 = self.Answer1
destination.ID = self.ID
destination.date = self.date
destination.send = self.send
}
}
I've made the changes as mentioned in one of the answers that was provided, I've tried to run the code but there is still another issue because its still not comparing the textfield to the password string variable and when the password is entered there isn't any perform segue movement.
#IBAction func DataReveal(_ sender: UIButton)
{
ID = ID2.text
date = date2.text
Answer1 = answ1.text
Answer2 = answ2.text
switch sender.tag
{
case 1: send = "current data collected"
default: print("No Selection")
}
let alertController = UIAlertController(title: "Password check", message: "Enter Password to see collected data", preferredStyle: .alert)
alertController.addTextField
{
textField in
textField.placeholder = "Enter Password"
textField.isSecureTextEntry = true
}
let confirmActionBtn = UIAlertAction(title: "OK", style: .default)
{
[weak alertController] _ in
guard let alertController = alertController, let textField = alertController.textFields?.first else
{
return
}
print("Password is \(String(describing: textField.text))")
func performSegue(withIdentifier identifier: String, sender: Any?) -> Bool
{
if identifier == "Link6"
{
if (textField.text == self.password)
{
return true
}
}
return false
}
}
alertController.addAction(confirmActionBtn)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
//self.performSegue(withIdentifier: "Link6", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "Link6", let destination = segue.destination as? DataviewController
{
destination.Answer2 = self.Answer2
destination.Answer1 = self.Answer1
destination.ID = self.ID
destination.date = self.date
destination.send = self.send
}
}
This is done in the shouldPerformSegue method.
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "FirstToUsersSegue" {
if (userText.text == username && passText.text == password) {
return true
}
}
return false
}
Alerts are asynchronous, as is performing segues, so this code is performing a segue and displaying an alert.
If you don't want to perform the segue until after the alert has been dismissed, you will need to put the call to performSegue(withIdentifier:sender:) inside of the confirmActionBtn handler.

How to send the title from UIAlertAction to another UIViewController as title of that VC

I have an UIAlertController with 4 options. When I choose any of that options I want to send the name of that option to the next ViewController as Title of that ViewController.
Here is my code:
func showOptions(){
let actionSheet = UIAlertController(
title: "Select a type of inspection",
message: nil,
preferredStyle: .actionSheet
)
let cancel = UIAlertAction(
title: "Cancel",
style: .cancel,
handler: nil
)
let copyOfVehicleShortCheck = UIAlertAction(
title: "Copy of Vehicle Short Check",
style: .default
) { action in
self.performSegue(
withIdentifier: Constants.checklistInspectionIdentifier,
sender: self
)
}
let fullDailyDefect = UIAlertAction(
title: "Full Daily Defect and Damage Check",
style: .default
) { action in
self.performSegue(
withIdentifier: Constants.checklistInspectionIdentifier,
sender: self
)
}
let licenceCheck = UIAlertAction(
title: "Licence Check Y/N",
style: .default
) { action in
self.performSegue(
withIdentifier: Constants.checklistInspectionIdentifier,
sender: self
)
}
let vehicleShortCheck = UIAlertAction(
title: "Vehicle Short Check",
style: .default
) { action in
self.performSegue(
withIdentifier: Constants.checklistInspectionIdentifier,
sender: self
)
}
actionSheet.addAction(copyOfVehicleShortCheck)
actionSheet.addAction(fullDailyDefect)
actionSheet.addAction(licenceCheck)
actionSheet.addAction(vehicleShortCheck)
actionSheet.addAction(cancel)
self.present(actionSheet, animated: true, completion: nil)
}
It is possible to send that parameter title from UIAlertAction to the next ViewController ?
As #Sh_Khan pointed out, to get the title you need action.title.
My answer is an add-on, since from your question and code it's not clear that you know how to do this.
To actually send the title from one VC to another you need to override the prepareForSegue method.
Create a global variable:
var selectedTitle = ""
In the action handlers set:
selectedTitle = action.title
Create a new property in your destination view controller:
var title = ""
And override prepareForSegue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "insert your identifier here" {
if let vc = segue.destination as? YourDestinationViewController {
vc.title = selectedTitle
print("Title set in destination VC")
}
}
}
The callBack of the alertAction returns it
let copyOfVehicleShortCheck = UIAlertAction(title: "Copy of Vehicle Short Check", style: .default) { action in
print(action.title)
}
As #Sh_Khan mentioned, you can get title of action inside action handler.
Now you need to stored selected title and pass it to next controller, like that:
Save action title:
let copyOfVehicleShortCheck = UIAlertAction(
title: "Copy of Vehicle Short Check",
style: .default
) { action in
self.selectedActionTitle = action.title
self.performSegue(
withIdentifier: Constants.checklistInspectionIdentifier,
sender: self
)
}
Pass in prepareForSegue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let destination = segue.destination as? MyViewController {
destination.title = selectedActionTitle
}
}

Custom Barcode scanner, cannot pass back scanned data

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

Error UIAlert when trying to do unwindsegue

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

Resources