pushing a view into stack below current root view - ios

I'm trying to implement a calendar like interface, with left and right arrows that allow users to scroll back in time/forward in time. To do this, I'm using a navigation controller, and pushing/popping views on the stack.
However, what if I'm currently viewing the root view, and I can't pop the view in order to get the correct animation direction if I'm trying to navigate back in time?
I found a post on Stack Overflow last week that demonstrated a method where the new view would be pushed onto the stack below the current root view, allowing the root view to be popped off. It was just a couple lines of code -- primarily getting an array of the current items in the stack and somehow pushing the new view below the currently active view. Unfortunately, I can't seem to find that particular post any longer...
Has anyone happened across the same link, or might be able to point me in the right direction? I had this working correctly earlier, and due to a computer malfunction lost a bit of my work.
EDIT:
With some experimenting, I have this partially working...
// create new view controller
ViewController *viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
// add below root
NSMutableArray *allViews = [[NSMutableArray alloc] initWithArray:self.navigationController.viewControllers];
[allViews insertObject:viewController atIndex:0];
[viewController release];
self.navigationController.viewControllers = allViews;
[[self navigationController] popViewControllerAnimated:YES];
[allViews release];
It does seem to be leaking memory, however -- if I do an NSLog of the allViews array, every time I go forward in time and then back to the previous view it seems to add an extra view to the array that doesn't get taken off later. This will work for now, but hopefully I can get this issue fixed.

UINavigationController Class Reference
I think that you are talking about the #property(nonatomic, copy) NSArray *viewControllers
Quoting the reference :
Discussion
The root view controller is at index 0 in the array, the back view controller is at index n-2, and the top controller is at index n-1, where n is the number of items in the array.
Assigning a new array of view controllers to this property is equivalent to calling the setViewControllers:animated: method with the animated parameter set to NO.
Is that the information you are looking for?
For what you are trying to do you might consider something in UIView instead.
transitionFromView:toView:duration:options:completion:
Creates a transition animation between the specified views using the given parameters.

Related

What sort of view controller approach is best for an iPhone iOS app using multiple window views?

