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()
}
Related
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()
}
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
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"
I have an app which is connected to Firebase and you can login using Google and Facebook. The app has a home screen which is loaded when the user is signed in. I don't want to show the login screen if the user has already signed in before. So I use this code in the home screen to check if the user is signed in. Home screen is the initial view controller in the app
override func viewDidAppear(_ animated: Bool) {
if Auth.auth().currentUser != nil{
// user is signed in
}else{
// No user is signed in.
performSegue(withIdentifier: "login", sender: nil)
}
}
This code works very well. I know, that I could use UserDefaults too, but this seems to be an easier way. Is it wrong or should I use UserDefaults?
But the only problem with that is, for a quick moment the home screen is shown and then the segue is performed to the login screen, and I guess it's because it's in the viewdidApepar method, so the code gets executed after the viewcontroller appeared. Do you know a solution to it?
This is what I do personally, didFinishLaunchingWithOptions in AppDelegate.
if UserManager.sharedManager.currentUser != nil {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let viewController = storyboard.instantiateInitialViewController()
appDelegate.window?.rootViewController = viewController
} else {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let storyboard = UIStoryboard(name: "Login", bundle: Bundle.main)
let viewController = storyboard.instantiateInitialViewController()
appDelegate.window?.rootViewController = viewController
}
Of course I have a UserManager which handles if the user is logged in or not.(UserManager:NSObject)
var currentUser: User? {
return User.unarchiveStoredUser()
}
User is stored with decoder into UserDefautls then unarchived to verify if a user is logged in or not (User :NSObject)
func archiveUser() {
let data = NSKeyedArchiver.archivedData(withRootObject: self)
UserDefaults.standard.set(data, forKey: "userIdentifier")
UserDefaults.standard.synchronize()
}
class func unarchiveStoredUser() -> User? {
if let data = UserDefaults.standard.object(forKey: "userIdentifier") as? Data {
let user = NSKeyedUnarchiver.unarchiveObject(with: data) as! User
return user
}
return nil
}
Hope it helps you.
I'm an android developer. in android, when user login in application, I will re-open the MainActivity class ( controller ) to refresh some views.
in iOS applications, how to do this scenario ?
You can reopen you default/LandingViewController.
Suppose you have a View Controller with name LandingViewController
When you successfully logged in all you need is to re instantiate the LandingViewController
In AppDelegate class make a function with name
func userDidLoggedIn(){
let storyboard = UIStoryboard(name: "Main", bundle: nil)//Replace Main With your own storyboard name containing LandingViewController
let landingViewController = storyboard.instantiateViewController(withIdentifier: "LandingViewControllerIdentifier")//Pass your LandingViewController Identier that you have set in your storyboard file.
guard let subviews = self.window?.subviews else {
return
}
for view in subviews {
view.removeFromSuperview()
}
self.window?.rootViewController = landingViewController
}
Now Simply Call this Function where ever in the entire project like this In your case write below lines in the completion block of login request API.
let delegate = UIApplication.shared.delegate as! AppDelegate
delegate. userDidLoggedIn()
Once user login, you can change your rootviewcontroller like this:
var nav_VC: UIViewController?
func onSuccessfulLogin()
{
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
nav_VC = nil
if nav_VC == nil {
nav_VC = storyboard.instantiateViewController(withIdentifier: "home_nav")
}
self.window?.rootViewController = nav_VC
self.window?.makeKeyAndVisible()
}