Swift problem with sending data to another ViewController - ios

Variable cards in second ViewController should be updated in first VC on buttonClick.
I tested the sending data from the first VC with printing data and it works good.
Here is the code:
#IBAction func btnTapped(_ sender: Any) {
let mainStoryboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let secondVC = (mainStoryboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController)
for card in setsOfCardsByLevel {
cardVC.cards.append(card)
}
print("Number of cards to send - \(cardVC.secondVC.count)") //PRINTS RIGHT NUMBER
presentVC("SecondViewController") //WORKS FINE
}
Maybe you dont need this method below but I will put it just in case:
func presentVC(_ VC_Name: String) {
guard let myVC = self.storyboard?.instantiateViewController(withIdentifier: VC_Name) else { return }
let navController = UINavigationController(rootViewController: myVC)
navController.modalPresentationStyle = .fullScreen
self.navigationController?.present(navController, animated: true, completion: nil)
}
PROBLEM: When I print cards in the secondViewController, they are empty(count = 0). How?
EDIT: While waiting for answer on StackOverflow I changed presentViewController to pushViewController and print in secondVC shows right number. I am confused.

Your problem is in your presentVC method. First in your btnTapped method you instantiate your second viewController and assing your cards, again in your presentVC method instantiate another controller and it's not relevant to the first you define in your btnTapped and cards variable not assigned. For solution you can pass your secondVC as a parameter to your presentVC, not the name of storyboard

Related

How to unwind through progrmmatically set rootVC (tabViewController) from child tab to different VC that was not instantiated

Swift 5 / xCode 11.5
When the app begins, App Delegate will check for a saved auth token on the users phone. If it is not present it will display what is the InitialViewController (instantiating it, setting it as rootViewController, and displaying it in the Window). If token is present it will instantiate and set the MainTabController as the rootVC and display it. The hierarchy looks like this:
RegisterViewController <-> (No nav controllers between) InitialViewController <->
LoginViewController All above controllers push to MainTabController if successful login or registration, assuming auth token for user is not present on phone (no nav controllers between any of these 'views', just pushing/dismissing.).MainTabController has three tabs that are UIViewControllers connected through NavControllers.
Since the rootVC is instantiated and displayed through Window object programmatically in the AppDelegate, the InitialViewController is not available to unwind to from a tab on the MainTabController (which was set as the rootVC), assuming the user does have a saved auth token.
My work around was deleting the auth token then calling a function in AppDelegate that would switch the rootViewController and have the Window display it (back to InitialViewController). I worry about all the other VC's that were instantiated though and the memory pile up this may cause. Are all the MainTabController's children de-allocated when I do this? Here is code for logout action and switching rootVC:
logout
#IBAction func logout(_ sender: Any) {
UserServices.logoutUser() {
responseResult in
if responseResult == .Success {
CommonUtils.clearAuthToken()
CommonUtils.clearAllUserDefaultData()
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.switchRootViewController()
} else {
self.showAlert(title: "Error", message: responseResult.rawValue)
}
}
}
switchRootViewController in AppDelegate
func switchRootViewController() -> Void {
let rootController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "InitialViewController")
self.window?.rootViewController = rootController
}
What I've tried with the unwind segue already (inside logout action)
let initialController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "InitialViewController")
initialController.addChild(self) // self is tab VC I'm trying to unwind from
let children = initialController.children
self.performSegue(withIdentifier: "unwindToHomeScreen", sender: self)
The InitialViewControllers unwind segue code
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
return true
}
override func canPerformUnwindSegueAction(_ action: Selector, from fromViewController: UIViewController, sender: Any?) -> Bool {
return true
}
#IBAction func unwindToInitialViewController(sender: UIStoryboardSegue) {}
I don't think this unwind is working because no reference exists for the InitialViewController. I'd display a picture of the layout but the project is secret in nature until product launch... Please let me know if you need more info or I was not clear in my description of the issue.
If you try this:
func ToSomeScreen() {
let homeViewController = storyboard?.instantiateViewController(identifier: "Storyboard ID") as? ScreenCustummerViewController //file name for next screen
view.window?.rootViewController = homeViewController
view.window?.makeKeyAndVisible()
}

Why does pushViewController doesn't work after navigating through Dynamic Link?

I'm building an app that can receive Firebase's Dynamic Link and redirects it into a certain UIViewController after clicking the link. So based on this question that I asked, I have the code in AppDelegate.swift that navigates the app to the UIViewController like this:
func goToDynamicLinkVC() { // This function is in AppDelegate.swift
let destinationStoryboard: UIStoryboard = UIStoryboard(name: "DynamicLink", bundle: nil)
let destinationVC = destinationStoryboard.instantiateViewController(withIdentifier: "DynamicLinkView") as? DynamicLinkVC
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = destinationVC
self.window?.makeKeyAndVisible()
}
And in that UIViewController I also have a function that moves to another UIViewController if a user pressed a button like this:
func routeToDynamicForm() { // This function is in DynamicLinkVC class
let destinationVC = UIStoryboard(name: "DynamicLink", bundle: nil).instantiateViewController(withIdentifier: "DynamicFormView") as! DynamicFormVC
self.navigationController?.pushViewController(destinationVC, animated: true)
}
But weirdly enough now that I pressed the button it doesn't move me to the next View Controller (in this case, DynamicFormVC). I've tried to debug the button whether it's working or not by doing this:
print("button tapped")
It shows the message on the debug area, but it still doesn't redirects to the next view controller. So what can I do to resolve this? If you need more information feel free to ask and I will provide it to you. Thank you.
I changed the goToDynamicLinkVC function to something like this:
func goToDynamicLinkVC() { // This function is in AppDelegate.swift
let destinationStoryboard: UIStoryboard = UIStoryboard(name: "DynamicLink", bundle: nil)
let destinationVC = destinationStoryboard.instantiateViewController(withIdentifier: "DynamicLinkView")
let navController = UINavigationController()
navController.setViewControllers([destinationVC], animated: true)
self.window?.rootViewController = navController
}

