I'm a beginner with Xcode and Objective-C, i want to make a view controller in code without a nib file and shape it how i want. Currently with this very simple code I can't seem to even change the background color because of EXC_BAD_ACCESS.
I read on internet it is something with memory management but I can't seem to find the fix for this. Pieces of my code:
AppDelegate.h
#import <UIKit/UIKit.h>
#import "DefaultViewController.h"
#class DefaultViewController;
#interface AppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
UIViewController *rootViewController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UIViewController *rootViewController;
#end
AppDelegate.m
#synthesize window = _window;
#synthesize rootViewController = _rootViewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIViewController *rvc = [[DefaultViewController alloc] init];
self.rootViewController = rvc;
[rvc release];
[self.window addSubview:self.rootViewController.view];
// Override point for customization after application launch.
[self.window makeKeyAndVisible];
return YES;
}
- (void)dealloc
{
[_window release];
[_rootViewController release];
[super dealloc];
}
The view controller I made via, right click -> new file and UIViewController subclass without xib! In the loadView I only try this:
self.view.backgroundColor = [UIColor redColor];
The problem could be that the rootViewController doesn't have an initialized view. Hard to tell, since you don't show the code of the DefaultViewController. It could also be another error in DefaultViewController.
FWIW, you have two obsolete ivars:
#interface AppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
UIViewController *rootViewController;
}
You can delete these, since you synthesize _window and _rootViewController and never use the above.
There are a few issues with your code. But first the key problem you are getting EXC_BAD_ACCESS is because you are calling self.view.backgroundColor inside the loadView. If you override loadView, you must construct your view hierarchy inside the method. By not creating a view hierarchy in that method you are calling backgroundColor on a view that does not exist. Instead completely remove the loadView method or comment it out and move self.view.backgroundColor into the viewDidLoad method. (Remember even an empty loadView method will be a problem, you need to remove it or comment it out)
Second.. change your code to
self.rootViewController = rvc; to self.window.rootViewController = rvc;
BTW, once you add a view controller you don't need to add the view of the rootViewController as a subview to the window again. Assigning a view controller to the rootViewController property installs the view controller's view as the content view of the window.
Third. When you are initializing DefaultViewController you do
UIViewController *rvc = [[DefaultViewController alloc] init];
dont do that, instead do
DefaultViewController *rvc = [[DefaultViewController alloc] init];
Change the code so it's
self.window.rootViewController = rvc;
[self.window makeKeyAndVisible];
return YES;
What's inside your DefaultViewController? If you are not using a nib file, did you implement -(void)loadView ?
You should call:
UIViewController *rvc = [[DefaultViewController alloc]
initWithNibName:#"yournib" bundle:nil];
to load the NIB view. Otherwise, your rvc.view will be nil.
Sorry, I overlooked.
The answer is: you should not call self.view.backgroundColor = [UIColor redColor]; within - (void)loadView since self.view is nil firstly at this stage. He has to show that he has correctly created at least self.view = [[UIView alloc] init] in loadView.
Related
I am very new to iOS development and would appreciate if experts here would be able to help me with my problem. At the moment, my application is extremely basic and does nothing much. Prior to trying to add a tab bar to my existing view, things worked fine. I am not sure what I'm missing but nothing is showing when I run the simulation. I'll try my best to explain the structure of my application so that you guys can understand the problem better.
The following are currently present in the application...
FeedList: A UITableViewController embedded inside a UINavigationController.
FeedCell: A UITableViewCell created for FeedList.
FeedItemDetail: A UIViewController with a UIScrollView within it. User will be brought to this screen by tapping on a cell in FeedList.
Below are the codes for AppDelegate.h and AppDelegate.m. I would greatly appreciate it if someone is able to tell me why nothing is showing on my simulation screen. Thanks!
//AppDelegate.h
#import <UIKit/UIKit.h>
#import "FeedList.h"
#interface AppDelegate : NSObject <UIApplicationDelegate>
{
UIWindow *window;
FeedList *feedList;
UITabBarController *tabBarController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) FeedList *feedList;
#property (nonatomic, retain) UITabBarController *tabBarController;
- (void)customizeAppearance;
#end
//AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window, feedList, tabBarController;
// Entry point
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
tabBarController = [[UITabBarController alloc] init];
feedList = [[FeedList alloc] init];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:feedList];
tabBarController.viewControllers = [NSArray arrayWithObject:nav];
[window addSubview:tabBarController.view];
[window makeKeyAndVisible];
}
Update (problem solved)
I realized that after adding the line tabBarController.viewControllers = [NSArray arrayWithObject:nav]; things start to go haywire. After checking Apple's Documentation, the reason is because if the value of this property is changed at runtime, the tab bar controller removes all of the old view controllers before installing the new ones. It is therefore required that we set the new tab bar controller as the root view controller.
I agree with Dustin's comment, you should use the storyboard if you are starting off new. What I see wrong with your method, or different from typical anyway, is that you don't add tabBarController as subview you set the rootViewController of self.window like so:
// Entry point
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
tabBarController = [[UITabBarController alloc] init];
feedList = [[FeedList alloc] init];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:feedList];
tabBarController.viewControllers = [NSArray arrayWithObject:nav];
//******* This is my correction *******
window.rootViewController = tabBarController;
//******* *******
[window makeKeyAndVisible];
}
Of course there is no way to tell from the info you provided if your tableview is set up right so there is no guarantee this will display your table.
I was trying to create a tab bar based application from scratch but I'm having some issues. Basically I have my AppDelegate.h, AppDelegate.m and a MainView.xib. In the the .h file I have:
#interface AppDelegate : UIResponder <UIApplicationDelegate, UITabBarControllerDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) IBOutlet UITabBarController *tabBarController;
#end
In My .m I have:
#synthesize window = _window;
#synthesize tabBarController = _tabBarController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
return YES;
}
And in my xib file I have my files owner as a class of Appdelegate and I have my outlet from there hooked up with a Tab Bar Controller. The problem is that my controller is returning null and thus isn't being set as the rootViewController. What step/idea am I missing in setting up my app? Thanks in advance!
EDIT FIXED: Since I was starting from a empty application I didn't have 'Main nib file base name' set, so I just had to set it to the nib I was trying to load.
You need to create the view controllers that will be accessed through the tabs on your Tab Bar Controller. Then you must assign them to the viewControllers property of the Tab Bar. You can do this just before setting the Tab Bar as the window's rootViewController:
MyViewController1 *vc1 = [[MyViewController1 alloc] init];
MyViewController2 *vc2 = [[MyViewController2 alloc] init];
NSArray *controllers = [NSArray arrayWithObjects:vc1, vc2, nil];
self.tabBarController.viewControllers = controllers;
I hope it helps!
Normally the appDelegate object is not a subclass of UIResponder, but a subclass of NSObject.
Are you hooking the TabBar instance to the first responder in your xib file? take into account that this won't work as the first responder is only a proxy object and setting the class type to it won't cause an object to be instantiated.
Instead, create an AppDelegate object in your xib file and hook the TabBar to it.
Hope it helps.
Since I was starting from a empty application I didn't have 'Main nib file base name' set, so I just had to set it to the nib I was trying to load.
I have a root view controller which should load another view controller as soon as it is done loading (i.e. in the viewDidLoad method).
I am using the UINavigationController in order to push a new view controller onto the stack:
In my rootviewcontrollerappdelegate:
-(void) viewDidLoad{
LoginViewController* lvc = [[LoginViewController alloc]init];
[self.navigationController pushViewController:lvc animated:NO];
}
I have textfields and buttons in the view controller to be loaded. The above doesn't seem to work however...It loads just a blank grey screen and no UINavigation bar is present. If I comment out the second line (pushViewController line), then I see the navigation bar. So I think it is loading something, but the items in the view controller being loaded are not being shown...Any ideas why?
Check if navigationController is pointing to nil. If it does, try
[self.view addSubview:self.pushViewController.view]
I had the same problem and found the above solution here:
UIViewController -viewDidLoad not being called
Unless you're doing something tricky, you should be calling alloc on the LoginViewController class rather than a variable. Also, if you've set up LoginViewController in Interface Builder (as opposed to programmatically), you'll need to load it from an NIB:
LoginViewController *lvc = [[[LoginViewController alloc] initWithNibName:nil bundle:nil] autorelease];
[self.navigationController pushViewController:lvc animated:NO];
Have a look at initWithNibName:bundle: in the docs.
Not entirely sure what you are trying to achieve but when you instantiate LoginViewContoller it should probably look like this
LoginViewController* lvc = [[LoginViewController alloc]init];
Judging by the nature of your naming for your view controller, is your LoginViewController the first view controller for your UINavigationController?
If that is what you're trying to do, you should instead initialise your navigation controller with the LoginViewController as the root controller instead of pushing it onto the navigation stack.
UINavigationController has a method to do this:
- (id)initWithRootViewController:(UIViewController *)rootViewController
EDIT:
Well, one way you can go about it is like this.
In your application delegate .h file, you should have declared a UINavigationController.
#interface MyAppDelegate : NSObject <UIApplicationDelegate>
{
UINavigationController *navController;
}
#property (nonatomic, retain) UINavigationController *navController;
#property (nonatomic, retain) IBOutlet UIWindow *window;
#end
In your App Delegate didFinishLaunching:withOption: you can create an instance of your LoginViewController there, and use that to init your UINavigation controller as the root view controller
#import "LoginViewController.h"
#implementation MyAppDelegate
#synthesize navController;
#synthesize window = _window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
LoginViewController *loginController = [[LoginViewController alloc] init];
navController = [[UINavigationController alloc] initWithRootViewController:loginController];
[loginController release];
[[self window] setRootViewController:navController];
[navController release];
[self.window makeKeyAndVisible];
return YES;
}
I probably have a typo here or there but that's one way I would go about doing it.
I have the mainWindow.xib in my application with a TabController in it for my main-menu.
Now I want to have a Login-form for my application.
I added a View (LoginViewController) and show this view at beginning:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// Add the tab bar controller's current view as a subview of the window
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
LoginViewController *lvc = [[LoginViewController alloc] initWithNibName:#"LoginViewController" bundle:nil];
lvc.delegate = self;
[self.tabBarController presentModalViewController:lvc animated:false];
[lvc release];
return YES;
}
Next I have my login-checks in the view, okay.
Now I try to dismiss the view after this.
For this I searched around and found the question here: present modal view controller
I add all things to my project, but getting a error in this code:
#import <UIKit/UIKit.h>
#interface Animexx3AppDelegate : NSObject <UIApplicationDelegate,
UITabBarControllerDelegate, LoginViewControllerDelegate>
{
UIWindow *window;
UITabBarController *tabBarController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
#end
Which means "cannot find protocol LoginViewControllerDelegate" in line 2/3.
Addition in the line "lvc.delegate = self" I get a warning that tells me "assigning to "id" from incompatible type "Animexx3AppDalagte".
What did Im wrong?
You need to import the header file where you declared the LoginViewControllerDelegate protocol. Most likely the LoginViewController.h file.
After struggling with this many times, we published an open source library called CLHoppingViewController which handles exactly this kind of scenarios.
So, in your case, you would do something like this to describe the start up flow:
UIViewController *loginViewController;
UIViewController *mainViewController;
if (user_not_logged_in) {
[self hopToViewController:loginViewController then:^{
[self hopToViewController:mainViewController then:nil];
}];
}
else {
[self hopToViewController:mainViewController then:nil];
}
The library can support much more advanced conditional sequences. For example, you can display a splash screen, conditionally show onboarding UX, etc.
There's a short tutorial here.
I have an iPhone project that starts out with a standard UIView based Window... when the user clicks a button its suppose to launch into a new view with a UITabBarController -- similar to the way the iTunes Connect app behaves after you login. There are no sample code examples in the Apple documentation doing what I want but I know its possible because Apple has done it in their own apps (another example is the MobileMe iDisk app for iPhone).
I already tried the standard -presentModalViewController:animated: method and that did not work because there isn't a view that I can attach within the UITabBarController.
Next I am going to attempt to work with two window XIBs within the App Delegate to see if I can get that approach to work instead.
I would appreciate any insight if you know how to answer this little problem of mine. =)
What I ended up doing is this:
In my App Delegate, I have the following in my interface:
#interface myAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow * window;
LauncherViewController * startup;
UITabBarController * tabs;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet LauncherViewController * startup;
#property (nonatomic, retain) IBOutlet UITabBarController * tabs;
#end
In my implementation file, I add the following to the app start up function:
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[self.window addSubview:self.startup.view];
[self.window makeKeyAndVisible];
NSNotificationCenter * notifier = [NSNotificationCenter defaultCenter];
[notifier addObserver:self
selector:#selector(launch)
name:MyAppLoginInitializedNotification
object:nil];
[notifier addObserver:self
selector:#selector(logout)
name:MyAppLogoutNotification
object:nil];
return YES;
}
- (void) launch {
[self.startup.view removeFromSuperview];
[self.window addSubview:tabs.view];
[self.window makeKeyWindow];
}
- (void) logout {
[self.tabs.view removeFromSuperview];
[self.window addSubview:startup.view];
[self.window makeKeyWindow];
}
My main XIB contains both the standard UIViewController defined as LauncherViewController as well as a generic UITabBarController. As soon as my main launcher controller authentices the user credentials and sends the MyAppLoginInitializedNotification, the app delegate switches from the launcher to the tab view enabling me to continue on with my app logic.
UITabBarController really is just a subclass of UIViewController, so -presentModalViewController:animated: should work:
UITabBarController *someController = [[UITabBarController alloc] init];
someController.viewControllers = /* your View Controllers here */
[self presentModalViewController:someController animated:NO];
if i understand your issue correctly, you want to start the UITabBarController View after the first view you mentioned in your Question, i am attaching a link doing the same thing you need except you have an Extra view before the UITabBarController View appears, hope it will give you a guide.
http://www.mobisoftinfotech.com/blog/iphone/iphone-tabbar-uitabbarcontroller-tutorial/
I don't think you have to re-add the UITabBarController in the nib file. Just create it in code, add it as the poster above says, and you should be good to go. Here's some code that works for me.
UITabBarController *nextController = [[UITabBarController alloc] init];
FirstController *firstView = [[FirstController alloc] initWithNibName:#"FirstView" bundle:nil];
SecondController *secondView = [[SecondController alloc] initWithNibName:#"SecondView" bundle:nil];
ThirdController *thirdView = [[ThirdController alloc] initWithNibName:#"ThirdView" bundle:nil];
[nextController setViewControllers:[NSArray arrayWithObjects:firstView, secondView, thirdView, nil] animated:NO];
Till this point it should be the same, but I'm pushing a tabbar controller into the uinavgiationcontroller instead, so this is where we might differ. I do it as follows:
[self.navigationController pushViewController:nextController animated:YES];