Send an Integer back to the previous View Controller - ios

Hello I am trying to pass back a variable that has the level number. After the user clears the round below is called
if enemyHP <= 0.0{
level = level + 1
battle(winPlayer:true)
}
then function battle is called
let alert = UIAlertController(title: "finished!", message: finishedMessage, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title:"Go Back",style:.default,handler:{action in
self.dismiss(animated: true, completion: nil)}))
self.present(alert,animated: true,completion: nil)
}
I am trying to display the level number in the previous view controller so I need the variable level to go be passed back. What is the best way for this? I have used segues and it doesn't work with the alert.
Delegate
protocol DataSentDelegate {
func Back(data: Int)
}
I used
delegate?.Back(data: level)
In the previous ViewController I added
func Back(data: Int){
new = data
}
print(new)
It doesn't appear.

Instead of using the dismiss-present methods, you should use a segue and then pass the object in prepare(for:sender:) this way:
func prepare(for segue:UIStoryboardSegue, sender:Any?)
{
if segue.identifier == "Back" // Give it any name
{
if let destination = segue.destination as? MyViewController // NOTE: this is your custom view controller class
{
destination.level = yourDesiredValue
}
}
}
Update: to perform the segue:
let alert = UIAlertController(title: "finished!", message: finishedMessage, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title:"Go Back",style:.default,handler:{action in
self.performSegue(withIdentifier: "Back", sender: nil)
}
self.present(alert,animated: true,completion: nil)

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 detect Add button presses in PKAddPassesViewController

I'm trying to detect when the users tap on the "Add" button in the PKAddPassesViewController.
I added addPassesViewControllerDidFinish() so that when passVC is dismissed, function addPassesViewControllerDidFinish() will be called.
override func viewDidLoad() {
self.pass = try PKPass(data: downloadedData! as Data)
let passVC = PKAddPassesViewController(pass: self.pass)
self.present(passVC!, animated: true)
// when passVC is dimissed by the user, addPassesViewControllerDidFinish is expected to be called, but it never gets called.
}
func addPassesViewControllerDidFinish(_ controller: PKAddPassesViewController) {
print("enter DidFinish")
let passLib = PKPassLibrary()
// Get your pass
guard let pass = self.pass else { return }
if passLib.containsPass(pass) {
print("if start")
// Show alert message for example
let alertController = UIAlertController(title: "", message: "Successfully added to Wallet", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
controller.dismiss(animated: true, completion: nil)
}))
controller.show(alertController, sender: nil)
print("if end")
} else {
// Cancel button pressed
print("else start");
controller.dismiss(animated: true, completion: nil)
print("else end");
}
}
However, when passVC is dimissed by the user, func addPassesViewControllerDidFinish() never gets called at all.
There are three things you should fix:
1. Extend PKAddPassesViewControllerDelegate in your ViewController class.
2. Double check if you added delegate to your PKAddPassesViewController: VC?.delegate = self, which will link your delegate to PKAddPassesViewControllerDelegate.
3. Inside addPassesViewControllerDidFinish, dismiss controller first. Then do whatever you want inside passLib.containPass. The alertController is no longer belonged to the controller, maybe to its parent view.

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

Prevent presenting the UIAlertViewController after navigating to the other view

