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

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.

Related

Linking To An Embedded VC in A Container Of A VC Embedded In A UINavigationController

I have the following storyboard structure:
Essentially I have a UIButon that I want to link to the "TargetTableViewController" via an IBAction - NOT by 'Ctrl+Dragging' but programatically by way of some code instead.
I believe I have to present "NavViewControllerTwo" as a NavigationController (as it 'holds' "HoldingViewController") and have tried various renditions of this via the many threads at StackOverflow. But this did not work.
I suspect it is because the actual "TargetTableViewController" is embedded in a ContainerView of "HoldingViewController" and that causes the problems.
I have tried implementing other renditions of presenting "TargetTableViewController" as a child of the "HoldingViewController", but that caused me more problems.
I realise I may have over-complicated things here, but the reason for the container is I want to display a BannerView at the bottom of my eventually presented view.
If anyone can be kind to point me in the right direction, I will be very grateful. Thank you for your time.
** CLARIFICATION **
I am actually trying to present "HoldingViewController" as that would effectively display my "TargetTableViewController".
Please refer to amended image below:
Apologies for the confusion and lack of clarification.
Setup a segue from MainViewController to NavViewControllerTwo in the storyboard, give it an identifier and then perform that segue from your code on the button press action using [self performSegueWithIdentifier...

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.

Changing ViewController programmatically

I have been looking for a solution to this for quite some time but I still fail to see what the best solution is:
I am trying to swap view controllers programmatically, without anything in the storyboard, pure swift files.
As far as I see presentViewController() just creates a 'modal' which would cause the previous ViewController to stay in the memory (tested this, deinit never fires for the first controller). The solution I have found is to switch the rootViewController: self.view.window?.rootViewController = ViewController2() -> this fires deinit of the first one.
While this solution would work in theory, I am wondering...
Is there some recommended way or best practice of how to do this programmatically? Or is it really just about changing the viewRootController's value?
How do you structure your app? Do you use one ViewController and you swap views? Or you present other ViewControllers as modals with presentViewController? (I am totally new to this and I can't seem to find any good source; most of the articles deal with storyboard)
Thanks a lot!
EDIT: I will add: my test app is not supposed to have any kind of navigation for different viewcontrollers (no tabs, nor anything like that). It basically works like screen1->screen2->screen3->screen4. If a reset button is pressed, it gets back to screen1. I am purely interested of swapping ViewControllers, nothing else.
1: Changing the main window's root view controller is correct for swapping view controllers.
2: Generally, swapping view controller is not used. You can use a navigation controller that has a root view controller (eg. your main screen) and then push other view controllers as needed. Tab bar view controllers are also very useful.
https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/CombiningViewControllers.html#//apple_ref/doc/uid/TP40011313-CH6-SW1
Hope this helps!
1.
The basic ways are:
navigationController?.popViewControllerAnimated(true)
or just pop to root view
navigationController?.popToRootViewControllerAnimated(true)
pushing can be done similarly
or setting a new stack
navigationController?.setViewControllers([viewController], animated: true)
These are the recommended ways of navigating in code just pick the one appropriate to your specific situation.
The push and pop mechanism is probably the most recommended way of doing general navigation.
Try to always use navigation controllers and only use modal views when appropriate.
Use navigation controllers when navigating through your main views. Modal views can be used as side views or extension to the view presenting that view modally.
Edit:
There are many ways of efficiency navigating through an app. In this particular case pushing and popping the navigation stack is probably the best way to go. But keep in mind that there are Tab bar controllers, collection views and others. At the end of the day The best is to look at your app structure and based on that decide which navigation method is the best for you.

Unwind segues not working with modal view controllers

I'm working on a storyboard-based iPhone app.
I am successfully using unwind segues to navigate my stack of view controllers.
However, when I present a modal view controller I cannot seem to be able to trigger the unwind segue that I have specified in the storyboard file.
Is this normal or a bug?
Problem solved.
Apparently I've hit some glitch with the storyboard editor.
What I did was to delete the destination (modal) view controller and then re-create it.
Works fine, now.
Bug report filed.
So I have the same problem (more than two years later!). I haven't fixed the underlying issue, but I found that a modal view being presented with a default presentation style will unwind OK, but one that uses a page sheet presentation style simply doesn't work. The unwindToViewX method gets called on the parent view controller that the unwind segue is moving to, though, so I've circumvented the issue with the following code:
if (self.presentedViewController) {
[self dismissViewControllerAnimated:YES completion:NULL];
}
This saves me from setting up a delegate system as the unwind action is doing all the heavy lifting, but also avoids an issue where perhaps the unwind action might work one day, because in that case self.presentedViewController should return NO because the unwind action worked correctly and we won't end up dismissing two view controllers by accident.
I hope this helps others in the same boat, but I'd also like to hear if anyone else has better solutions.

iPhone: need simplest way to handle iOS navigation

I have a fairly big app made with all view-controllers connected by storyboard segues, before I realized that navigation buttons is a requirement.
I am at a point where I can no longer delete all scenes and start over, nor I can extend the segue approach because its quite overused.
I am looking up for small, elegant solution that will allow all my view controllers to have backward navigation. See my scene hierarchy:
Page 1---> Page 2
Page 1---> Page 3 ---> Page 4
Page 3---> Page 5 and so on.
So I should be able to go back from Page 5 to Page 1 backwards.
I have about 12 screens as of now.
What should I do?
Can I safely mess up with storyboard?
Or should I add code snippets to my viewDidLoads and so on to handle the stuff?
My problem is that when I searched the solution, I found too many approaches including navigation controllers, uinavigationbar, UIBarButtonItem and so on. With each of them, I find it maybe too much for my need, and I need a smaller thing that may just work.
All I may need is only back buttons as of now, but with minimal effort - as I am quite tight on deadline :-(
Appreciate quick help.
I found the answer from apple docs - basically I needed an approach to handle things (instead of straight code), and here it is:
Initialize navcontroller in appdelegate didFinishLaunchingWithOptions method, with root view controller set to first view controller of the app
set appdelegate window's rootviewcontroller = navcontroller
with every 'forward' navigation - use pushViewController along with target view controller
there is no need to handle backward navigation - navcontroller takes care of it by default.
Above will also add extra navigation bar on top of every view controller (except the root view controller of the app's delegate).

Resources