UINavigationController not working under ARC in iPhone - iphone

I have created a new project "Empty Application" template in Xcode 4.3, it is having only two classes AppDelegate.h & .m
I checked with ARC to use automatic reference count while creating the app.
I added two new files "RootViewController" & "NewProjectViewControllers".
I implemented code to set navigation controller as follows in AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
rootViewController = [[MainViewController alloc] init];
UINavigationController *navigation = [[UINavigationController alloc] initWithRootViewController:rootViewController];
[self.window addSubview:navigation.view];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
and in hte home view (Root view controller) implemented as follows
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = #"Projects";
UINavigationBar *navigationBar = [self.navigationController navigationBar];
[navigationBar setTintColor: [UIColor colorWithRed:10/255.0f green:21/255.0f blue:51/255.0f alpha:1.0f]];
//To set the customised bar item
UIButton *rightBarBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[rightBarBtn setBackgroundImage:[UIImage imageNamed:#"plus_new.png"] forState:UIControlStateNormal];
rightBarBtn.frame=CGRectMake(0.0, 100.0, 30.0, 30.0);
[rightBarBtn addTarget:self action:#selector(addProject) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem* rightBarItem = [[UIBarButtonItem alloc] initWithCustomView:rightBarBtn];
self.navigationItem.rightBarButtonItem = rightBarItem;
// Do any additional setup after loading the view from its nib.
}
- (void) addProject
{
NewProjViewController *editProject = [[NewProjViewController alloc] init];
[self.navigationController pushViewController:editProject animated:YES];
NSLog(#"xxxxxxxxxxxxxxx");
}
But since i used ARC the navigation may dealoc immediately and it doesn't work, All the actions in method works except push to the next view
if i do same thing with out ARC it works fine
How to resolve this issue..? Thanks in advance

In Your appdelegate appdidfinishlaunching method, you have not set
self.window.rootviewcontroller to navigationController. In fact you did not set any rootViewController to Window. Thats why it is not being shown. Please set it before you start to see your ViewController on the screen.
The UIWindow rootViewController property is new with iOS4.
The older technique was to use addSubview.
The new, recommended technique is to set rootViewController.

Try this:
NewProjViewController *editProject = [[NewProjViewController alloc]initWithNibName:#"NewProjViewController" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:editProject animated:YES];
[editProject release];

Related

Custom Tabbar Controller for iPhone

I try to create framework for iPhone and try to implement Custom Tabbar Controller in my framework. Any one can help me to create custom tab bar controller for iPhone. I mean programmatically add tab bar controller.
To create tab bar give this code in AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIViewController *viewController1, *viewController2,*viewController3;
viewController1 = [[FirstViewController alloc] initWithNibName:#"FirstViewControllerXibname" bundle:nil];
viewController2 = [[SecondViewController alloc] initWithNibName:#"SecondViewControllerXibname" bundle:nil];
viewController3 = [[ThirdViewController alloc] initWithNibName:#"ThirdViewControllerXibname" bundle:nil];
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:viewController1, viewController2,viewController3, nil];
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
self.tabBarController.moreNavigationController.navigationBar.tintColor =[UIColor darkTextColor];
[[UITabBar appearance] setSelectedImageTintColor:[UIColor yellowColor]]; //change selected image color on tabbatItem
self.tabBarController.moreNavigationController.navigationBar.barStyle = UIBarStyleBlackOpaque;
[self.tabBarController.tabBar setOpaque:YES];
return YES;
}
This links may be helpful to you:
http://howtomakeiphoneapps.com/how-can-i-add-tabs-programmatically-to-uitabbar/201/
http://kurrytran.blogspot.in/2011/10/ios-5-tutorial-creating-custom-tab-bar.html
http://mobiledevelopertips.com/open-source/ios-open-source-custom-tabbar-controller-bctabbarcontroller.html
Or
The other option is - you can go with the custom buttons which acts like an UITabbarController.
Enjoy Coding :)
Mrunal

UIWindow subview disappears after when application becomes active

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

Why I can't release the navigation controller of the view I added to the window

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self setWindow:[[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]];
// Override point for customization after application launch.
//self.window.backgroundColor = [UIColor whiteColor];
// Create First view
FirstViewController *firstViewCTL = [[FirstViewController alloc] init];
// Create UINavigationController
UINavigationController *navCTL = [[UINavigationController alloc] init];
[[navCTL navigationBar] setBarStyle:UIBarStyleBlack];
[navCTL pushViewController:firstViewCTL animated:NO];
[firstViewCTL release];
[[self window] addSubview:navCTL.view];
[navCTL release];
[[self window] makeKeyAndVisible];
return YES;
}
I understand that adding subview (addSubview:) will retain the view to be aded. But why I can't now release my navigation controller (navCTL) that own the views that has already been retained
-addSubview: retains the view, not the view controller.
You can use UIWindow's rootViewController property (iOS 4 and higher) to retain the view controller, it also saves you adding the view as a subview yourself.
...
[window setRootViewController:navCTL];
[navCTL release];
...

Show View before displaying thee UITabBar

I need to display a view before I display a tab based app and once the user taps a button the view goes away.
Any ideas?
here is some code that demonstrates the process. You can paste this code into the app delegate to run. Note, that this is spaghetti code, but I did it this way so you can see all the steps in one place. Normally, you will put parts of this code into its own view controller and classes.
this is in the appdelegate.. note that this is not completely tested for leaks and stuff.. its meant for an example.
#synthesize tabViewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIWindow *w = [[UIWindow alloc]initWithFrame:[[UIScreen mainScreen] bounds]];;
self.window = w; //property defined in the .h file
[w release];
//create the tab bar controller
UITabBarController *tbc = [[UITabBarController alloc]init];
self.tabViewController = tbc;
[w addSubview:tbc.view];
//create the two tabs
NSMutableArray *a = [[NSMutableArray alloc]init];
//create the first viewcontroller
UIViewController *vca = [[UIViewController alloc]init];
vca.view.backgroundColor = [UIColor redColor];
vca.title = #"View A";
[a addObject:vca];
[vca release];
//and the second
UIViewController *vcb = [[UIViewController alloc]init];
vcb.view.backgroundColor = [UIColor blueColor];
vcb.title = #"View B";
[a addObject:vcb];
[vcb release];
//assign the viewcontrollers to the tabcontroller
tbc.viewControllers=a;
//release the array now that its retained by the tabcontroller
[a release];
[tbc release]; //tabbarcontroller is retained by our property
UIViewController *vcc = [[UIViewController alloc]init]; //this is the popup view
vcc.view.backgroundColor = [UIColor whiteColor];
UIButton *b = [UIButton buttonWithType:UIButtonTypeRoundedRect];
b.titleLabel.text = #"Click here";
[b addTarget:self action:#selector(buttonDismiss:) forControlEvents:UIControlEventTouchUpInside]; //hook into the buttons event
b.frame = CGRectMake(10, 10, 300, 40);
[vcc.view addSubview:b]; //add it to the popup view
[tbc presentModalViewController:vcc animated:YES];
[self.window makeKeyAndVisible];
return YES;
}
-(void) buttonDismiss:(UIButton *)sender
{
[self.tabViewController dismissModalViewControllerAnimated:YES];
}
Use a UIViewController. Present that controller as a modal view with the tab bar controller. Dismiss the view controller after that to hide it

How to switch from one UIViewController to another?

I have a problem with switching from one viewcontroller to another.
Here is what I have :
In my "AppDelegate_iPad.m" :
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
CGRect frame = [UIScreen mainScreen].bounds;
window = [[UIWindow alloc] initWithFrame:frame];
window.backgroundColor = [UIColor blackColor];
LoginViewController_iPad *loginView = [[LoginViewController_iPad alloc] initWithNibName:#"LoginViewController_iPad" bundle:nil];
[window addSubview:loginView.view];
[self.window makeKeyAndVisible];
return YES;
}
That works. Now I have a login button on that screen and when pressed it calles this :
- (IBAction)doPressLoginButton
{
DebugLog(#"doPressLoginButton");
MenuViewController_iPad *menuView = [[MenuViewController_iPad alloc] initWithNibName:#"MenuViewController_iPad" bundle:nil];
[self.navigationController pushViewController:menuView animated:YES];
[menuView release];
}
The problem is that nothing happends. I assume I am missing the actual navigationconroller ?!
Hope that anyone can help.
Thank you
You should create a UINavigationController instance variable in your app delegate. Then, make sure you synthesize it and release it in your dealloc method.
Your implementation of application:didFinishLaunchingWithOptions: could look like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// window is usually an IBOutlet
CGRect frame = [UIScreen mainScreen].bounds;
window = [[UIWindow alloc] initWithFrame:frame];
window.backgroundColor = [UIColor blackColor];
LoginViewController_iPad *loginView = [[LoginViewController_iPad alloc] initWithNibName:#"LoginViewController_iPad" bundle:nil];
navigationController = [[UINavigationController alloc] initWithRootViewController:loginView];
[loginView release];
[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
return YES;
}
Now you'll be able to use self.navigationController.
I assume I am missing the actual navigationconroller ?!
you are right. :D
self.navigationController returns nil if you don't set up an NavigationController.
And any messages send to nil object will be ignored.
If you only need switch from one to another.
using
[self presentModalViewController:modalViewController animated:YES];
instead.