I'm fairly new to Objective-C and iPhone programming so I apologize if this is a newbie question. I have a simple application that needs to go from one view, to another. The first view is a UIViewController. I set up the xib file in IB (i.e. dragged some buttons onto the window) and hooked up all the buttons (which all work). I then created another xib file and class (also a UIViewController) and hooked them up. When a button is pressed in the first view I want to load the second view. Here's the code that is supposed to be pushing the view:
-(IBAction)createAccount:(id)sender{
CreateAccountViewController*acctView = [[CreateAccountViewController alloc] initWithNibName:#"CreateAccount" bundle:nil];
[self.navigationController pushViewController:acctView animated:YES];
[acctView release];
}
But this does nothing. When I put print statements in the createAccount method those are printed (I can click the button any number of times and it never crashes) but the acctView is never pushed. When I print out the value of self.navigationController it returns null. It's even stranger because if I present the acctView modally then it works.
-(IBAction)createAccount:(id)sender{
CreateAccountViewController*acctView = [[CreateAccountViewController alloc] initWithNibName:#"CreateAccount" bundle:nil];
[self presentModalViewController:acctView animated:YES];
[acctView release];
}
This works just fine, but I don't want to use the view modally. I'm completely lost here. In the past couple of hours I've come across a lot of posts saying to do something with a UINavigationController and hook that up to my view, but how do I do that? Any help is greatly appreciated! Thanks.
It seems you haven't created a UINavigationController for your app.
Best thing would be starting from scratch with a new Xcode project, taking care of choosing a Navigation Based application. In this way you will get almost everything already set up for you.
If you don't like this approach, you can create programmatically your UINavigationController. Here you find a tutorial for doing that.
If you prefer more straight-to-the-point instructions, here they are:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
MainPageDialog *overviewViewController = [[MainPageDialog alloc] initWithNibName:#"MainPage" bundle:nil];
self.navigation = [[[UINavigationController alloc] initWithRootController:overviewViewController] autorelease];
[overviewViewController release];
[window addSubview:[navigation view]];
[self.window makeKeyAndVisible];
return YES;
}
whereby self.navigation is a retained property in your appDelegate.
EDIT:
This answer was quite old, therefore an update:
If you are using ARC, you should be using a strong (vs. retain) property and you would not need the autorelease;
if you target iOS > 4.0 (which is also implied by the above point), you can use the rootViewController property in UIWindow and say:
MainPageDialog *overviewViewController = [[MainPageDialog alloc] initWithNibName:#"MainPage" bundle:nil];
self.window.rootViewController = [[[UINavigationController alloc] initWithRootController:overviewViewController] autorelease];
[overviewViewController release];
[window addSubview:[self.window.rootViewController view]];
[self.window makeKeyAndVisible];
without the need for any navigation property.
Related
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 11 years ago.
I'm developing a first iPhone app for iOS 4.x/5.0 and have some confusions on how to comply with the recommended Apple guidelines. I have also attached code snippet.
Here is the scenario:
When the app loads, I want to show the following functionalities in the bottom of the screen: Test, Settings. My first question is do I have to go with a UISegmentedControl or UITabBar? The trickiest part is I don't want the first "Test" tab/button to be selected automatically. I want the user to select the tab/button. Until that happens, I want a timer that runs showing some text. I tried to use a UITabBar and when the app loads, the first "Test" tab is selected and the associated ViewController is shown by default. I circumvented this by pushing a "root view controller" in the "didFinishLaunchingWithOptions" method in the AppDelegate.m. This works and when I click on the "Scores" tab, it pops the "root view controller" and pushes the "test view controller". But if I select the "Settings" tab immediately after the app loads and click on the "Test" tab, the view controller for the "Test" tab is not loading at all, but the "main view controller" is still displayed. I also have a UINavigationController in place on the "Test" tab and I hide it in the "viewDidAppear" event. I also want to eliminate the animation (pushing back) when I click on the "Test" tab.
My confusions are:
Can I replace the UINavigationController with some other control to let the user click on a "Start/Stop" button (displayed on the top) when in the "test view controller"?.
Is there a click event on the tabs in the UITabBar for each of the tabs?
I wanted to do everything from code (except adding controls) to ViewControllers. It looks confusing to me to use IB to do the same thing. Is this is a bad approach?
The following is the code snippet:
AppDelegate.m
#import "NavTabTestAppDelegate.h"
#import "RootViewController.h"
#import "FirstViewController.h"
#import "SecondViewController.h"
#implementation NavTabTestAppDelegate
#synthesize window = _window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RootViewController *rootViewController = [[RootViewController alloc ] initWithNibName:#"RootViewController" bundle: nil];
[rootViewController.view setBackgroundColor:[UIColor blueColor]];
// [rootViewController setTitle:#"RootViewController"];
FirstViewController *firstViewController = [[FirstViewController alloc ] initWithNibName:#"FirstViewController" bundle: nil];
[firstViewController.view setBackgroundColor:[UIColor yellowColor]];
[firstViewController setTitle:#"FirstViewController"];
SecondViewController *secondViewController = [[SecondViewController alloc ] initWithNibName:#"SecondViewController" bundle: nil];
[secondViewController.view setBackgroundColor:[UIColor redColor]];
[secondViewController setTitle:#"SecondViewController"];
//create the navigation controller and use NavRootController as its root
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:firstViewController];
//create an array of views that will be passed to our tabbar
NSArray *viewsArray = [NSArray arrayWithObjects:nav, secondViewController, nil];
//Now create our tab bar controller
UITabBarController *tabbarController = [[UITabBarController alloc] init];
//then tell the tabbarcontroller to use our array of views
[tabbarController setViewControllers:viewsArray];
[nav pushViewController:rootViewController animated:NO];
//nav.view.hidden = YES;
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
//then the last step is to add the our tabbarcontroller as subview of the window
self.window.rootViewController = tabbarController;
return YES;
}
RootViewController.m (added the following code)
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES animated:animated];
// NSLog(#"Inside RootViewController.m viewWillAppear %#", animated);
}
- (void) viewWillDisappear:(BOOL)animated{
// NSLog(#"Inside RootViewController.m viewWillDisappear %#", "Done");
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:NO animated:NO];
}
FirstViewController.m (added the following code to prevent the animation but didn't work)
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:NO];
[self.navigationController setNavigationBarHidden:NO animated:NO];
// NSLog(#"Inside FirstViewController.m viewWillAppear %#", animated);
}
- (void) viewWillDisappear:(BOOL)animated{
// NSLog(#"Inside FirstViewController.m viewWillDisappear %#", "Done");
[super viewWillAppear:NO];
[self.navigationController setNavigationBarHidden:NO animated:NO];
}
NSLogs throws exception when I click on a tab (BAD_ACCESS). Couldn't figure out why.
I come from a Windows developer background and I apologize for the long post. Please help.
Thanks.
My first question is do I have to go with a UISegmentedControl or
UITabBar? The trickiest part is I don't want the first "Test"
tab/button to be selected automatically.
I think you're missing the point of UITabBarController. Its job is to let the user select one of several different views, and it manages the view controllers corresponding to those views. One of the views will always be selected -- it really doesn't make sense to have no tab selected. If you want the "tabs" to do something other than select a view, you should use something else, such as UIToolbar or perhaps UISegmentedControl.
Can I replace the UINavigationController with some other control to let the user click on a "Start/Stop" button (displayed on the top)
when in the "test view controller"?.
I had a hard time grokking what you're trying to do with the navigation controller, so it's hard to say whether or not you can replace it. You should understand, though, that UINavigationController is a view controller, not a control. If you're just talking about the navigation bar that's provided by the navigation controller, then sure, you can display that or not depending on your preference.
Is there a click event on the tabs in the UITabBar for each of the tabs?
The tab bar delegate should implement the UITabBarDelegate protocol, which includes – tabBarController:didSelectViewController:.
I wanted to do everything from code (except adding controls) to
ViewControllers. It looks confusing to me to use IB to do the same
thing. Is this is a bad approach?
Depends on who you ask. Most experienced Objective-C programmers use IB when it's appropriate, i.e. just about any time you need to define a relatively static user interface.
Some people prefer not to use IB, but it seems to me that most of those folks never really took the time to learn how to use IB. You should do whichever you prefer.
I'd say that avoiding a useful tool because you don't understand it might be reasonable in the short term, but is a poor approach over the long term. When you're just learning to program on a whole new platform, it's quite reasonable to limit the number of things you need to know. IB really isn't complicated, though, and it's very useful. If you don't use it right now, make a note to yourself to spend some time working with it in the future.
Hihi all,
I am trying to make use of tabbar control in my iphone application, I have a few enquiries regarding the control.
If i create a tabbar template project, in my application delegate, it loads all the 5 tab controllers during the launch of the application, will this cause any inefficiency of the memory usage?
Can I actually drag the tabbar control into each of my screen, and manually switch between screens with [self presentedViewController..] and [self dismissModalViewControllerAnimated...] methods?
What is the most efficient way of using tabbar in iphone app?
Thanks in advance!
:)
Even though I'm not sure what 'most efficient' means in your context, I'll try to explain 3. with explaining what I usually do when it comes to typical tabbar apps:
I don't go with the sample project, because there's so much magic (to me) in all that IB stuff (and I had hard experiences in trying, to combine tabbar controllers and navigation controllers).
I just set up a simple project, get rid of all IB stuff and do something like that in the app delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSMutableArray *cons = [[NSMutableArray alloc ]init];
viewController = [[UITabBarController alloc] init];
int i = 0;
UIViewController *firstController = [[SomeViewViewControllerClazz alloc] init];
firstController = [[UITabBarItem alloc]initWithTitle:#"Een" image:nil tag:i];
[cons addObject:firstController];
[firstController release];
i++;
UIViewController *secondController = [[AnotherViewControllerClazz alloc] init];
secondController = [[UITabBarItem alloc]initWithTitle:#"Twej" image:nil tag:i];
[cons addObject:secondController];
[secondController release];
i++;
UIViewController *thirdController = [[WhateverViewControllerClazz alloc] init];
thirdController = [[UITabBarItem alloc]initWithTitle:#"Drej" image:nil tag:i];
[cons addObject:thirdController];
[thirdController release];
i++;
viewController.viewControllers = cons;
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
That way I've maximum freedom to do whatever I want with the controllers but also have the built in power of tabbars.
1.: That way I never had memory issues even though I load controllers right at the beginning.
2.: If the tabbar idea fits your app, use it as iOs provides it.
The answer of your enquiries are:
No, this will not cause any inefficiency of the memory usage. But you should release tab bar controller
You may be do this, But it is not good practice and when you call presentedViewController tab will be vanished (sorry for inappropriate term).
The most efficient way of using tabbar in iphone app is take a tabbar controller and add that tabBar view.
I'm trying to use a UINavigationController but I'm uncertain how. Up till now (for about a year), I've been using presentModalViewController and dismissModalViewController to present/dismiss view controllers.
So, this is what I did. My main view controller (the first one that shows on launch) is called MainViewController, and it extends UIViewController.
So I made this launch function in my app delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
MainViewController *controller = [[MainViewController alloc] init];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:controller];
[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
return YES;
}
And in my MainViewController's viewDidLoad method:
- (void)viewDidLoad {
[super viewDidLoad];
self.title = #"Title";
self.navigationController.navigationBar.tintColor = [Constants barColor];
....more code...
}
But, in my MainViewController, I'd like to present another view controller called SecondViewController, which needs a UINavigationBar with a back arrow button. So do I make SecondViewController extend UIViewController and do the same thing by setting the title and backButton in the viewDidLoad method? And how do I present it? What should I do to accomplish this?
You'll need to set a root view controller up, it's easiest starting from the apple template.
Here's where the magic happens:
UIViewController *controller = [[UIViewController alloc] initWithNibName:#"MyNib" bundle:nil];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
The nav controller does all the work for you (back buttons, titles, animations) - it keeps track!
My workflow is this:
Setup MutableArray in the viewDidLoad, add controllers to it, e.g:
NSMutableArray *array = [[NSMutableArray alloc] init];
MyCustomViewController *customView = [[MyCustomViewController alloc] initWithNibName:#"nib" bundle:#"nil"];
customView.title = #"Second Level";
[array addObject:customView];
self.controllers = array;
Then in your delegate:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger row = [indexPath row];
UIViewController *childControllerToBe = [controllers objectAtIndex:row];
[self.navigationController pushViewController:childControllerToBe animated:YES];
}
This, along with a lot more can be learnt by reading a decent beginner book such as Beginning iPhone Development
Also, apple docs are always good :)
UINavigationController is a subclass of UIViewController, but unlike UIViewController it’s not usually meant for you to subclass. This is because navigation controller itself is rarely customized beyond the visuals of the nav bar. An instance of UINavigationController can be created either in code or in an XIB file with relative ease.
Please visit "How to add UINavigationController Programmatically"
You should Push it onto the navigation stack.
This Lecture by Stanford's iPhone Course will teach you a lot about Navigation Bars. (It's a quick read)
Basically at the heart of it you need this code:
[self.navigationController pushViewController:SecondView];
You can use PopViewController to go back programmatically, but the Back Button is automatically created.
Here's some source code from the Lecture. It covers exactly what you are having issues with.
So I feel like a serious rookie right now, but I have a problem I can't seem to figure out. I have a barebones app, with literally nothing in it except a login screen and a second view containing a tableview. When I add the second view after logging in (I have done this like 4 times before...), the table view goes through its delegates and appears that it's going to load, but something happens. I have enabled my NSZombies, and it appears to be deallocating the new view, right before it appears.
After tracing through it, and building up again piece by piece, it appears to happen after I wire the table to the view as the datasource/delegate in IB. I have set the view as a UITableViewDelegate, and the methods indeed get fired. Does anyone have any idea what might be causing this behavior?
Have you added the 'second'view to an exisitng view using addSubview: or added it to some form of UINavigationController or UITabBarController? When you do this it will automatically increase the retain count and whatever code you have releasing the view won't cause is to be deallocated.
In my AppDelegate application:didFinishLaunchingWithOptions I have something like;
LoginViewController *login = [[LoginViewController alloc] init];
[login setDelegate:self];
loginNavController = [[UINavigationController alloc]
initWithRootViewController:login];
[window addSubview:[loginNavController view]];
And then once login has occured (and succeeded using a protocol/delegate to send the message back to AppDelegate) I call this code;
UIViewController *newView1 = [[UIViewController alloc] init];
UIViewController *newView2 = [[UIViewController alloc] init];
UIViewController *newView3 = [[UIViewController alloc] init];
myTabBarController = [[UITabBarController alloc] init];
myNavController = [[UINavigationController alloc]
initWithRootViewController:newView1];
// nav controller now retaining
[newView1 release];
NSArray *viewControllers = [NSArray arrayWithObjects:myNavController,
newView2,
newView3,
nil];
[myTabBarController setViewControllers:viewControllers animated:YES];
[[myTabBarController view] setFrame:[[UIScreen mainScreen] applicationFrame]];
[window addSubview:[tabBarController view]];
// tab bar controller now retaining
[newView2 release];
[newView3 release];
// remove login from application
[[loginNavController view] removeFromSuperview];
The AppDelegate has the following declared in the header file;
LoginViewController *loginViewController;
UITabBarController *myTabBarController;
UINavigationController *myNavController;
In the dealloc method for the AppDelegate these are released.
This gives me my login page and then when that has processed my views with a top nav all controlled using the bottom tab bar.
Hope this helps in some way.
You have either too many release (or autorelease) calls - or not enough retain calls - in your view loading/transitioning code, but it's impossible to be more specific without seeing that code.
What's probably happening is the autorelease pool is being flushed between your view loading and your view being shown, and that's what's leading the behaviour you describe.
I need to display a couple of view controllers (eg, login screen, registration screen etc). What's the best way to bring each screen up?
Currently for each screen that I'd like to display, I call a different method in the app delegate like this:
Code:
- (void) registerScreen
{
RegistrationViewController *reg = [[RegistrationViewController alloc] initWithNibName:#"RegistrationViewController" bundle:nil];
[window addSubview:reg.view];
}
- (void) LoginScreen
{
LoginViewController *log = [[LoginViewController alloc] initWithNibName:#"LoginViewController" bundle:nil];
[window addSubview:log.view];
}
It works, but I cant imagine it being the best way.
I'd recommend reading the View Controller Programming Guide if you haven't: http://developer.apple.com/iphone/library/featuredarticles/ViewControllerPGforiPhoneOS/Introduction/Introduction.html
It sounds like presenting a view controller modally may be your best bet - but you'll probably want to wrap it in an UINavigationController first.
eg
UINavigationController *navController = [[[UINavigationController alloc] initWithRootViewController:theControllerYouWantToPresent] autorelease];
[self presentModalViewController:navController animated:YES];
I've often wondered if this is the best way myself, but when I'm not using IB's built-in stuff (like a NavigationController) I have a single method in the AppDelegate, switchToViewController:(UIViewController *)viewController that I pass...well, it's pretty self-explanatory I guess. This way there's only one place where it's done, and I can easily define transitions in that method once the app nears completion.
Also, don't forget to remove the previous views in your methods, otherwise you're liable to run out of memory. Something like this:
-(void) switchToViewController:(UIViewController *)c {
if(c == currentController) return;
[currentController.view removeFromSuperview];
[window addSubview:c.view];
[currentController release];
currentController = [c retain];
}