Automatically drill down through navigation hierarchy? - ios

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];

Related

Switching between UIViewControllers in story board

As someone who usually used separate xibs in the past I thought I'd give storyboard a go as it seemed a lot simpler to use and much easier to develop with. I've been writing an application where the essential set up is this:
At the top of all this is a UINavigationController (first level). Then I have Multiple UIViewControllers (second level) with buttons in them which you can tap to switch between the second level UIViewControllers.
However a problem occurs when I start switching between the second level UIViewControllers. I first thought this was an initialisation problem with the NSMutableArrays because in my code I have a NSTimer set to loop periodically and found when I set a breakpoint during it, when I went forward to the next timer tick event there appeared to be different instances of the same NSMutableArrays and it seemed a gamble to try and insert new values into these array with it sometimes working, sometimes not (as it may or may not insert into the correct instance).
Then, looking at the memory usage under Debug Navigator I found the issue. Each time I "switched" between the UIViewControllers a new UIViewController was being initiated, along with all new variables.
The code I am using to switch between them is
-(void) perform {
[[[self sourceViewController] navigationController] pushViewController:[self destinationViewController] animated:NO];
}
Or essentially a push segue transition. This also explains why when I tried to switch back to my view, the data on that view was lost as it is a complete new view.
Does anyone know how to switch between multiple ones of these UIViewControllers (say 5) essentially like a UITabViewController would except without the tab bar being present?
First option you can do this: You can use a tabbarcontroller for switching viewcontroller and hidden the tabbar. Then on buttonclick setthe tabbar index.
Second option you can do this: Create one more view controller and in this viewcontroller subview the all switching viewController and when you want to switch viewcontroller just bring that viewcontroller view to front by delegate.
Do you need the navigation bar and other features provided by your top level navigation controller?
If not, you could use a UIPageViewController instead.
You set up all your second level view controllers and then just have to tell the page view controller which one to display.
If you implement the associated delegate methods, it will automatically provide swipe gestures to switch between them and nice animations to get them on and off screen.
You can also get it to put a UIPageControl at the bottom showing a dot for each VC with the dot for the current VC highlighted.

How do I keep views alive like UITabBar applications?

I am creating an app with different views as you can see in this screenshot.
However when I tap on "Transitions" it just loads the view again with viewDidLoad it does that every time so it always resets upon opening. But when you have a tabbed application it just loads it with viewDidLoad then whenever you open it again it just loads viewDidAppear since it already loaded.
Does anyone know how I can keep the controller alive like in a tabbed application ?
*Image of tabbed application - http://s1.ibtimes.com/sites/www.ibtimes.com/files/styles/v2_article_large/public/2013/06/18/ios-7-notonthisphone-2.PNG
Here's how the view loads now.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *menuItem = self.menuItems[indexPath.row];
// This undoes the Zoom Transition's scale because it affects the other transitions.
// You normally wouldn't need to do anything like this, but we're changing transitions
// dynamically so everything needs to start in a consistent state.
self.slidingViewController.topViewController.view.layer.transform = CATransform3DMakeScale(1, 1, 1);
if ([menuItem isEqualToString:#"Transitions"]) {
self.slidingViewController.topViewController = self.transitionsNavigationController;
} else if ([menuItem isEqualToString:#"Settings"]) {
self.slidingViewController.topViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"MESettingsNavigationController"];
}
[self.slidingViewController resetTopViewAnimated:YES];
}
Decided to share my files with all of you if someone is an expert in this (clearly I'm not one of them) : http://www.mediafire.com/download/1q6n01514bf78jo/Sldiing_App.zip
Well, keep them.
Maintain some container (array or individual properties/ivars etc) and keep references to them. Then, when the user tapps on "transitions", do not re-create the views and their controllers but put them back into place.
You may need to maintain each view controllers sub-view-controllers call hierarchy and manipulate the navigation controllers navigation stack (see docs for the viewControllers property of UIViewController).
It is a bit of work though and does not come for free.
Alternatively, if you do not have a navigation controller involved or do not need to stack more view controllers on top of your bunch of view controllers that you want to keep alive, then you could just try to move them off the screen (To negative coordinates or beyond the windows' boundaries. But don't use constants for that. We all don't know what screen sizes future devices will have. Fetch the boundaries from the actual window on runtime.)
Whenever you're tapping on "transitions" I assume you're doing an alloc/init of your view controller and then pushing it to whatever view stack you have for the main view. Instead, alloc/init a global view controller when your side menu loads, and push that onto your stack when you tap on "transitions" instead of creating a new one every time (this assumes that your menu persists and isn't loaded every time it comes up as well)

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.

pushing a view into stack below current root view

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.

Resources