Push to iOS NavigationController from inside Modal View - ios

I'm new to iOS programming and also to Stack Overflow. I've been trying to find an answer to my question, but searching hasn't yielded any results.
I'm trying to get the flow of my application down, but I'm running into issues. What I would like to have happen is this:
Initial view (NavigationController) -> Searching view (modal) -> programatically push different views onto the initial view's NavigationController from withing the searching view before dismissing the view.
My understanding is that inside the modal view, I should be able to do something like
[self.parentViewController.nagivationController pushViewController: someView]
but that doesn't work at all. After dismissing the modal view I'm just back at the initial view.
I've also attempted to pass a reference to the initial view navigation controller, but I can't seem to make that work right.
So if anyone knows how to programatically push views onto a navigation stack from inside a modal view, I'd love to learn! I'm really starting to think that my understanding of modal views is fundamentally flawed.
Thanks in advance for any help you can provide, and also your patience with a complete newb.

Annnnnd, I'm dumb.
I had the right approach, but it took me a day to realize that self.parentViewController returns a UINavigationController, so the extra ".navigationController" was completely unnecessary.
Correct reference:
[self.parentViewController pushViewController: someView]
Thanks for commenting, Rob.

parentViewController returned nil, but using presentingViewController worked. Swift 2.x:
let vc = storyboard!.instantiateViewControllerWithIdentifier(CustomViewControllerID) as! CustomViewController
if let navController = presentingViewController as? UINavigationController {
navController.pushViewController(vc, animated: false)
}

Related

Why my navigation controller make transitions modally instead of push now?

Looking for help with some problem which i clearly don't understand since i'm just beginner here so please help me out if you can.
Here is my issue, so far i built all views through storyboard and all are showed by navigation controller using "push" segue set up in storyboard, till now everything worked as expected.
In some point i decided to add calendar view in the bottom half part of the main view controller to show up when user press the button. Following some advices found online i decided to add it as a child view controller (and did it programatically).
Here is the code i used to present the view:
addChild(calendarVC)
view.addSubview(calendarVC.view)
calendarVC.didMove(toParent: self)
and here is the code i used to dismiss the view:
willMove(toParent: nil)
removeFromParent()
Everything works great so far, but here is the catch:
after triggering removeFromParent() part, transition between every other view controller changed from push to modal. Almost like i lost connection with navigation controller somehow?
I don't know, and my knowledge level is way too low to figure it out myself, any attempts on finding answer here by searching similar topics also didn't give me any answer i can understand, so hoping someone can shed a light on my problem here.
Thanks
make sure you invoke willMove(toParent: nil) removeFromParent() from child view controller.
Invoking the above functions from a parent view will remove the controller from the Navigation hierarchy.

How to correctly dismiss previous view controller

Apologies as I know there are some similar questions, but I've been looking for two weeks through every one I can find, and cannot figure it out (I'm a bit of a novice).
I have a few different View Controllers, not using a Navigation Controller. I can segue between them no problem. The issue is, I need each view to be dismissed when I segue to a new one. Here is some of what I've tried so far.
Option 1 (in new View Controller)
override func viewDidAppear(_ animated: Bool) {
presentingViewController?.dismiss(animated: false, completion: nil)
}
Option 2 (in old View Controller)
override func viewDidDisappear(_ animated: Bool) {
self.dismiss(animated: false, completion: nil)
}
In both of these cases, the new view gets dismissed and I'm taken back to the old view. I've tried about 20 versions of similar code.
Should I be using the first VC in my program as my "main" view controller, and presenting/dismissing all others on top of it? I didn't think this approach seemed memory efficient, when the "main" VC is not often used after initially loading the app.
It seems like I'm missing or not understanding something. Any help would be greatly appreciated.
Think of it this way: A view controller can't exist on an island. It has to be presented on top of something.
That means when you present one VC on top of another, the presenting view controller is the "foundation" for the new one you just presented.
If you don't want to present VCs on top of each other, you have a couple of options:
1) Use a navigation controller. This is probably the best approach. You can present or push any view controller. If you decide to push, you can remove the old one from the navigation stack, or you can keep it there so the user can go back. There are lots of ways to use a navigation controller, and it's easily the most flexible way to navigate between controllers.
2) Use a tab bar controller. This works best if you have just a few different view controllers in your app, but it's good for certain use cases.
3) Do exactly what you said in your post (use the root view controller to present/dismiss all other VCs). As I said, you can't present a view controller out of thin air-- there always has to be something behind it. Unless there's a ton of stuff going on in your root VC, this shouldn't cause any memory issues. This approach should be fine unless you're very particular about the animations between your view controllers.
In general, I wouldn't worry too much about memory usage until it becomes a problem. It should be fine to present view controllers on top of each other for 99% of normal use cases.
if you want to present VC B from VC A and want to dismiss VC A while Presenting you can use this Code
let parentVC = presentingViewController
dismiss(animated: true) {
let vc = self.storyboard!.instantiateViewController(withIdentifier...)
parentVC.present(vc, animated: true)`enter code here`
} `enter code here`