I have one scenario when the user did not use the application for more than 5 min app will show a popup with session expiration message.
The code for session expiration is added in the appDelegate and from there the popup will be presented on the current view controller.
code is
#objc func applicationDidTimeout(notification: NSNotification) {
if (window?.rootViewController?.isKind(of: UITabBarController.self))! {
for view in window?.rootViewController?.view.subviews ?? [(window?.rootViewController?.view)!] {
if view.isKind(of: MBProgressHUD.self) {
return
}
}
if window?.rootViewController?.presentedViewController != nil {
window?.rootViewController?.dismiss(animated: true, completion: {
self.showMessage(message: Message.sessionTimeout)
})
} else {
self.showMessage(message: Message.sessionTimeout)
}
}
}
fileprivate func showMessage(message: String) {
let alert = UIAlertController(title: appName, message: message, preferredStyle: .alert)
let actionOkay = UIAlertAction(title: "OK", style: .default) { (action) in
DispatchQueue.main.async {
UIView.transition(with: self.window!, duration: 0.3, options: UIView.AnimationOptions.transitionCrossDissolve, animations: {
CommonFunctions.setLoginAsRootVC()
}, completion: nil)
}
}
alert.addAction(actionOkay)
self.window?.rootViewController?.present(alert, animated: true, completion: nil)
}
Now if the user is doing some data entry and at that time, if the user leaves application ideal for 5 min or more the keyboard will dismiss and the session expiration message shown there.
But as the text field's delegate method textFieldShouldEndEditing has some validation and if that validation fails it shows a popup with the message and ok button.
So when the user taps on the ok button in the session expiration message popup, it will redirect the user to the login screen but due to the text field's delegate method validation, it shows one pop up in the login screen.
Code for the validation fail message popup is
fileprivate func showErrorMessage(message: String) {
let alert = UIAlertController(title: appName, message: message, preferredStyle: .alert)
let actionOkay = UIAlertAction(title: "OK", style: .default) { (action) in
self.txtField.becomeFirstResponder()
}
alert.addAction(actionOkay)
self.present(alert, animated: true, completion: nil)
}
How to prevent the popup from being present in the login screen?
I try to get the proper way to prevent the popup from appearing on the login screen.
But Finally, I found one heck to solve this issue.
I have declared one boolean in AppDelegate and set it's value to false when I want to prevent the popup from appearing and then revert it back to true when I want to show the popup.
I know this is not the elegant or efficient solution for the issue, but it works for now.
If anyone knows the better answer can post here, I'm still open to any better solution.
#objc func applicationDidTimeout(notification: NSNotification)
{
let visibleView : UIViewController = self.getVisibleViewControllerFrom(self.window?.rootViewController)!
self.showMessage(message: Message.sessionTimeout,Controller: visibleView)
}
fileprivate func showMessage(message: String , Controller : UIViewController) {
let alert = UIAlertController(title: appName, message: message, preferredStyle: .alert)
let actionOkay = UIAlertAction(title: "OK", style: .default) { (action) in
//Now apply your code here to set login view controller as rootview
// This controller is for demo
window!.rootViewController = UIStoryboard(name: "Main", bundle:
nil).instantiateViewController(withIdentifier: "loginview")
window!.makeKeyAndVisible()
}
alert.addAction(actionOkay)
Controller.present(alert, animated: true, completion: nil)
}
//MARK:- Supporting method to get visible viewcontroller from window
func getVisibleViewControllerFrom(_ vc: UIViewController?) -> UIViewController? {
if let nc = vc as? UINavigationController {
return self.getVisibleViewControllerFrom(nc.visibleViewController)
} else if let tc = vc as? UITabBarController {
return self.getVisibleViewControllerFrom(tc.selectedViewController)
} else {
if let pvc = vc?.presentedViewController {
return self.getVisibleViewControllerFrom(pvc)
} else {
return vc
}
}
}
Try this code, I've use this code many times may be it's work for you.

Conditional loading of ViewControllers: Temporary display of wrong ViewController

I am trying to write a login process for my app. I have embedded a navigation controller to HomeViewController and set it as the initial ViewController. How can I fix it such that when a user enters the wrong credentials the HomeViewController will not be shown at all?
This is what it is doing:
Correct credentials entered
Display LoginViewController -> User inputs credentials -> Display HomeViewController
Wrong credentials entered
Display LoginViewController -> User inputs credentials -> Display HomeViewController -> Display LoginViewController
Code for LoginViewController (look at the last block of code)
func handlingAuthentication(notification: NSNotification) {
let dict = notification.object as! NSDictionary
if dict["error"]! as! Bool == true {
let errorMessage = dict["message"] as! String
//initialize Alert Controller
let alertController = UIAlertController(title: "Authentication error", message: errorMessage, preferredStyle: .Alert)
//Initialize Actions
let okAction = UIAlertAction(title: "Ok", style: .Default){
(action) -> Void in
self.dismissViewControllerAnimated(true, completion: nil)
}
//Add Actions
alertController.addAction(okAction)
//Present Alert Controller
self.presentViewController(alertController, animated: true, completion: nil)
}
else
{
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "isUserLoggedIn")
NSUserDefaults.standardUserDefaults().synchronize()
self.dismissViewControllerAnimated(true, completion:nil)
}
}
Code for HomeViewController
override func viewDidAppear(animated: Bool) {
let isUserLoggedIn = NSUserDefaults.standardUserDefaults().boolForKey("isUserLoggedIn")
if(!isUserLoggedIn){
self.performSegueWithIdentifier("toLoginVC", sender: self)
}
}
UPDATE
I've tried placing the code block in ViewDidLoad but I am still getting the same issue (in fact now I'm stuck on the homePage)
override func viewDidLoad() {
super.viewDidLoad()
let isUserLoggedIn = NSUserDefaults.standardUserDefaults().boolForKey("isUserLoggedIn")
if(!isUserLoggedIn){
self.performSegueWithIdentifier("toLoginVC", sender: self)
}
usernameLabel.text = Data.sharedInstance.userName
getTaskDetails()
displayTask.dataSource = self
}
If the main view controlled decides and displays the login you will inevitable see it on screen because it's already in the process of displaying - so it shouldn't do it. You should have some other controller, perhaps a splash view controller, which decides to show either the login or the main view.
In your login view controller the alert OK button calls dismiss, this is the reason the login controller disappears and re-appears again (after showing the main controller for a short time).

Resources