Present 2 navigationControllers in one stack in one go - ios

current i am developing this app and i have the main and login screen.
The architecture works in such a way that when the app launches, the main screen will always present first.
then, if the user is not logged in, the login screen will be presented modally on top of the main screen. (i think this is a normal approach right?).
However, the thing is now that, whenever i do the following code.
self.present(mainNavigationController, animated: false, completion: {
let storyboard = UIStoryboard(name: "LoginSignUp", bundle: nil)
let loginNavigationController = storyboard.instantiateViewController(withIdentifier: "loginNavigationViewController") as! UINavigationController
mainNavigationController.present(loginNavigationController, animated: false, completion: nil)
})
I will always see a blank white main screen before the the login screen is shown, that is kinda ugly.. Anyone has any solution such that these two view controllers are stacked on top of each other from the very beginning and shown once, rather than shown in sequence?

You code is trying to accomplish this:
Present main navigation controller modally over the current view controllers navigation.
Then after showing it present login modally on mainNavigation controller.
You need to change in this way:
Push your main screen in current navigation stack by pushViewController.
And then try to present login screen modally over the current navigation stack.

Related

Deinit Two ViewController at the same time in Swift

I have two UIViewController. First one is a welcome screen and second one is a login screen (which is inside a navigation Controller). Users can go back to welcome screen from login screen with a back button so login screen opens with self.present(LoginViewController(),animated: false) and after Login Screen, final UIViewController opens with appDelegate.window?.rootViewController = FinalViewController().
My problem is that neither LoginViewController or WelcomeViewController deinit at this scenario. However, If I;
Open FinalViewController (via changing RootViewController) directly from WelcomeViewController, without showing LoginViewController.
Open LoginViewController without showing WelcomeViewController then open FinalViewController (again changing RootViewController)
Controllers deinited. So I don't think any of viewcontroller has a retain cycle vs..
I want to deinit both login and welcome screens after open final controller.
EDIT: I found that putting it inside NavigationController blocks the deniting.
EDIT2: If I call self.dismiss(animated: false, completion: nil) before changing rootViewController. All controllers seems to be deinited but I'm not sure If It will be a better answer.
Why don't you use this hierarchy:
-UIWindow
-----UIWindow.RootViewController
----------UINavigationController
---------------WelcomeScreen
---------------LoginScreen (Push without animation)
On Login Success:
-UIWindow
-----UIWindow.RootViewController
----------UINavigationController
---------------FinalViewController
Hide navigation bar and use animated property as per need.
EDIT
A part from document:
viewControllerToPresent
The view controller to display over the current view controller’s content.
So reference of the parent controller can be access from presented controller, hence both VCs can access each other. e.g., self.presentedViewController. In order to remove it, one must dismiss controller. So presented controller will release the reference of presenter controller.

Transition Back to Sign In View Controller

On sign-out, I am trying to take the user back to the sign-in page. I've tried both instantiating the sign-in VC and pushing onto the navigation stack as well as performing an unwind segue. The former I've read is bad for memory and the latter didn't even animate a transition perhaps due to how my view hierarchy is set up.
Is there a recommended best approach to accomplish this action? The following is my view hierarchy:
Initial Nav Controller (logic to initialize sign-in VC vs tab bar VC based on user auth status) -> Sign-In VC (on sign-in pushes tab bar VC onto stack)
Tab Bar VC -> Nav Controller -> Home VC -> Nav Controller -> SignOut VC
There is a problem in the way you show the Sign in in view controller.
Sign in view controller shouldn't be pushed into the navigation controller stacks. The best way, is to present the Sign in view controller modally. That way, you have the ability to show the Sign in view controller wherever you are in the application.
1 During the launching process in application(_:didFinishLaunchingWithOptions), you could simply do:
yourTabBarController.present(signInViewController,
animated: false,
completion: nil)
This will make sure your Sign in view controller cover the screen. The user couldn't access whatever behind it.
Notice that I use animated: false to avoid showing the main screen to the user.
2 After you check the credentials, and the sign in succeeded, you could simply do:
signInViewController.dismiss(animated: true, completion: nil)
This will dismiss the login view controller that's covering the main screen, and show the actual main screen.
3 Whenever you want to redirect the user to the login screen (for example: After user click sign out), you do:
yourTabBarController.present(signInViewController,
animated: true, // Now we add animation.
completion: nil)
This will show the Sign in view controller again. The Sign in view controller will cover the main screen, and there's no way for the user to access the main screen.

