Can two UIViewControllers use a single navigation bar? - iphone

In my iPhone app I am trying to have the allusion of having a single static navigation bar, so the title and buttons don't ever swipe across when switching views.
I can't think a way of doing it (simply at least), so do you have any suggestions? I need to have a static title and buttons up in the nav bar space (even if I don't use the UINavigationBar, but make something custom) so that when I do something such as push a view controller, when it swipes across my nav bar doesn't move and the buttons change function for the new view.
Edit
Ok, I have thought of a possible method. Each of my views have a secondary view in which gold the view contents, except the nav bar objects. Can I override the pop and push methods to just animate this subview on and off screen?

Just do a push as normal but set it to not animate.
i.e.
[self.navigationController pushViewController:newViewController animated:NO];
Then when you want to go back...
[self.navigationController popViewControllerAnimated:NO];
Then you can have two navigation bars but it will look like it's the same bar as it isn't animating.
EDIT TO ALSO ANIMATE IN THE VIEW AND KEEP THE NAV BAR
I'm not sure of the whole flow of the app but if you want to keep the nav bar and swipe the new UI in then you could create a scroll view (with paging) and put the views of each VC on different frames of the scroll view or something.
Why do you want to keep the nav bar still anyway? There is nothing wrong with animating the nav bar and keeping the same buttons etc on it.
Having said that, if you are using different VCs then the nav bar should change anyway to show the details (i.e. title) of the VC you are currently looking at.
ANOTHER MORE RADICAL APPROACH
OK, thinking laterally now :D
How about, you use the not animated push and pop (as above) but instead of just displaying the UI you can animate it in from the relevant side. (A singleton or a VC subclass which you then subclass for your UI could do this for you across all view controllers).
The next problem is that it will look like the UI has gone instantly blank before animating in the new UI so you need to animate out the old UI. This means both UIs (the old and the new) have to be on the screen at the same time.
You can get round this by converting the entire view of the old UI into an image (not hard to do will find a link) and then passing this image into the new VC. The new VC will then instantly display this image and animate it out of the screen at the same time as animating its own UI onto the screen.
Really not as hard to do as it sounds. Especially if you subclass UIViewController ad give it a function animateUI and a property oldUIImage and direction. Then you can override viewWillAppear in this class to do the animation for you. Then the only thing you have to do is give each VC an image and a direction when you push/pop to it.
This is just giving the illusion of what you're after and means you can still keep a fairly simple object model and flow of the app.
...or something.

Just a riff on #Fogmeister's good idea...
In the presenting view controller, get self.view's image by implementing the suggestion here. Then, when it's time to present...
UIImage *image = [self makeImage]; // what it was called in the other post, consider a better name
MyViewController *newVC = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:nil];
newVC.presentationImage = image;
[self.navigationController pushViewController:newVC animated:NO];
In MyViewController, give it a UIImage property called presentationImage, then ...
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
UIImageView *imageView = [[UIImageView alloc] initWithImage:self.presentationImage];
imageView.frame = self.view.bounds;
imageView.tag = 128;
[self.view addSubview:imageView];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
UIImageView *imageView = (UIImageView *)[self.view viewWithTag:128];
[UIView animateWithDuration:0.5 animations:^{
imageView.frame = CGRectOffset(imageView.frame, -self.frame.size.width, 0);
}];
}
FYI - I didn't test or even compile this. Just liked the idea and decided to stretch my fingers.

To do this in a "clean" way, you'd need to abandon UINavigationController and write a custom container controller that does not push new navigation items onto it's navigation bar when pushing a new view controller (or allows you to push the navigation item in a non-animated fashion while animating the push of the view controllers views).
However doing this will take some time. If you decide to do this, i recommend the WWDC Session on UIViewController containment.
Another alternative that springs to my mind is to (by subclassing or method swizzling) alter the behaviour of UINavigationController to push the navigation items non-animated while animating the viewcontroller-push.
I have in no way tested this, but overriding push (and pop respectively) in a subclass like this might work:
- (void)pushViewController:(UIViewController *)vc animated:(BOOL)animated {
[super pushViewController:vc animated:animated];
[self.navigationBar setItems:self.navigationBar.items animated:NO];
}
If this method doesn't work, it might be worth inspection what animations are going on inside the nav bar directly after the call to pushViewController:animated:. Maybe you can cancel some animations to go to the final state directly.

