So, I am trying to achieve having a fixed top banner on Application UIWindow and a working navigation of multiple view controllers. I know there are other ways of performing this but for my project requirements I need to use xibs.
The code below allows to achieve this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.headerView = [[UIImageView alloc] init];
self.headerView.frame = CGRectMake(0.0f, 20.0f, 320.0f, 44.0f);
self.headerView.image = [UIImage imageNamed: #"header-banner-background.png"];
[self.window addSubview:self.headerView];
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
self.window.rootViewController = navigationController;
[self.window makeKeyAndVisible];
return YES;
}
Then in each ViewController viewWillAppear method I have resized both self.view and self.navigationController.navigationBar.frame in order to show themselves below the banner:
self.navigationController.navigationBar.frame = CGRectMake(0, 64, 320, 44);
self.view.frame = CGRectMake(0, 64, 320, 396);
This is fine, works exactly the way I wanted.
But if the user presses the home button and the application resigns active, when it becomes active again the last viewController seen get's displayed but without the UIWindow banner, and the self.view and navigationBar are displayed on top.
Of course the current UIViewController viewWillAppear method is not called when the application returns active, but even if I put the resize code in another function and call it through a notification it does not work. But when the user presses a bar item and moves in the navigation stack, then everything works again and UIWindow displays the headerView.
Anyone understands why the UIWindow headerView disappears when application returns active?
EDIT 1: Possible solution?
#interface RootViewController ()
{
ViewController *viewController;
UINavigationController *navigationController;
}
#end
#implementation RootViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
}
return self;
}
- (void)loadView
{
// Implement loadView to create a view hierarchy programmatically, without using a nib.
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 88)];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
[imageView setImage:[UIImage imageNamed:#"header"]];
[view addSubview:imageView];
navigationController.navigationBar.frame = CGRectMake(0, 44, 320, 44);
[view addSubview:navigationController.navigationBar];
self.view = view;
}
EDIT 2: Second possible solution, still not working
- (void)loadView
{
NSLog(#"loadView root");
// Implement loadView to create a view hierarchy programmatically, without using a nib.
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 88)];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
[imageView setImage:[UIImage imageNamed:#"banner"]];
[view addSubview:imageView];
navigationController = [[UINavigationController alloc] init];
navigationController.navigationBar.frame = CGRectMake(0, 43, 320, 44);
[view addSubview:navigationController.navigationBar];
[self addChildViewController:navigationController];
self.view = view;
[self.view setBackgroundColor:[UIColor yellowColor]];
}
This is the result, which seems fine, without the push of the viewController1 (note the yellow background is from the rootViewController):
This instead is the result of adding:
- (void)viewDidLoad
{
viewController1 = [[ViewController1 alloc] initWithNibName:#"ViewController1" bundle:nil];
[navigationController pushViewController:viewController1 animated:YES];
}
The new view controller is not pushed (the background should be black) and the navigation bar has disappeared.
EDIT 3: Olloqui solution, still not working
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[self.window setBackgroundColor:[UIColor redColor]];
UIView *bannerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
[self.window addSubview:bannerView];
UIView *navigationView = [[UIView alloc] initWithFrame:CGRectMake(0, 44, 320, 416)];
ViewController1 *viewController1 = [[ViewController1 alloc] initWithNibName:#"ViewController1" bundle:nil];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:viewController1];
self.navigationController.navigationBar.frame = CGRectMake(0, 0, 320, 44);
[navigationView addSubview:self.navigationController.navigationBar];
[self.window addSubview:navigationView];
self.window.rootViewController = self.navigationController;
self.window.autoresizesSubviews = YES;
[self.window makeKeyAndVisible];
return YES;
}
Result is I have only the banner, viewController1 is pushed but no navigation bar is displayed. I really need to figure this out, anyone has a different approach?
The window expects to have a root view controlled by its rootViewController property. When returning from the background the window only asks for the rootViewController to restore the appearance of that view controller's view. It doesn't ask all subviews to refresh.
To fix this I would create a new root view controller. This would have the banner and the navigation controller. That navigation controller would then hold your normal view controllers and manage the navigation. The banner will appear properly upon the app returning from the background. Your banner will be stationary while navigation occurs within the navigation controller.
Edit: I whipped up a little example program.
I started with a simple single view app template. I added two view controllers for the content. The just have labels for differentiation, and one has a button that loads the second view.
All the magic happens in the main view controller's viewDidLoad method:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
OneViewController *oneVC = [[OneViewController alloc] initWithNibName:#"OneViewController" bundle:nil];
self.navController = [[UINavigationController alloc]initWithRootViewController:oneVC];
self.navController.view.frame = CGRectMake(0.0, 64.0, 320.0, 416.0);
[self.view addSubview:self.navController.view];
[self addChildViewController:self.navController];
}
The nib for this view loads the image view for the banner.
The example project in on Bitbucket.
I have never tried it before but I would take a shot to create 2 views in the UIWindow, one for the banner and the other one for the navigation + content. Then, insert the navigationBar view in this second view, that will be sized properly. I am not sure if the navigation is going to try to take the full window are, but resizing the navigation controller on each viewWillAppear doesnt look a good solution to me. Try it and comment the result!
I think you can try to workaround this problem by adding the banner view to window.rootViewController.view like this:
[window.rootViewController.view addSubview:bannerView]
Ok, I really needed to solve this ASAP, therefore I opted for a solution I was lucky enough to find here. I still have to solve the management of the UITabBarController added on bottom but the rest seems to be working fine. Thanks everyone for help and additional hints.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[self.window setBackgroundColor:[UIColor redColor]];
// Create a view for the banner add the banner to this view
UIImageView *bannerImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 45)];
bannerImageView.image = [UIImage imageNamed:#"banner"];
// put this banner above the nav bar
UIView *bannerView = [[UIView alloc] initWithFrame:CGRectMake(0, -44, 320, 44)];
[bannerView addSubview:bannerImageView];
//add the tabBarController as a subview of the view
UITabBarController *tabBarController = [[UITabBarController alloc] init];
[tabBarController.view addSubview:bannerView];
// Move the root view to show status bar & banner
tabBarController.view.frame = CGRectMake(0, 20+44, 320, 480-20-44);
//add the modified logo view to window
[self.window addSubview:tabBarController.view];
ViewController1 *viewController1 = [[ViewController1 alloc] initWithNibName:#"ViewController1" bundle:nil];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController1];
tabBarController.viewControllers = [NSArray arrayWithObjects:navigationController, nil];
//tabBarController.tabBar.hidden = YES;
[self.window setRootViewController:tabBarController];
[self.window makeKeyAndVisible];
return YES;
}
When click button, a popoverView embedded in NavigationView appear.
My code is as follows:
-(IBAction)myStuffPOP:(id)sender
{
if(myStuffListViewController ==nil)
{
myStuffListViewController = [[MyStuffListViewController alloc] init];
}
UINavigationController *navcontroller=[[[UINavigationController alloc] initWithRootViewController:myStuffListViewController] autorelease];
// Here we create popover controller.
mystuffPopoverView = [[UIPopoverController alloc] initWithContentViewController:navcontroller] ;
CGRect popoverRect = [self.view convertRect:[sender frame] fromView:[sender superview]];
popoverRect.size.width = MIN(popoverRect.size.width, 100);
popoverRect.size.height = 40;
[mystuffPopoverView presentPopoverFromRect:popoverRect inView:self.view permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
[navcontroller setNavigationBarHidden:NO];
}
In NavigationView of PopoverView :
1st subView ---> 2nd SubView ---> 3rd Subview
If I touch 3rd SubView, PopoverView disappears.
My Question is :
When I click button again, I wanna to see lastest View - 3rd SubView not 1st SubView.
Declare UINavigationController *navcontroller; in .h file
Create only onсe navcontroller with your root view controller,
in viewDidLoad method, for example.
- (void)viewDidLoad {
myStuffListViewController = [[MyStuffListViewController alloc] init];
navcontroller=[[UINavigationController alloc] initWithRootViewController:myStuffListViewController]; }
Show UIPopoverController with exist navigation stack
-(IBAction)myStuffPOP:(id)sender {
//Here we create popover controller.
mystuffPopoverView = [[UIPopoverController alloc] initWithContentViewController:navcontroller] ;
CGRect popoverRect = [self.view convertRect:[sender frame] fromView:[sender superview]];
popoverRect.size.width = MIN(popoverRect.size.width, 100);
popoverRect.size.height = 40;
[mystuffPopoverView presentPopoverFromRect:popoverRect inView:self.view permittedArrowDirections:UIPopoverArrowDirectionUp
animated:YES];
[navcontroller setNavigationBarHidden:NO]; }
Don't forget to release objects
- (void)dealloc {
[myStuffListViewController release];
[navcontroller release];
}
No need to create UINavigationController every time. Save it the same way as MyStuffListViewController
if ( myNavigationController == nil )
myNavigationController = [[[UINavigationController alloc] initWithRootViewController:myStuffListViewController] autorelease];
I want to show the subview of a view in popover.
To elaborate,
I have a mainViewController.
I hava a subview 'songListView' in this mainViewController.
I have a button titled 'list' in the mainViewController' itself.
I want to show the songListView in popover on clicking the button 'list'.
So, how should I do this.
You could use the below code as a reference for showing PopOver from a UIButton
-(void) buttonAction:(id)sender {
//build our custom popover view
UIViewController* popoverContent = [[UIViewController alloc]
init];
UIView* popoverView = [[UIView alloc]
initWithFrame:CGRectMake(0, 0, 200, 300)];
popoverView.backgroundColor = [UIColor greenColor];
popoverContent.view = popoverView;
//resize the popover view shown
//in the current view to the view's size
popoverContent.contentSizeForViewInPopover =
CGSizeMake(200, 300);
//create a popover controller
self.popoverController = [[UIPopoverController alloc]
initWithContentViewController:popoverContent];
//present the popover view non-modal with a
//refrence to the button pressed within the current view
[self.popoverController presentPopoverFromRect:popoverButton.frame
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
//release the popover content
[popoverView release];
[popoverContent release];
}
My problem is solved.
I just created another View Controller class, i.e. 'TempPopoverView'.
Then I set the view of this TempPopoverView equal to the subview of my MainView
Here is the code snippet from my code:
TempPopoverView *viewController = [[TempPopoverView alloc]initWithNibName:#"TempPopoverView" bundle:nil];
[self. songListView setHidden:NO];//songListView is subview of MainView
viewController.view=self. songListView;
UINavigationController *navCont = [[UINavigationController alloc]initWithRootViewController:viewController];
navCont.navigationBar.tintColor = [UIColor colorWithRed:.102 green:.102 blue:.102 alpha:1];
[self showPopOverController:navCont andFrame:sender.frame andInView:self.view];//self.view is the MainView
[viewController release];
[navCont release];
U can use presentModalViewController on your main view.
For example
SongListView *songList = [[SongListView alloc] init];
[self presentModalViewController: songList animated: YES];
There are different animations possible as well.
as simple as you think to add subview in self.view
[popoverController.contentViewController.view addSubview:yourselfobject];
A view is presented modally:
[self presentModalViewController:modalNavController animated:YES];
This view uses a UITabBarController with 4 elements. One of these elements, "Info" has a button that's only visible if its available. If the button is clicked, it needs to push to another view controller, but I'd also like to maintain the tab bar from it's parent view. I haven't been able to figure out how to do this with or without keeping the tab bar. Ive tried pushing and presentingModally in all the places that I could image. How should this be done properly?
Creating tab bar:
infoController.title = #"Info";
streetViewController.title = #"Street View";
reviewController.title = #"Reviews";
streetViewController.tabBarItem.image = [UIImage imageNamed:#"flag.png"];
infoController.tabBarItem.image = [UIImage imageNamed:#"openMarker.png"];
reviewController.tabBarItem.image = [UIImage imageNamed:#"reviews.png"];
UITabBarController *tabBarController = [[UITabBarController alloc] init];
tabBarController.view.frame = CGRectMake(0, 0, 320, 460);
UINavigationController *infoNC = [[[UINavigationController alloc] initWithRootViewController:infoController] autorelease];
infoNC.navigationBarHidden = YES;
[tabBarController setViewControllers:
[NSArray arrayWithObjects:infoNC, streetViewController, reviewController, nil]];
[self.view addSubview:tabBarController.view];
When you add the view controllers to the tab bar controller you need to do this:
MyCustomViewController *vc1 = [[MyCustomViewController alloc] initWithNibName:nil bundles:nil];
UINavigationController *nc1 = [[[UINavigationController alloc] initWithRootViewController:recipesRootView] autorelease];
[vc1 release];
then add nc1 instead of your custom view.
Then in MyCustomViewController to push another view controller do:
[self.navigationController pushViewController:(UIViewController *)page animated:YES];
That should work for you, and keep the tab bar controller.
I'm getting this gap at the bottom of my window after I add the view of a custom UIViewController. The gap goes away as the view is shifted down after I move to another view and then back.
Here is the code in the app delegate:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after application launch
CustomViewController *gvc = [[CustomViewController alloc] init];
self.customViewController = gvc;
[gvc release];
[window addSubview:customViewController.view];
[window makeKeyAndVisible];
}
"CustomViewController" is used as a root view controller to simply coordinate which other UIViewControllers to display. As such I simply set its view = to that of the first ViewController's view needed like so:
- (void)loadView {
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
self.view = v;
[v release];
// Add the HomeViewController to the top of the stack
MainViewController *mvc = [[MainViewController alloc] initWithNibName:#"MainView" bundle:nil];
mvc.delegate = self;
self.mainViewController = mvc;
[mvc release];
[self.view addSubview:self.mainViewController.view];
}
I've tried a bunch of things including what you see here with no luck. Any ideas?
thanks
This line
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
should be ...
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0, 20, 320, 460)];
Good times.