Push and present view controllers with a single push animation - ios

From my navigation controller's root view controller Root
I want to push a view controller A
which then instantaneously presents another view controller B.
How can I do both at the same time with a single push animation?
(💡 The idea behind this is that view controller A allows for editing some content. If no content has been created, it needs to show view controller B first which allows the user to enter a title and then create the content.)
What I've tried:
When I do the following in view controller A's viewDidLoad() method:
if content == nil {
let createContentViewController = // instantiate new view controller instance
present(createContentViewController, animated: false)
}
UIKit also omits the push animation when animated is set to false – so I get no animation at all. When animated is set to true, I get a double animation (first push, then modal). 🙄

The reason you're having trouble doing what you describe is that you can't present View Controller B on View Controller A until View Controller A is part of the view controller hierarchy — and until you have pushed it, it isn't.
I would suggest, therefore, not using a presented view controller in this story at all. All you are really describing, it seems to me, is a View Controller A and its main view (View A) with a View B in front of it. That's something you can readily prepare between creating View Controller A and pushing it. View B can still contain a Dismiss button or similar to which you respond by sliding it off the screen, revealing View A.
If you really need a View Controller B for purposes of code organization, then have View Controller A be a custom parent view controller for View Controller B. A cool feature of that solution is that the whole thing can be configured in the storyboard using an embed segue. If we push View Controller A and you don't need View Controller B, you just hide View B before pushing.

Related

What is the best way to present a view controller on top of everything in iOS?

I'm trying to present a view controller that will be kept displayed above everything, regardless of the currently presented view controller, so it will be kept as displayed even if the view controller behind it will get dismissed, or starts to present another view controller.
I know how to find the topmost view controller (for example as suggested here) but in this case presenting over this view controller will make the new view controller depends on the hierarchy of the parent controller, which I try to avoid.
Present the view from the rearmost view, the window. But give it a high z index so that it shows up in front of everything else.

How to dismiss all but one view controllers and pop to specified view controller in Swift?

I have a bunch of views and then a logout button, which logs the user out and takes them to the first view controller (a login/register screen). I tried doing this with a modal presentation, but it destroys my navigation, and I can't use a pop to root view controller because it is not the root view controller - I am at least 2 navigation controllers deep. How would I go about somehow displaying only the first one? I basically need it to act as if the app was just relaunched. Would unwind segues help in some way? Thanksthis is what I mean by messing up the navigation. The following view controllers now pop up, instead of (the following pic is from the actual first time launching the app) how it should look
Supposing that you use a UINavigationController, you can use the UINavigationController.setViewControllers(_:animated:) method in combination with UINavigationController.viewControllers. The latter provides you an array of view controllers in the exact order they are stacked by the navigation controller, while the former can be used to alter the navigation stack. For instance, if you want to keep only the first 3 view controllers in the navigation stack:
guard let navigationController = self.navigationController else { return }
let viewControllersToKeep = navigationController.viewControllers.prefix(3)
navigationController.setViewControllers(viewControllersToKeep, animated: true)
Note: The animation performed by the navigation controller in this case is usually (I believe always, but not 100% sure) a push animation instead of pop, which might not be the desired one.
So if you want to make the navigation controller to perform a pop animation, then you should call pop until you reach the desired view controller, but the key is to animated a single pop. For the same example as above:
guard let navigationController = self.navigationController else { return }
let numberOfPops = navigationController.viewControllers.count - 3
for i in 0..<numberOfPops {
let animated = i == 0
navigationController.popViewController(animated: animated)
}
Hope this answers the question about popping to the desired view controller (you can detect your view controller by its type, then you can find out its index and use the logic above).
If you want to dismiss to the first presenting view controller, then you need to call UIViewController.dismiss(animated:completion:) on the first presenting view controller. dismiss method will behaves like this:
If the view controller is presenting a view controller, then it will dismiss the presented view controller;
If the view controller is not presenting any view controller, but it is presented by another view controller (i.e. has a parent view controller), then it will ask the parent view controller to dismiss it;
If none of the above, it will do nothing.
So long story short, you need to call dismiss on the view controller that you want to be left on the screen.
I basically need it to act as if the app was just relaunched.
In this case, assigning the login/register screen controller to UIWindow.rootViewController seems to be the right option. This topic has been already covered here: Swift ios set a new root view controller