Related

UINavigationBar Above UITableView

I'm programmatically creating a UITableViewController class that shows a table view with a simple navigation bar (though without a UINavigationController, as there are no further levels to the table view hierarchy).
Here is the relevant code:
- (void)viewWillAppear:(BOOL)animated {
UINavigationBar *navBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 44.0f)];
[self.tableView addSubview:navBar];
}
However, the navigation bar covers most of the first table view cell, and scrolls with the whole view.
How can I fixate the navigation bar above the table view, and keep it from scrolling through code?
The problem is that you are using UITableViewController. Switch to a standard UIViewController, add the tableview delegate and datasource methods, point the tableview to those methods, and then you do what you want to do. You could also add a UIToolbar in the XIB and create it that way if you wish.
If you really want a navigation bar, then use an NSNavigationView controller.
When you use a navigation controller, it takes care of this for you, but the navigation bar is just another subview. The remedy is to frame your table view relative to the navigation bar.
self.tableView.frame = CGRectMake(0,myNavBar.frame.size.height, 320, self.view.frame.size.height-myNavBar.frame.size.height);
Set the navigation bar's translucent property to NO:
self.navigationController.navigationBar.translucent = NO;
Having a hierarchy of data doesn't really drive whether or not you need to use a UINavigationController. There are three good reasons to go ahead and just use the UINavigationController.
You get the Navigation Bar for free, and the Nav Controller handles setting the proper frame of your root view controller when you set the nav controller's root view controller property to your view controller
If you one day wake up and say, "Hey! I want to add another layer of information to my awesome app!", you don't need to make any changes to the overall design (or, at most, minimal ones).
As my comment to #danh's suggestion implies, you're immune to whatever whacky changes Apple may decide to do with regards to nav bar height.
The solution to this behavior would be to add the UINavigationBar to the Parent View Controller's view:
[self.parentViewController.view addSubview:myNavBar];

How to create physical menu button (like the one in Android) menu in iPhone?