passing data from 2 view controllers without segue

I have a mainviewcontroller and a popup view controller which opens without a segue.
the popup viewcontroller recive data from Firebase, and then i need to append this data to an array in the mainviewcontroller.
How can i do that?
(i tried to create a property of the popupviewcontroller in the mainviewcontroller, but in crashes the app)
this is the code that opens the popup:
#IBAction func showPopUp(_ sender: Any) {
let popOverVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "sbPopUp") as! PopUpViewController
self.addChild(popOverVC)
popOverVC.view.frame = self.view.frame
self.view.addSubview(popOverVC.view)
popOverVC.didMove(toParent: self)
You need to connect the classes so that the popup class knows what to do with the data once it has been received. Here's a sample structure that works in a playground that you should be able to apply to your real classes.
class MainClass {
func showPopUp() {
let popOverVC = PopUpClass()
popOverVC.update = addToArray
popOverVC.receivedData()
}
func addToArray() {
print("Adding")
}
}
class PopUpClass {
var update: (()->())?
func receivedData() {
if let updateFunction = update {
updateFunction()
}
}
}
let main = MainClass()
main.showPopUp()
Or you could create a global variable so it can be accessed anywhere. ... It is better to pass data but I just thought it would be easier in this instance, so the variable can be accessed anywhere in your entire project.
if it is just a notification, you can show it in an alert, but if you don't want to use an alert my offer to present another view controller is not like this , try my code :
//if you have navigation controller ->
let vc = self.storyboard?.instantiateViewController(
withIdentifier: "storyboadrID") as! yourViewController
self.navigationController?.pushViewController(vc, animated: true)
//if you don't use navigation controller ->
let VC1 = self.storyboard!.instantiateViewController(withIdentifier: "storyboadrID") as! yourViewController
self.present(VC1, animated:true, completion: nil)
also if you want to pass any data or parameter to destination view controller you can put it like this before presenting your view controller :
VC1.textView.text = "test"

How to show once an UIVIewController in swift

I’m making an app, and I need to show only one time the on boarding view controller, so when the user re enter the app the on boarding view controller doesn’t appear any more.
I guess the on boarding view controller is your initial view controller. Try this in app delegate
First put checkFunction() in didFinishLaunchingWithOptions then set the identity (eg. Home) for the view controller you want to go
func checkFunction() {
let acceptedTerms = UserDefaults.standard.string(forKey: "acceptedTerms")
if acceptedTerms != nil && acceptedTerms == "Yes" {
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let VC = storyBoard.instantiateViewController(withIdentifier: "Home")
self.window?.rootViewController = VC
}
}
Second, modify UserDefaults.standard.string in somewhere
// example
#IBAction func acceptButtonTapped(_ sender: AnyObject) {
UserDefaults.standard.set("Yes", forKey: "acceptedTerms")
UserDefaults.standard.synchronize()
}

Swift: How to return to the same instance of my UIViewController

I have a button in my SettingsViewController that when pressed, I want to present the same instance of my TimerViewController each time the button is pressed.
I think I have gotten fairly close with this post here, iOS Swift, returning to the same instance of a view controller. So if you could point me to saving the instance of TimerViewController, that would work the best.
This is the code I have right now -
var yourVariable : UIViewController!
if yourVariable == nil {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
yourVariable = storyboard.instantiateViewControllerWithIdentifier("timer") as! TimerInterface
}
presentViewController(yourVariable, animated: true, completion: nil)
the code you provided should work. if your SettingsViewController gets deallocated though the timerViewController also gets deallocated and recreated the next time you present it. so you have to make sure to save its instance at an appropriate location.
var timerViewController: TimerViewController!
if timerViewController == nil {
let timerViewController = UIStoryboard(name: "Main", bundle: nil)
yourVariable = storyboard.instantiateViewControllerWithIdentifier("timer") as! TimerInterface
}
presentViewController(timerViewController, animated: true, completion: nil)
The best would be to save the ViewController somewhere , and get back to it .
A way to "get back to it" :
add
var tvc: TimerViewController? = nil
inside AppDelegate
when you get to your Timer (the best would be when you left it , in viewDidDisappear)
you add :
(UIApplication.sharedAplication().delegate as! AppDelegate).tvc = self
then when you get to the setting , if you want to segue back to the timer
let tvc = (UIApplication.sharedAplication().delegate as! AppDelegate).tvc
(UIApplication.sharedApplication().delegate? as! AppDelegate).window?.rootViewController?.presentViewController(tvc, animated: true , completion: nil)
if you ask yourself why should you present it with the rootViewController (last line ) it is because you can present an already active viewController , this will not present an already active vc .

Resources