Swift viewWillAppear redirect - swift

I'm using "viewWillAppear" to check UserDefaults.standard if i have data on the defaults i want redirect to Dashboard View and pass out of Login.
When i Log In correctly the app redirect normally but not in viewWillAppear in case of have data.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let defaults = UserDefaults.standard
if defaults.string(forKey: "username") != nil {
print("Exist")
print(defaults.string(forKey: "username")!)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "Dashboard")
self.present(vc, animated: true, completion: nil)
}
}
And i get this error con debug.
2016-11-04 17:44:32.130024 RHiOS[1155:278240] Warning: Attempt to present <RHiOS.MenuViewController: 0x100f0d480> on <RHiOS.ViewController: 0x100e0b880> whose view is not in the window hierarchy!
How can i redirect to Dash in case of have data? Thanks.

You cannot present a ViewController in viewWillAppear. Simply put this code in viewDidAppear and it should fix your problem.
When the view is not visible, it's not in the window hierarchy as the error mentioned. You could also create a segue in your storyboard and call performSegue(withIdentifier:sender:) instead.

You won't be able to present anything when the view hasn't appeared yet, so you either have to move your code into viewDidAppear or prevent this view from showing in the first place and redirect directly to the dashboard from a parent view controller that presents either the dashboard or login screen depending on whether or not you have data.

Related

Can't present view controller

I have a function that checks if the user is logged in. If the user is not logged in I want to present a login view controller. But as I mentioned in the title, the view controller is not shown. In addition, the initial view controller is not the login view controller (which is set in a storyboard and is a tab bar view controller). In fact, that view controller is in a different storyboard. Is that the problem?
private func validateAuth(){
if FirebaseAuth.Auth.auth().currentUser == nil {
let storyboard = UIStoryboard(name: "LoginAndRegisterStoryboard", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "LoginViewController")
self.present(vc, animated: true, completion: nil)
}
}
I tried to set the breakpoint to the function. The debugger says that the function is executed but the view controller is not shown. Any solutions?
And yes, the function is called.

using pushViewController in containerView Not work

I have created containerView which contain tableViewController.
The main issue, when user click on information, tableViewController must show the information which user clicked.
it show the information when I use present(<#T##UIViewController#>, animated: <#T##Bool#>, completion: <#T##(() -> Void)?#>)
but doesnt work if I call self.navigationController?.pushViewController
the main issue is when I make vc.info = info it doesnt work, the info has value but using injection the value in another class is nil.
Here is my code:
func showLoginDetailsOnIpad(encryptedDataBase: info) {
self.view.addSubview(loginContainer)
let mainStoryboard : UIStoryboard?
mainStoryboard = UIStoryboard(name: "StoryboardiPad", bundle: nil)
let vc = mainStoryboard!.instantiateViewController(withIdentifier: "tableVC") as! tableVC
vc.info = info
vc.showingLoginInfo = true
vc.modalPresentationStyle = .automatic
self.navigationController?.pushViewController(vc, animated: true)
}
Explanation
You mentioned the use of a containerView. The table controller being displayed inside the containerView, doesn't automatically get a reference to the nearest navigationController.
So in this line: self.navigationController?.pushViewController(vc, animated: true)
self.navigationController might actually be nil. You can confirm this by printing its value before running the push method like so:
print(self.navigationController) // See if this prints nil.
self.navigationController?.pushViewController(vc, animated: true)
Fixes
To fix the issue, I suggest to pass a reference to the parent controller's navigationController to the child controller - through the embed segue.
In your parent controller, you can use this code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? UITableViewController{
destination.navigationController = self.navigationController
}
}
And in your table view controller, leave the code as is.

Making a new view controller appear after interstitial ad is dismissed