I am developing a navigation based application in iPhone.
In which I want to create physical menu button like menu like the one in Android phones.
The menu can be accessed from any view in the hierarchy.
Do any one have any idea how can we achieve this .
Can we add a drawer like menu to UINavigationBar ?
Please don't suggest the tabbarcontroller as it is the last option.
What you should do is create a subclass of UIViewController, e.g. MasterViewController. Have all of the view controllers subclass MasterViewController. In the MasterViewController you can write code to create and display your drawer view, and then all of your view controllers will inherit that code and can call it on themselves at any point.
To slide the view in:
drawerView.frame = CGRectMake(0, -1*drawerView.frame.size.height, self.view.frame.size.width, whateverHeight);
[UIView beginAnimations:nil context:NULL];
drawerView.frame = CGRectMake(0, 0, self.view.frame.size.width, whateverHeight);
[UIView commitAnimations];
I think that what you want is just set the titleView of UINavigationItem to an arbitrary UIView to handle user taps.
This doesn't have to be a text label :)
If that's the case then I'd suggest you taking sharedInstance with flags tracking the hierarchy of the view controller stack. Every time when user navigate just update your sharedInstance and do the custom menu preparation and populate it. The good practice would be reuse your UI component rather than making it new on every navigation operation.
You can create a "DrawerView" class and import it into where you will want to use it. Then you can use presentModalViewController: myDrawer to make it appear when you need it. Hope that makes sense.
Its very simple..
In one XIB u can add many views right. now add two main views like say data view and down side button view u want in. (if m nt wronge with 4 buttons to navigate) now keep ur this view at top . and all the other hirarchy of views u can add in that data view.
I think you should create a singleton UIViewController or UIView for this. Advantage of the UIViewController is that it can be presented with the presentModalViewController:animated: method, and also if you need, you can push it to your navigation stack. Also an advantage that you have a UIView instance in the same time.
Using the singleton pattern, you can reach your class anywhere.
Check this link from the official documentation about singleton in objective-c: Creating a Singleton Instance
Or you can use Matt Gallagher's macro (because this is very comfortable to use): Singleton macro
If you can get iOS 5 beta, I highly recommend it. The new beta dev release has an extension on UIViewController, dubbed by Apple as container view controllers. They are very easy to set up, and I am using one in my app.
Container view controllers work like UINavigationControllers and UITabBarControllers in that they allow you to add child view controllers, not just child views. Like I've said, they are simple to set up. I'll explain how:
Create a new class called ContainerController. Add an initialization method called - (id)initWithNavigationController:(UINavigationController *)controller. It's implementation should look something like this:
if ((self = [super init])) {
[self addChildViewController:controller];
controller.view.frame = CGRectMake(0, 0, 320, 420); // Leave room for the menu at bottom of the screen
[self.view addSubview:controller.view];
}
The reason we are adding a navigation controller is because it keeps this container controller from doing transitions, which is more complicated.
In the ContainerController's viewDidLoad method, create the menu:
[super viewDidLoad];
UIView *menuWrapperView = [[[UIView alloc] init] autorelease];
menuWrapperView.frame = CGRectMake(0, 420, 320, 40); // Place a wrapper for the menu buttons directly below the navigation controller
UIButton *button1 = ...;
UIButton *button2 = ...;
[menuWrapperView addSubview:button1];
[menuWrapperView addSubview:button2];
[self.view addSubview:menuWrapperView];
With this approach (using child view controllers), your views and navigation controller will get their needed memory warning, view loading, and rotation handling method calls when needed.

iOS: confused about removeFromSuperview and switching views