I am interested in creating an app that starts with a menu which may possibly contain an options view, then steps from the menu view to a data-item selection view, then to a configuration view, and finally a result view that displays progress or changes. I want to have this process be repeatable like a loop, and have the user be able to jump backwards to a previous view if necessary. Jumping from view to view would of course be a user input / output with a button or something. FYI, I am using Xcode 5.1.1.
What would be the best approach to this? What kind of view controller is going to do the trick? I have heard a lot about navigation controllers, tables, etc.. but am having a hard time figuring out what to use in my case.
Below is a state-diagram similar to what I would like to do...
A UINavigationController should work great as your root view controller. It automatically includes a back button, and you can use the popToRootViewController method to return to the root of the navigation controller. You can set up a navigation controller as your root view controller from the applicationDidFinishLaunching method using this code.
MainMenuViewController *mainMenuViewController = [[MainMenuViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:mainMenuViewController;
self.window.rootViewController = navController;
For more information take a look at apples UINavigationController programming guide https://developer.apple.com/library/ios/documentation/uikit/reference/UINavigationController_Class/Reference/Reference.html
Each of your other screens may use different types of view controllers depending on their specific needs. If you need to display a list of items, definitely look into a UITableView. Apple's documentation for a UITableViewController can be found here https://developer.apple.com/library/ios/documentation/uikit/reference/UITableViewController_Class/Reference/Reference.html

conditions for navigation between viewcontrollers

I am working with the project of ios and doing well in it. But now i stuck at one place where i am having three views (Say 1stview, 2ndview, 3rdview). I am navigating to second view from first view and third view using the code line below.
[self.navigationController pushViewController:first view animated:YES];
How can i check on second view wheather i am navigating from first view or third view. So that i can use particular condition on it.
So please help me out regarding this issue. Your help will be much appreciable.
Take a variable in second View controller. When you are creating the object of it, set proper value into it. Later on when it will get pushed, you can use that value to take proper decisions.
In the file of FirstViewController you will write below lines:
SecondController *controller = [[SecondController alloc]init];
controller.flag = 1; //That means you came here from viecontroller 1
[self.navigationController pushViewController:controller animated:YES];
In the file of ThirdViewcontroller you will write below lines:
SecondController *controller = [[SecondController alloc]init];
controller.flag = 3; //That means you came here from viecontroller 3
[self.navigationController pushViewController:controller animated:YES];
Try to arrange thing so that the 2ndView doesn't know about 1stView or 3rdView, but instead just changes it's behavior according to how it was configured. So, let's say that when you're navigating to 2ndView from 1stView, 2nd should display with a green background and when you get there from 3rd it should use blue instead. Rather than telling 2nd which controller preceded it, have the preceding controller just tell 2nd what background color to use. Same goes for any other aspect of 2ndView's behavior.
The benefit of doing it this way is that you can change 1st or 3rd without having to change anything in 2nd, and you can later add a 4thView or 5thView that also use 2ndView without having to change 2ndView.
Implement the method – navigationController:willShowViewController:animated: from the UINavigationControllerDelegate Protocol Reference. Inside this method you can check the navigation stack to get the current view controller using several properties of UINavigationController. An example would be access the visibleViewController property.
As #Apurv pointed out you need some sort of identifier mechanism to be able to know which view controller the call came from. e.g.: viewController.view.tag

Dismissing the last tab ViewController

I have a UITabBarController to which I add and remove UIViews without any issue in the following way:
Adding
FileTabViewController* newTab = [[FileTabViewController alloc] initWithNibName:#"File" bundle:nil];
NSMutableArray* array = [NSMutableArray arrayWithArray:bar.viewControllers];
[array addObject:newTab];
[bar setViewControllers:array animated:YES];
[bar setSelectedIndex:bar.viewControllers.count-1];
Removing (within FileTabViewConroller.m)
NSMutableArray* newArray = [NSMutableArray arrayWithArray:self.tabBarController.viewControllers];
[newArray removeObject:self];
[self.tabBarController setViewControllers:newArray animated:YES];
This code does exactly as I'd expect but there's on anomaly which I cannot explain. When the app launches and the view controller array is empty (nil) then there are (as you'd expect) no views shown in the tab view controller. As I add views the tabs increase correctly, but when I remove the very last one (i.e. index 0 of count 1) then the view appears to stay loaded, the buttons and labels on the final tab remaining visible - there seems to be no way to remove everything back to the 'nil' state the app launches with - despite the fact that there aren't actually any tabs shown on the controller.
Launch (as you'd expect)
Opening and closing a tab (this is where I get confused) - you see the tab disappears but the view remains open...?
Thanks in advance for any input!
I made a little test app, and I see the same behavior you're seeing. I don't know whether this is an unintended consequence of the way a tab bar controller removes controllers and views, or whether Apple intended for this to happen -- after all, why would you want a black screen? Something seems to be keeping a strong pointer to that last view controller, because if I have a button that removes the controller (and it's the last one), I can still click that button and get the action to happen. The first time I click it, the tab bar controller still lists that controller as the selected controller, but if I click again, the selected controller is nil, but my view controller still lives.
The solution to getting rid of the view is easy, just remove the view from its superview after you remove the last controller from the array.
[self.view removeFromSuperview];
However, this doesn't get rid of the view controller. Something is still holding a reference to it, and so far, I haven't found out what is doing that.
I haven't tested this hypothesis, but I wonder if the `moreNavigationController is coming into play here. Check its value before adding any tabs, and again after removing the last tab. Also take a look at the visibleViewController property of both the moreNavigationController and the UITabBarController.

How should a view controller abort loading / dismiss itself?

I have a view controller which contains a table view, and which is wrapped within a navigation controller, i.e. in the app delegate these two are created and set as:
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:self.myViewController];
self.window.rootViewController = navController;
If the user clicks on a row within a table then another view controller is created and pushed to the navigation controller's stack:
[self.navigationController pushViewController:webPageController animated:YES];
The webPageController loads and reads local files. If a file is missing I want to abort the loading of the webPageController and the displaying of its view and have the table view displayed.
How should I achieve this?
If the webPageController detects a problem I've tried experimenting with it calling various things such as:
[self.navigationController popViewControllerAnimated:YES];
or
[self.navigationController.navigationBar popNavigationItemAnimated:YES];
To pop itself off the navigation stack, however these aren't working, is it wrong for a navigation controller to attempt to pop itself like this? What is the canonical way of implementing this?
Thanks
That should be fine. Where are you calling popViewControllerAnimated:? If you're calling before viewDidAppear, you'll likely run into problems. ViewControllers need to finish the appearing and disappearing before they can make any kind of pop or push to their stack. If you do it before, you get really weird results. The most common symptom of this is it doesn't work. Often buttons inside it will get messed up as well.

Automatically drill down through navigation hierarchy?

I have a navigation-based app that allows the user to drill down through hierarchies. Some of the child hierarchies have only one element in them, e.g.,
TopLevel1----->Level2a
TopLevel2 |->Level2b----->Level3a----->Level4a
|->Level2c
Instead of making the user tap 'Level3a', I just want to jump from Level2b to Level4a, but keep the Level3a view in the stack so when the user backtracks, it is visible.
I found some code here to simulate a row tap:
Simulate a Detail Disclosure Button press
When each level is loaded, I check to see if there is only one element in it. If so, I simulate the row tap. This all works initially, and the final view is loaded. But when I start backtracking through the view hierarchy, I get problems (it appears that the skipped views aren't loaded).
I think what I'm trying to accomplish is fairly simple, so I'm hoping someone on here can point me in the right direction.
You should be able to place a [self.navigationController pushViewController:level4 animated:NO] call in the viewWillAppear method for your level3 view controller. This will automatically push level4 on top of level3.
If it only happens some of the time, level3 can have a property to indicate when this behavior takes place.
I'm not 100% sure that would work, but that's what I would do.
You could directly [self.navigationController pushViewController:level4a animated:NO] and when that's done, set a new array of viewControllers, the navigationController propriety (an array that includes Level3a).
Here is a code sample, in you didSelectRowAtIndexPath:
[self.navigationController pushViewController:level4a animated:NO]; //Push the level 4 first
NSMutableArray* mutableViewControllers = [self.navigationController.viewControllers mutableCopy];
[mutableViewController addObject:level3a atIndex:3]; //Add the level 3 manually
self.navigationController.viewControllers = mutableViewControllers;
[mutableViewControllers release];

Resources