I am building an iOS app in swift with Xcode that has a button that, when pressed, will (among other things) show an interstitial Admob ad, and then go to a different view controller. Right now, when I press the button, an interstitial ad will appear. However, when I close the ad, the view is still on the original page. If I click the button again, then I am taken to the second view controller. Sometimes, however, I have to dismiss three or four ads before I get to the second view controller. How can I make it so that, after I dismiss the interstitial ad, I go straight to the second view controller?
Here is the current code:
#IBAction func postPressed(_ sender: Any) {
if contact.text?.isEmpty == true {
print("No contact")
return
}
if avail.text?.isEmpty == true {
print("No availability")
return
}
uploadSpot()
}
func uploadSpot() {
let key = Int.random(in: 20001..<30000)
let object: [String: Any] = [
"Contact": contact.text!,
"Availability": avail.text!,
]
geoFireRef.child("\(key)").setValue(object)
let location = manager.location!
geoFire.setLocation(location, forKey: "\(key)")
if interstitialAd?.isReady == true {
interstitialAd?.present(fromRootViewController: self)
}
let storyboard = UIStoryboard(name: "SpotLoaded", bundle: nil)
let vc = storyboard.instantiateViewController(identifier: "SpotLoaded")
vc.modalPresentationStyle = .overFullScreen
present(vc, animated: true)
}
The import part is in the function "uploadSpot()," where
if interstitialAd?.isReady == true {
interstitialAd?.present(fromRootViewController: self)
}
presents the ad and
let storyboard = UIStoryboard(name: "SpotLoaded", bundle: nil)
let vc = storyboard.instantiateViewController(identifier: "SpotLoaded")
vc.modalPresentationStyle = .overFullScreen
present(vc, animated: true)
switches the viewcontroller. I have tried reversing the order if these two (i.e. putting let storyboard, etc., before if insterstitialAd), but that just takes me to the new view controller and the ad never appears. I have also tried putting the let storyboard chunk inside of the brackets following the if interstitialAd. This does not help either. If I put it after interstitialAd?.present(fromRootViewController: self), I have the same problem where the ad shows but the view controller does not change. If I put it after, the viewcontroller changes but the ad never shows.
As the main purpose of interstitial ads is to appear in between views of an app, I do not think what I am trying to do should be so difficult. I think there must just be something I am missing or do not understand. Please help in whatever way you can.

How can I add a segue identifier to a programmatically modal transition?

I have a controller which is in the Main storyboard. When I click on a button, I call the displayBorneDetailsAction() action, it presents a modal view from another storyboard.
I would add a Segue identifier when I present my modal to pass data from my main view controller to my modal view controller (with prepareForSegue), but I don't know how to do it.
I tried to use performSegue(withIdentifier:), but it doesn't present the modal in the same way.
#IBAction func displayBorneDetailsAction(_ sender: Any) {
// open the modal of a borne
let storyboard : UIStoryboard = UIStoryboard(name: "Borne", bundle: nil)
let vc: BorneVC = storyboard.instantiateViewController(withIdentifier: "BorneVC") as! BorneVC
let navigationController = UINavigationController(rootViewController: vc)
navigationController.modalPresentationStyle = UIModalPresentationStyle.overFullScreen
navigationController.edgesForExtendedLayout = []
self.present(navigationController, animated: true, completion: nil)
}
You cannot add an identifier to a programmatical segue, since you are able to access the instance of the controller that is being presented. Simply do anything you want with the controller in the function where you present it.

How to programmatically show modal in a UINavigationController

In my app, I have a UINavigationController that i'm pushing and popping ViewControllers from. At some point, I want to show a VC modally (showing the previous controller "underneath"). I can get it to work by setting up a segue in a storyboard, however I'm in a spot where I need to do it programmatically, and I can't seem to find the right magic incantation to make it work.
I saw a couple of similar questions but they seemed to be showing the UINavigationController modally, not showing one of the VC's on the UINavigationController stack modally.
(I put up a test application here: https://github.com/SuperTango/ModalNavController, and that's where this code and images come from)
The "Manual" code does:
#IBAction func goToVC2Tapped(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let destinationViewController = storyboard.instantiateViewController(withIdentifier: "VC2ViewController") as! VC2ViewController
destinationViewController.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
self.navigationController?.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
self.navigationController?.pushViewController(destinationViewController, animated: true)
}
but it's not working (see the second transition in the gif below).
The segue that works is setup like this:
This gif is from the test app and shows how it works with the segue, but not manually.
Any ideas? Thanks!
To present modally you need to use:
present(destinationViewController, animated: true, completion: { })
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let destinationViewController = storyboard.instantiateViewController(withIdentifier: "YourVC") as! YourVC
destinationViewController.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
destinationViewController.modalTransitionStyle = .crossDissolve
self.present(destinationViewController, animated: false, completion: nil)
I think this helps show your ViewController in a modal way of presenting. Write the above code under outlet actions or where you want to present your ViewController.