Preserve state of View Controller when using segues

I have a View Controller (A) with a text field and some other things in it.
When the user presses a button on View Controller A it segues to View Controller B using "Present Modally".
How could I preserve the state of View Controller A (e.g. text in the textfield) when returning to it from View Controller B. I would rather avoid using NSUserDefaults if possible.
Thanks!
When you present view controller B modally on top of view view controller A, view controller A is not closed - it's just covered by view controller B. The close action on view controller B should invoke dismiss(animated:completion:) to dismiss the modal. When you do that you can be certain that view controller A will be revealed with its state intact.
You should NOT use a segue to go back to view controller A. That would create a new copy of view controller A that would wind up being displayed on top of the original view controller A and the new view controller B. That is a bad idea.

ContainerView in ios

I am new to ios and working on container view first time.I have a containerview in a Taskviewcontroller .The containerview contains four childs i.e. Viewcontroller A,Viewcontroller B,Viewcontroller C,Viewcontroller D.
These Viewcontrollers contains table view cells .When I click on the cells of ViewcontrollerA it opens AdetailViewcontroller,When I click on the cells of ViewcontrollerB it opens BdetailViewcontroller,When I click on the cells of ViewcontrollerC it opens CdetailViewcontroller,When I click on the cells of ViewcontrollerD it opens DdetailViewcontroller.
These View controllers have back
(Task) button (that comes by default due to navigation).This button is suppose to take to the previous Viewcontroller,like if it is on CdetailViewcontroller,then it should bring back to CViewcontroller).But this doesn't happen actually .In every case ,it brings back to ViewcontrollerA.I am not able to understand the reason.
Please help to fix this.
As your all view controller A, B, C, D are child view controller of task view controller and when you are navigating the navigation controller of task view controller is used as it is the parent of all four controllers.
So when you do popviewcontroller it goes back to task view controller(not to any child view controller specifically). And when task view controller is appearing it reinitialises all its child view controller which sets view controller A as default child of its container view.
If you want your desired result, add the child view controllers from code in viewDidLoad of TaskViewController.
Visit Add child view controller to current view controller

NavBar doesn't appear when segue is called programmatically

I have an iOS app with 3 views. I am using a "push segue" to move between them. I have linked a push segue from a button in First view to Second View. For moving from second to third, I cannot have a button. I have a list of items and the person can click in any of those to move to third(and last) view. So I added a segue in storyboard and calls it programmatically. The third view is opening properly except that the nav-bar is no longer there.
[self performSegueWithIdentifier: #"moveToFilesSegue" sender: self];
I can live without nav-bar. But the real problem is that I cannot do a push segue from my third view to any other view.
So my question is whether there is a way to call a segue from second view to third view in such a way that the third view also has the navigation controller that the first two have by default(ie navbar is also visible in third segue). There is some break in navigation because of me calling a segue programmatically.
So a few comments. If you would like to be able to get to the two other views from any view at any point in time, then I would suggest using a tab bar controller rather than a navigation controller.
If you must use a navigation controller, then you should keep the first view controller as the root view controller. Imagine then tapping a button that pushes the second view controller. Then say you tap a button to get to the third view controller from the second. What you should really do is perform an unwind segue from the second and then immediately perform a segue to the third. The animations will be a bit undesirable by default since it will probably show the pop and the push, but to treat it like a tab bar, you could just disable the animations on the pop and push.
To implement, keep an int variable in the first view controller (btw.. you can do this by keeping the int in the navigation controller as well if you want). In the viewDidAppear, check the int variable. If it == 1 then push the second view controller. If it == 2, then push the third view controller. Else, don't do anything. When a button is pushed in the second view controller to view the third, unwind the second view controller, but in prepareForSegue set the int variable in the destination view controller to 2 . Thus, when the first view controller appears, it will immediately push the third view controller.
I know it is a bit annoying, but you don't really want to keep pushing the same view controllers over and over again without unwinding.

Resources