Can't add view infront of modal controller's view

I have a modally presented view controller and I want to push a modal view on top of it. This should be simple to do, but I've missed something :)
The modally presented view controller doesn't cover the whole screen (it uses custom presentation) so I can't just add my view on top of that. But when I try to add my view to either the presenting view controller, or just onto UIApplication.shared.keyWindow.rootViewController.view I get a very strange result.
I've tried this using the default modal presentation an it still behaves in the same way, so I don't believe it's my custom presentation causing this.
Here is the exploded view from Xcode - with the overlay at the correct position front and center.
And here is the simulator at the time this interface was snapshotted.
Does anyone know why the alert view (which is clearly at the top of the stack looking in Xcode) doesn't appear?
NB: I'm pretty confident that the exploded view in Xcode is correct because it matches up with the output from po [[[[[UIApplication sharedApplication] keyWindow] rootViewController] view] recursiveDescription]
I think you should try to present some vc in your current and in other view controllers first.
... // your view controller
present(/* your alert view controller */, animated: true, completion: nil)
For example, it should show a empty vc like below. See if you can present any vc normally. If yes, it might be your alert view controller problem.
... // your view controller
let vc = UIViewController()
present(vc, animated: true, completion: nil)
... // presented a black screen, it works

'Application tried to present modally an active controller' with swift 3

This problem has been answered using objective C and tab view controllers. I can't figure out how to solve this problem of presenting modally to an active controller when the app tries to return to the home screen.
The series of view controllers for making a post is launched by a button on the top of my app's home screen. All view controllers are presented with the method: present(viewController, animated: true, completion: nil).
I read here that I have to dismiss the view controller that is trying to bring the user back to the home screen. If I write a line of code for dismissing that view controller, it brings me to the view controller immediately preceding it, which is not the home screen. How do I get my app to present the home screen from the end of a series of view controller for making a post without triggering this error?
I know that this has been previously answered in different contexts, but they didn't appear able to help me solve this problem. Would greatly appreciate some fresh eyes on this to help me solve this.
You can dismiss all view controllers instead of showing home screen over them.
var controller = presentingViewController
while let presentingVC = controller?.presentingViewController {
controller = presentingVC
}
controller?.dismiss(animated: true)

"Present Modally" does not show when another modal is already presented

In my iOS application, my VC is presenting a modal (A) via code.
However, when I already have another modal presented (B), this one is not showing at all.
However, when (A) is deinitted, I see that (B) also gets deinitted.
How can I make sure that (B) always gets shown, no matter what, and in front of all other modals?
performSegueWithIdentifier(SEGUES.SegueTabBarToBroadcast, sender: view )
My TabBarViewController is calling this segue. (The segue is modal according to storyboard).
The problem occurs when one of the view controllers in my TabBar presents a modal. Then, when I try to call performSegueWithIdentifier, the modal doesn't show (but yet deinits when I close the other modal).
I just want this modal to present NO MATTER WHAT. This modal should overlap all other modals.
I also tried this, but the problem persists:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let bvc = storyboard.instantiateViewControllerWithIdentifier("BroadcastViewController") as! BroadcastViewController
self.presentViewController( bvc , animated: true, completion: nil)
Presenting multiple view controllers as a modal is not good practice. If you want to present your vc no matter what, then you have to understand the hierarchy of your view controllers. Shortly, you can present view controller modally on the vc with view that has a window, and when you have already presented any vc modally, your view controller's view that has presented the vc does not have the window, thus can't present other view controller modally. Conclusion: you can present vc modally from the top-most vc. So, solution would be keeping reference to the top-most vc, and presenting the desired vc from that vc. Another solution would be adding vc's view directly to the main window of the app, but I would not recommend solving your problem that way. Hope, this helps.

Resources