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

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.

Related

Does calling viewController without dismissing it create a second instance?

I have been searching all over the web but I can't seem to find the answer to this.
Currently i am using presentViewController to start new ViewControllers, but on certain view controllers i do not dismiss it and call over it. I currently am not using any navigation controllers or anything like that.
I am just worried that if I call the same viewController again via presentViewController, that the same viewController would have 2 running instances.
Is it possible? Or does the iOS framework automatically reuse the idle viewController?
If so, how do i remove the idle view controllers?
Thank you! (I was holding back my question and tried to find it all over the web, so if you can point me in the right direction, it would be very helpful thanks!)
iOS will not reuse your view controller, you can easily check it yourself by printing your view controller in viewDidLoad, you will notice first that viewDidLoad is called every time, and next that all objects have different addresses.
Unless you create thousand of them, or the navigation of your app doesn’t let you come back to an “idle” view controller, I would not say this is an issue though.
I don’t see any clean way to remove a view controller from the memory without calling “dismiss”. You could try to:
- “refresh” your view with new data.
- use something like UIPageViewController if the workflow of your app allows this kind of behaviour.
- rework the navigation so you can dismiss the view before calling another one
Good luck

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.

Does the method popViewControllerAnimated: act different than the Back button of the NavigationController?

In my Application i have a container ViewController which is the RootView of an UINavigationController.
I transit between various Child ViewControllers through a segmentedControl in the Toolbar of the UINavigationController.
One of the Child views pushes another View on the NavigationController, keeping the ToolbarItems.
Using the Back Button I get the desired behaviour of returning to the RootViewController, but any try of going back programatically won't change the view.
popViewControllerAnimated always takes the topViewController of the Stack but nether updates the View. viewWillAppear of the Container ViewController won't be called this way.
So i am wondering if the Back button does any additional Stuff which won't happen in with popViewControllerAnimated.
Also popViewControllerAnimated:YES will freeze the app without throwing an error.
Thanks for the help in advance.
Edit:
Ok one test case was a little of.
A popViewControllerAnimated in the topViewController of the Stack works as normal. But a call to popViewControllerAnimated through the SegmentedControll does not.
I realise the way i wrote this might be a little confusing, i will try to make this all a little clearer later.
Ok, it was a timing issue. I changed the ChildViews of the container ViewController directly after the call to popViewControllerAnimated.

Push to iOS NavigationController from inside Modal View

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)
}

Resources