UINavigationController.set​View​Controllers is resulting in an empty viewControllers array

I am using the set​View​Controllers function on UINavigationController to set my first root view controller but the viewControllers array is empty after it has been called.
I know it is expected behavior to see the view controller in the viewControllers array immediately after setting, even if there is an animation so I'm not sure what could be causing the problem.
setViewControllers([viewControllerToPresent], animated: true)
Noteworthy: It is happening at app launch time but after I am certain the UINavigationController is loaded and ready.
The problem ended up being trying to simultaneously present a viewcontroller modally (tutorial slides) as the navigationcontroller was trying to push a viewcontroller on to the stack (both animated)
By delaying the presentation of the modals by 1 second, both view controllers were able to present without clobbering one another.
Would love to know the whys of this if anyone if familiar enough with UIKit and Animation APIs to explain. Will accept my own answer if I don't hear from anyone but will gladly change this if we get a better answer in the future.

Correct way of removing (deleting) a view/viewController from the stack after segue in Swift

I am running into a problem here. I am presenting views with performSegueWithIdentifier. Everything goes smoothly. The problem is that as a test, I only have 2 viewControllers with some data in them and I have two buttons that call a segue back to the other VC. If I keep performingSegues, you can clearly see that the memory usage goes up every two segues by around 0.4Mb. This tells me that the Views are not being deleted/removed from the view stack and are just using memory. I would like to know the correct way of getting rid of the view that presents the other view by using performSegueWithIdentifier (of course, after it finished presenting the view, else it will not work I guess).
Could you point me in the right direction? I found some objective-c code that tried to do this but it was very extensive and I don't know much about objective-C, so it was a little hard for me to understand.
Thank you in advance for your help!
Cheers!
Edit:
I am doing a "manual segue". By this I mean I have two view controllers standing on their own. They are not embedded in any navigationVCs or something like that. I am using an "Adaptive Segue" of type "Show".
If your goal is to always keep one VC, and since you already have a manual segue, what you can do is, after presenting the next VC you can pop the existing VC from the root and assign the destination VC as your root VC like so
class ManualSegue: UIStoryboardSegue {
override func perform() {
sourceViewController.presentViewController(destinationViewController, animated: true) {
self.sourceViewController.navigationController?.popToRootViewControllerAnimated(false)
UIApplication.sharedApplication().delegate?.window??.rootViewController = self.destinationViewController
}
}
}
When you do normal segues, you are piling up new view controllers on top of the existing ones without dismissing them. That's why your memory usage keeps going up.
You need to provide more information before we can give you specific guidance. Are you doing modal segues or pushing onto a navigation stack?
If you're pushing, you want to pop to the view controller you came from. Think in terms of a stack of plates. When you push, you add plates to the stack. When you pop, you take plates off and reveal the plates (view controllers) underneath. Do a search in the Xcode help system on "PopTo" to find the methods that let you either pop back to a specific view controller or back to the root view controller of your navigation controller.
Modal view controllers stack on top of each other in a similar fashion, but without the origination of a navigation controller. If you've pushed a series of one or more modals and you send the dismissViewControllerAnimated:completion: method to the view controller you want get back to it will dismiss the modals that are on top of it.
You can also look at using unwind segues. Unwind segues let you go back where you came. Do a search in the Xcode help system on "Using unwind segues" for more information. Most of the tutorials on unwind segues are written in Objective-C but you should be able to find some in Swift if you look. Explaining unwind segues in enough detail to be useful is more than I can cover in this answer.

poptorootviewcontroller and instruct rootviewcontroller to perform some action

I have a navigationcontroller, and under that I have controller A as rootviewcontroller and controller B is linked to A through a default push segue.
What I want to do is to when some action is pressed in B, I will ask the navigation controller to pop to rootviewcontroller, which is A, and apply some popovercontroller(or just another view or another window) on top of A.
Someone suggested me to ask appdelegate to create another window on top of current uiwindow. I think it is possible and I want to know if there is any simpler/more elegant solution for this problem.
a general solution of what I can do will be appreciated and some simple code samples in Swift will be much more appreciated :P
I will just provide my own answer here.
According to my own experience, there is no popping segue and therefore we cannot exactly get destination view controller from segue. however, UINavigationController has an attribute viewControllers which basically are all children controllers on the stack.
so before popToRootViewController, just get the VC but calling self.nagivationController.viewControllers[0] and you get the root view controller already

Resources