New to iPhone development, but I've been given a big project as a first go and I'm a bit stuck.
Basically the app will start with a settings screen, then you click a button to go to a dashboard with multiple option buttons. Each button will lead to a different Navigation View with tables.
The way I've approached this is to start with a UIViewController with a button, which I've got wired up but when you hit the button and I do:
[self.view removeFromSuperview];
UIViewController *newView = [[UIViewController alloc] initWithNibName:#"Dashboard" bundle:nil];
[self.view addSubview:newView.view];
the second view isn't loading. I just get a blank screen. Do I need to make a reference in the first controller to the second?
Also, am I approaching this in the right way? As long as I removeFromSuperview will I be able to load the navigation controllers on the press of a button?
Sorry if this isn't too clear, I've been through books and lots of websites but don't seem to be able to get my head around this.
Thanks
There is nothing here with the new view, rather the problem is with current view. You have removed the self.view from super view.
[self.view removeFromSuperview];
So anything added to self.view will not be shown, as self.view itself is removed.
When presenting child controller/view from a parent controller, you should consider using presentViewController. Eventually, use dismissViewControllerAnimated when you want child to disappear and parent to reappear.
In parent view controller:
ChildViewController * child = [[ChildViewController alloc] init];
[self presentViewController:child animation:YES completion:Nil];
In child view controller, ie. in some action handler:
-(IBAction)close:(id)sender
{
[self dismissViewControllerAnimated:YES completion:Nil];
}
IMHO you should also get in the habit of naming instance variables to what they are instantiated from. In your example you name the instance newView, when it should be something like newViewController. That way you make sure you don't mix up views with view controllers.
[self.view removeFromSuperview];
You've removed the view from the superview
[self.view addSubview:newView.view];
But you're adding the new view to the same view that you have just removed from the superview. It's not displaying anywhere.
Your third line adds newView as a subview of self.view, but you just removed self.view from it's superview.
I'd suggest reading more about view controllers. You'll want to have one view controller per "screen", so one for your settings screen, one for your dashboard, one for each table, and so on. Then, manage which one is visible by pushing and popping these view controllers from the nav controller's stack.
This removes self.view, which will most likely destroy the object since there will be no other references to it:
[self.view removeFromSuperview];
Here you are creating an UIViewController, and adding it's view to self.view, which is probably not what you want:
UIViewController *newView = [[UIViewController alloc] initWithNibName:#"Dashboard" bundle:nil];
[self.view addSubview:newView.view];
Look into UINavigationController so that you can easily swap screens in and out with some built in animations. Here's a bit more about them. Here's a tutorial.
The UIViewController's view should not be removed from or added to a view hierarchy outside the control of the view controller. While you might be able to get that manipulation to work now it won't in the future.
Read up on view controllers here.
The basic idea is that you present the view controller then it will take care of manipulating the view hierarchy for you.
So a better approach to get started would be to do something like this;
[viewController1 presentModalViewController:viewController2 animated:YES];
This line of code will present viewController2 with the default modal animation (slide in from the bottom). If you'd like a different animation you can change the modalPresentationStyle to one of the constants in the UIModalPresentationStyle enum on viewController1 (note thats a viewController1, not viewController2).
If you want something more like the Clock app look into the tab bar controller. If you want something more like the Mail app look into the navigation controller.

Full screen UIImage view

I have an application with a navigation bar and a tab bar. A user can navigate to a view which displays images in a scroll view. I'd like to have the same behavior as the iPhone photo app: Nav bar at the top, tool bar at the bottom, which will hide or show based upon a tap.
I'm moving my view to the window object in order to achieve full screen mode. This works fine:
myView = [self.view retain];
self.view = nil;
[window addSubview:myView];
But when I want to redisplay the Nav & tool bar, I run into a problem. The bars show fine, but the view is empty, and I can't seem to add any content to the view:
[myView removeFromSuperview];
self.view = myView;
I got a lot of good info from this post
but can't quite get the right combination.
By simply setting the controller's view, you aren't adding it as a subview to anything else, so it will never appear.
Moving views around like this can get a little tricky. I recommend that you not move the view from one to the other, but instead have two UIViews. Add second UIView to the window's subview and set it to hidden=YES initially. When you want to show it, set the image for the UIImageView, and then set the hidden property to NO.
what's wrong with just using setNavigationBarHidden: animated: and setToolbarHidden:animated:?

iPhone SDK: Advancing from one view to another using a button tap

I want to make a really simple iphone app: one screen with a single button... when the button is tapped a new screen appears. That's it. No animations, nothing,
I've tried endlessly to make the NavBar sample project do this... and it works but only if I use a UINavigationController with a table that I can tap etc. I've tried all the skeleton projects in XCode too.
I thought I was done when I did this:
[[self navigationController] presentModalViewController:myViewController animated:YES];
But I couldn't do it without the UINavigationController. I just want a simple example.
Thanks so much!
One way you could do this is to create a new UIView and then when the button is pressed add that new UIVIew as a subview, therefore making it what you see.
If you make the new view its own subclass of UIView you would do something like this.
LoginView *login = [[LoginView alloc] initWithFrame: rect];
[mainView addSubview: login];
[self presentModalViewController:myViewController animated:NO];
Will pop up a new view, no animations, nothing. To get rid of it, inside myViewController:
[self dismissModalViewControllerAnimated:NO];
Though I reccomend you use the nice sliding animations (change NO to YES.) And yes, you can stack them up. I think this is better than creating a new UIView, but I may be wrong.
The correct way to do this is set up your project with a UINavigationController. In your root view controller, add your button in the view controllers's view. Then in viewDidLoad, register for UIControlEventTouchUpInside events from you button. Then, in your event callback, call:
[self.navigationController pushViewController:[[[SecondViewControllerClass alloc] initWithNib:nibName bundle:nil] autorelease]];
What kdbdallas suggested will work, but you won't get the nice sliding effects, nor will the navigation bar automatically change and provide your users with a back button.