I've seen this question asked around before, and found an answer for how to do this in a simple view. But... when I go to a subsequent view pushed onto the view stack, manually setting the titleView doesn't work out. The titleView view gets pushed off to the right while the back button and its text take over the left half of the UI.
Is there a standard way to do this? I've noticed the Gowalla app apparently does it quite well. I've tried a multitude of approaches including categories, subclasses, etc and haven't had any luck.
Every UIViewController has it's own navigationItem, which (potentially) has a titleView. When you push and pop view controllers in a navigation control, the parts of the navigationItem are what you are seeing. If you wanted a custom title color, you could very easily do something like the following in each of your view controllers.
- (UINavigationItem *)navigationItem
{
UINavigationItem *navigationItem = [super navigationItem];
UILabel *customLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320.0f, 44.0f)];
customLabel.text = #"My Title";
customLabel.textColor = [UIColor purpleColor];
navigationItem.titleView = customLabel;
[customLabel release];
return navigationItem;
}
Related
As I have told you many times, I'm new to iOS development.
I've learned something about views, view controllers, views hierarchy and stuff like that, and I'm thinking about one thing.
Assume that I want to create some app using some tab section, some table section, drawing canvas with context menu (quartz2D) etc, and I would like to have all these things visible at a time (static application) and can ensure some interaction between all these sections (changing something in the canvas results in some changes in the table, editing some values via context menu on canvas results in drawing a charts in tab pane and so on...).
The question is, if it is efficient to put all views on one screen and have some viewcontroller inside viewcontroller inside viewcontroller inside v...
How should I manage all particular views (with controllers) assuming that I have to present all of them at a time during entire application workflow?
/* PS. I'm new to StackOver as well, so let me know if such basic and not concrete questions like that one, are unwelcome */
You can't add ViewController inside a ViewController , you have one view Controller and on it's View you add SubViews Buttons, labels,...etc.
You can iterate on the subViews by get self.view.subViews
If you have a complex view, please make it as CustomView that inherits from UIView to make the code readable and well organized.
UIView *v1 = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 270, 400)];
v1.backgroundColor = [UIColor redColor];
UIView *innerView = [[UIView alloc] initWithFrame:CGRectMake(10, 20, 240, 370)];
innerView.backgroundColor = [UIColor greenColor];
[v1 addSubview:innerView];
[self.view addSubview:v1];
for (UIView *subview in self.view.subviews)
{
//You can get all subView object that You added
}
I'm adding a series of buttons to a UINavigationBar using:
NSArray *items;
items = [NSArray arrayWithObjects:
fixedSpace,
refreshStopBarButtonItem,
flexibleSpace,
self.backBarButtonItem,
flexibleSpace,
self.forwardBarButtonItem,
flexibleSpace,
self.actionBarButtonItem,
fixedSpace,
nil];
UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, toolbarWidth, 44.0f)];
toolbar.items = items;
toolbar.tintColor = [[UIColor whiteColor] colorWithAlphaComponent:1.0];
toolbar.autoresizingMask = UIViewAutoresizingFlexibleHeight;
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:toolbar];
All working well.
However when I rotate to landscape mode the toolbar within the uinavigationbar doesn't rotate.
Adding this code (found on SO) causes the toolbar to resize but not the buttons within it, so they are partially cropped at the bottom and no longer lines up with the toolbar background
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
CGRect navigationToolbarFrame = self.navigationController.navigationBar.frame;
CGRect customToolbarFrame = CGRectOffset(navigationToolbarFrame, 0.0, navigationToolbarFrame.size.height);
[UIView animateWithDuration:duration animations:^{
//self.toolbar.frame = customToolbarFrame;
// FAILS!!!
//self.navigationItem.rightBarButtonItem.toolbar.frame = customToolbarFrame;
// FAILS!!!
}];
}
What is the correct way to address the toolbar within the uinavigationbar?
Something like...
self.toolbar.frame = customToolbarFrame;
Or do I have to specify a autoresizemask for the UIBarButtonItems?...
self.backBarButtonItem.autoresizingMask = UIViewAutoresizingFlexibleHeight;
... trying to do so like this fails
Very curious because this code rotates toolbar fine when I include it in my code. No problem rotating the toolbar.
I assume your view controller is responding to shouldAutorotateToInterfaceOrientation? Could you include screen snapshot of what you're seeing?
Are you doing any UIToolbar category/subclass to eliminate its border? (I just subclass with empty (void)drawRect:(CGRect)rect to get rid of border, but I tried both that and the standard UIToolbar and both rotated fine.) Anyway, if you're doing subclass/category of UIToolbar, please include that code?
Also, you could alternatively just use iOS 5's rightBarButtonItems and bypass the toolbar altogether, e.g. self.navigationItem.rightBarButtonItems = items; will then add the array of UIBarButtonItem objects to the navigation bar.
This is a bit of a long shot, but how is your view controller being loaded? Some people try bypassing presentViewControllerAnimated and/or pushViewController and instead simply create a view controller, grab its view, add it as a subview of the previous view controller's view. Unfortunately, this ends up with a disconnect between the view controller hierarchy and the view hierarchy, and according to WWDC 2011 session 102 on view controller containment, this can prevent rotation events from being transmitted correctly. Make sure you're using presentViewControllerAnimated or pushViewController if this isn't your root view controller.
I don't do any of that willAnimateRotationToInterfaceOrientation or subsequent code, just the simple UIToolbar and it works fine during rotation, so I wonder if the problem rests elsewhere.
I am trying to add a the UINavigationController UIToolbar to the top of the view, (under the navigation controller.
My view is a UITableViewController... so there is that to deal with. Currently I am just positioning the UIToolbar that appears at the bottom of the view where I want it to display using
[self.navigationController.toolbar setFrame:CGRectMake(0, 60, 320, 30)];
this positions the toolbar in the correct place I would like it to appear, However there is a problem with where its positioned, which I will explain.
When you set a UINavigationController toolbar to be displayed it puts itself at the bottom of the view and pushes the UITableView up so the toolbar does not cover the tableview. However when I change the position of the toolbar the tableview still thinks the toolbar is at the bottom of the screen meaning the toolbar does not meet flush at the bottom of the screen how I would like it too.
So my question is how can I get the toolbar to display directly below the navigation controller bar and push the tableview down abit to accommodate for the toolbar in its new position.
I hope this all makes sense, Any help I would like to than in advance and below is the current code I am using (all be it basic I am still abit perplexed about whats going on behind the scenes for this to happen).
- (void) viewDidLoad
{
//..
[self.navigationController setToolbarHidden:NO animated:YES];
[self.navigationController.toolbar setFrame:CGRectMake(0, 60, 320, 30)];
self.navigationController.toolbar.tintColor = [UIColor lightGrayColor];
//..
}
update
this is currently what the toolbar is doing to my tableview
Call setFrame on your UITableView to move it into positon.
float y = self.navigationController.toolbar.frame.origin.y + self.navigationController.toolbar.frame.size.height;
[myTable setFrame:CGRectMake(0, y, self.view.frame.size.width, self.view.frame.size.height-y)];
I have the following snippet of code:
- (void)loadView {
self.view = [[UILabel alloc] initWithFrame:CGRectMake(20.0, 20.0, 40.0, 40.0)];
self.view.backgroundColor = [UIColor blackColor];
}
If I run the snippet in a project created using the 'view-based application' in the main view controller everything works as expected. However, If I run it in a project created using a 'navigation-based application' the label fills the screen (does not respect the sizes). How can I fix the behaviour for presenting when in a navigation controller? Thanks!
Navigation view controller will resize the view of the view controller to the display area, so the behavior you describe is normal and by-design. Generally, we use empty UIView at the top of hierarchy, and put the subviews inside, which is UILabel in your case.
I'm trying to do something that shouldn't be that complicated, but I can't figure it out.
I have a UIViewController displaying a UITableView. I want to present a context menu when the user press on a row. I want this to be a semi-transparent view with labels and buttons.
I could use an AlertView, but I want full control on the format of the labels and buttons and will like to use Interface Builder.
So I created my small view 250x290, set the alpha to .75 and create a view controller with the outlets to handle the different user events.
Now I want to present it.
If I use presentModalViewController two (undesired) things happen
1) the view covers all of the screen (but the status bar).
2) It is semi-transparent, but what I see "behind" it its not the parent view but the applications root view.
Ive tried adding it as a subview, but nothing happens, so Im not doing something right:
RestaurantContextVC* modalViewController = [[[RestaurantContextVC alloc] initWithNibName:#"RestaurantContextView" bundle:nil] autorelease];
[self.view addSubview:modalViewController.view];
Is it possible to do what I want?
Thanks in advance.
Gonso
I'm coding similar thing. My approach include.....
Not using dismissModalViewControllerAnimated and presentModalViewController:animated.
Design a customized full sized view in IB. In its viewDidLoad message body, set the background color to clearColor, so that space on the view not covered by controllers are transparent.
I put a UIImageView under the controllers of the floating view. The UIImageView contains a photoshoped image, which has rounded corners and the background is set to transparent. This image view serves as the container.
I uses CoreAnimation to present/dismiss the floating view in the modal view style: (the FloatingViewController.m)
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setBackgroundColor:[UIColor clearColor]];
[UIView beginAnimations:nil context:nil];
[self.view setFrame:CGRectMake(0, 480, 320, 480)];
[UIView setAnimationDuration:0.75f];
[self.view setFrame:CGRectMake(0, 0, 320, 480)];
[UIView commitAnimations];
}
wangii
Thats pretty much the solution I found.
I load the view with loadNibNamed and then just add it on top with addSubView, like this:
//Show a view on top of current view with a wait indicator. This prevents all user interactions.
-(void) showWaitView{
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"WaitView" owner:self options:nil];
#ifdef __IPHONE_2_1
waitView = [ nibViews objectAtIndex: 0];
#else
waitView = [ nibViews objectAtIndex: 1];
#endif
CGFloat x = self.view.center.x - (waitView.frame.size.width / 2);
CGFloat y = self.view.center.y - (waitView.frame.size.height / 2);
[waitView setFrame:CGRectMake(x,y,waitView.bounds.size.width,waitView.bounds.size.height)];
[self.view addSubview:waitView];
}
Could you elaborate on points 3 and 4?
What I did to give the view the round rect aspect is put it inside a round rect button.
This code will actually allow you to have a small floating view, but if the view is smaller that its parent, the user could interact with the visible part of the parent.
In the end I create my view with the same size, but kept the code just in case.
Gonso
I would strongly consider using a navigation controller to slide in your subview instead of overlaying it. This is the expected model and any small benefit you may think you'll get by doing it your own way will be greatly offset by the principle of (least) surprise.
If you really really have to do it this way, I believe the trick is to add the first table view as a subview of a transparent "holding" view that the view controller maintains. Then add your new sub view as another subview of that.
Again, if you really want to do this, instead of adding a transparent "holding" view, since this pop-up is essentially modal, I would make it a subview directly of the window.
You might want to put in a transparent black shield behind it to prevent touches on the background and focus input on the popup.
But seriously, consider either popping a controller on the stack or using that alert view. Unless you've hired a $$ designer, it's probably not going to look appropriate on the iPhone.
What I did was create a UIViewController on top of my UINavigation controller in my app delegate and made it a property of a singleton object for convenience:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//--- create root navigation controller
self.window.rootViewController = self.navigationController;
//--- create view controller for popups:
popupViewController = [[BaseViewController alloc] init];
popupViewController.view.backgroundColor = [UIColor clearColor];
popupViewController.view.hidden = true; //for rendering optimisation
[self.window addSubview:popupViewController.view];
[AppState sharedInstance].popupViewController = self.popupViewController;
//--- make all visible:
[self.window makeKeyAndVisible];
return YES;
}
At any point in my app, I can then call e.g.
MyViewController * myVC = [[UIViewController alloc] init];
//... set up viewcontroller and its view...
// add the view of the created view controller to the popup view:
[AppState sharedInstance].popupViewController.view.hidden = false;
[[AppState sharedInstance].popupViewController.view addSubview:myVC.view];
The BaseViewController used on the top just inherits from UIViewController and sets up a full-screen view:
//----- in BaseViewController implementation
- (void)loadView {
//------- create root view:
CGRect frame = [[AppState sharedInstance] getScreenFrame];
rootView = [[VCView alloc] initWithFrame:frame];
rootView.backgroundColor = [UIColor whiteColor];
self.view = rootView;
}