I have an iOS application for which I want to create a ViewController programmatically.
I started an empty XCode project and modified the main method so it looks like this
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, #"MyAppDelegate_iPad");
[pool release];
return retVal;
}
The app is a Universal Application, MyAppDelegate_iPad is a subclass of MyAppDelegate, which is a subclass of NSObject <UIApplicationDelegate>.
My problem is that the applicationDidFinishLoading method I've overridden in MyAppDelegate_iPad is never called (break point on the first line never hits). The method looks like this
-(void) applicationDidFinishLaunching:(UIApplication *)application {
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
if(!window)
{
[self release];
return;
}
window.backgroundColor = [UIColor whiteColor];
rootController = [[MyViewController alloc] init];
[window addSubview:rootController.view];
[window makeKeyAndVisible];
[window layoutSubviews];
}
I removed the line to link to a nib file from my plist file (I used to get the default "My Universal app on iPad" white screen) and now all that is displayed is a black screen. applicationDidFinishLoading is still not being called.
Am I doing something wrong? How should I properly create my AppDelegate instance?
There’s a main nib file that bootstraps your application. This nib file is referenced in the Info.plist file under the NSMainNibFile key and should contain an object that corresponds to your application delegate class (setting the Class attribute in Interface Builder). This application delegate object is referenced by the delegate outlet on the file’s owner placeholder.
So if I understand things correctly, the application loader loads the main nib file, setting itself as the nib owner. Its delegate property gets set to a fresh instance of your application delegate class, and so the loader knows where to dispatch the various application lifecycle event callbacks.
There’s an awesome blog post about Cocoa application startup on Cocoa with Love.
If you are making universal you don't need two different app delegate classes. see this link (my answer), it may be help you to make universal app.
Related
I am trying to create a custom NSViewController and just log something out in viewDidLoad. In iOS, this is very trivial and works fine. However, when I setup a contentViewController on NSWindow (which i assume is similar to RootViewController in iOS?) it attempt to load it from a nib.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
self.ABViewController = [[ABViewController alloc] init];
self.window.contentViewController = self.ABViewController;
}
2016-06-28 09:15:42.186 TestApp[32103:33742217] -[NSNib _initWithNibNamed:bundle:options:] could not load the nibName: ABViewController in bundle (null).
What assumptions am I missing about how Cocoa is different from iOS that prevent me from simply setting up a viewController?
NSViewController doesn't have the same behavior as UIViewController, in that it won't automatically know to look for a nib file with the same name as itself. In other words, it won't automatically know to look for the ABViewController.nib file.
The simplest way to fix this is just override the nibName method in ABViewController:
#implementation ABViewController
- (NSString *)nibName {
return NSStringFromClass([self class]);
}
#end
Note that using NSStringFromClass() is usually better than trying to hard code the string, as this way will survive refactoring.
You can then call [[ABViewController alloc] init]; like before and NSViewController's default init method will get the nib name from your overridden nibName method.
It looks like your program wan't find the file where the view's are defined. You need to do something like this for storyboards:
UIStoryboard *sboard = [UIStoryboard storyboardWithName:#"StoryboardFileName"
bundle:NSBundle.mainBundle()];
SecondViewController *vc1 = [sboard instantiateInitialViewController];
I've searched for this on Apple's site and can only seem to find documentation using Storyboards or Navigation Controllers, neither of which I'm using. It's an incredibly straightforward question about memory management.
I created a completely blank application. In my AppDelegate's didFinishLaunchingWithOptions function I'm creating an instance of a View Controller which I've built. My design (which itself could be a problem) is to have a 1:1 relationship between View Controllers and Views.
So the main menu of my application, which is a launching pad for everything is in MenuViewController.h/m.
In .h:
MenuViewController *m;
In .m:
- (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.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
m = (MenuViewController *)[[MenuViewController alloc] init];
m.window = self.window;
[m doStuff]; // handful of functions, not actually called this
//[m release]; // doesn't make sense to me
return YES;
}
This is where I'm confused. I want this to exist for basically the entirety of the application life cycle. But I'm also under the impression that you should (in the scope of the current function) release anything you allocate. If you need it beyond that, you should retain it elsewhere first. Is this not true?
My fundamental question is...where should I be releasing this View Controller? Is there anything else I've said that seems out of whack?
The initialization is wrong. You don't assign a window to controller, you assign a controller to window:
// window creation code here
...
m = [[MenuViewController alloc] init];
[window setRootViewController:m]; // window does retain for m
[m release]; // so we release it here
[self.window makeKeyAndVisible];
return YES
}
You're right. Generally you should release anything you create in a scope. But in this case you want ownership of the view controller. In this case you need to release the object in the dealloc method of your app delegate:
- (void)dealloc {
[m release];
[super dealloc];
}
Alternatively you could define a #property for your view controller with retain flag and then do this:
MenuViewController *viewController = [[MenuViewController alloc] init];
self.m = viewController;
[viewController release];
Btw, you don't need to cast to MenuViewController in either case.
EDIT: I completely missed that you don't add your view controller to your window. Good point #Eimantas.
I am new to Cocoa, and I am just experimenting with creating a window programmatically (without using Interface Builder).
I start a new Cocoa Application in Xcode, then I remove the window from the nib file in Interface Builder to replace it with my own one.
In the main function, I add the code:
NSWindow* myWindow;
myWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(10,100,400,300)
styleMask:NSTitledWindowMask
backing:NSBackingStoreBuffered
defer:NO];
When I try to build and run the application, I receive the following error message:
Error (1002) creating CGSWindow
Why does this happen??? What is a CGSWindow by the way?
Rainer
You probably don't have a connection to the window server yet. That's NSApplication's job, so try creating the shared application first.
If that doesn't help, I'd just go through with my usual application layout: Create an NSObject subclass for a custom controller, instantiate this from your application delegate's applicationWillFinishLaunching: and release it in applicationWillTerminate:, and have your custom controller's init method create the window. The application object will definitely be running by this point (as main does nothing but call NSApplicationMain, which gets/creates the shared application and tells it to run), so you should definitely have your connection to the window server and so be able to create the window.
Move the code you've written into the following method from your App Delegate implementation file -
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
NSWindow* myWindow;
myWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(10,100,400,300)
styleMask:NSTitledWindowMask
backing:NSBackingStoreBuffered
defer:NO];
}
and it should load your window perfectly.
It's worth noting that it's never safe and is not a good practice to create any UI related objects in your main function.
If you want to display a completely empty window from scratch this is all the code you'll need:
//if you used a template this will be already in the file:
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoReleasePool alloc] init];
int retval=UIApplicationMain(argc,argv,nil,#"SimpleWindowAppDelegate");
[pool release];
return retVal;
}
#implementation SimpleWindowAppDelegate : NSObject <UIApplicationDelegate>
-(void) applicationDidFinishLaunching:(UIApplication *)application
{
UIWindow *window=[[UIWindow alloc] initWithFrame:[[UIDevice mainScreen] bounds]];
//You could create views and add them here:
//UIView *myView=[[UIView alloc] initWithFrane:CGRectMake(0,0,50,50)];
//[window addSubView:myView];
//[myView release];
[window makeKeyAndVisible];
}
#end
The only code that you will probably ever want to have in your main() function in a Cocoa application is automatically created for you by XCode (if you are using it).
I suggest that you add the code that you want into your applicationDelegate's -applicationDidFinishLaunching: method
- (void) applicationDidFinishLaunching:(NSNotification *)aNotification {
myWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(10,100,400,300)
styleMask:NSTitledWindowMask
backing:NSBackingStoreBuffered
defer:NO];;
}
I'm looking into a project that would upload custom NSBundles that include a NSViewController. In my main program I've got this code to deal with the bundle after it's been loaded...
id principalClass = [loadedBundle principalClass];
id instance = [[principalClass alloc] init];
[localLabel setStringValue:[instance name]];
NSView *incomingView = [[instance viewController] view];
[localView addSubview:incomingView];
[localView display];
And the principal classes init method in the bundle looks like this...
-(id) init {
if(self = [super init]){
name = #"My Plugin";
viewController = [[ViewController alloc] initWithNibName:#"View" bundle:nil];
}
return self;
}
View.nib is a nib located in the bundles project. But whenever I load the bundle I get this error...
2010-05-27 09:11:18.423 PluginLoader[45032:a0f] unable to find nib named: View in bundle path: (null)
2010-05-27 09:11:18.424 PluginLoader[45032:a0f] -[NSViewController loadView] could not load the "View" nib.
I know I've got everything wired up because the line [label setStringValue:[instance name]]; sets the label text correctly. Plus, if I take all of the clases in the bundle and load them into my main applications project everything works as expect. Any thoughts on how I can correctly reference "View" in my bundle?
Thanks!
In the init method, you shouldn't pass nil to the bundle parameter. According to the UIViewController documentation, passing nil will look up the NIB file in the main bundle (the application's one), which is not what you want.
You can workaround, by using a specialized initializer like this:
- (id) initWithBundle:(NSBundle *)bundle {
if(self = [super init]){
name = #"My Plugin";
viewController = [[ViewController alloc] initWithNibName:#"View" bundle:bundle];
}
return self;
}
And use it as follow:
Class principalClass = [loadedBundle principalClass];
id instance = [[principalClass alloc] initWithBundle:loadedBundle];
"View.xib is a nib"? Either it is a xib or it is a nib. A xib is not a nib. xib files can only be used in the interface builder, in deployment applications they must be transformed to nib files. That is what ibtool is doing. Xcode does not simply copy your xib files, it runs them through ibtool that converts them to nib files that are then placed into the Resources folder.
I usually create my projects without IB-stuff. The first thing I do is to strip off all references to xibs, outlets updated plist, etc and so forth. No problems, works great (in my world)!
Now, I just installed 3.2 and tried to develop my first iPad app. Following same procedure as before, I created a UISplitView-based application project and stripped off all IB-stuff. Also, I followed the section in Apple's reference docs: Creating a Split View Controller Programmatically, but nevertheless, the Master-view is never shown, only the Detail-view is (no matter what the orientation is). I really have tried to carefully look this through but I cannot understand what I have missed.
Is there a working example of a UISplitViewController without the nibs floating around somewhere? I have googled but could not find any. Or do you know what I probably have missed?
Declare your splitviewcontroller in your delegate header, use something like this in your didfinishlaunching
ensure you add the UISplitViewControllerDelegate to the detailedViewController header file and that you have the delegate methods aswell. remember to import relevant header files
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
splitViewController = [[UISplitViewController alloc] init];
rootViewController *root = [[rootViewController alloc] init];
detailedViewController *detail = [[detailedViewController alloc] init];
UINavigationController *rootNav = [[UINavigationController alloc] initWithRootViewController:root];
UINavigationController *detailNav = [[UINavigationController alloc] initWithRootViewController:detail];
splitViewController.viewControllers = [NSArray arrayWithObjects:rootNav, detailNav, nil];
splitViewController.delegate = detail;
[window addSubview:splitViewController.view];
EDIT - as per Scott's excellent suggestion below, don't add to the windows subview, instead
[self.window setRootViewController:(UIViewController*)splitViewController]; // that's the ticket
[window makeKeyAndVisible];
return YES;
}
//detailedView delegate methods
- (void)splitViewController:(UISplitViewController*)svc
willHideViewController:(UIViewController *)aViewController
withBarButtonItem:(UIBarButtonItem*)barButtonItem
forPopoverController:(UIPopoverController*)pc
{
[barButtonItem setTitle:#"your title"];
self.navigationItem.leftBarButtonItem = barButtonItem;
}
- (void)splitViewController:(UISplitViewController*)svc
willShowViewController:(UIViewController *)aViewController
invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
self.navigationItem.leftBarButtonItem = nil;
}
I also prefer code to IB ;-)
Oldish thread, but thought I'd spare reader time + grief when the above technique fails to produce a UISplitViewController that responds correctly to device orientation change events. You'll need to:
Ensure all subordinate views respond properly in
shouldAutorotateToInterfaceOrientation. Nothing new here.
Rather than add the UISplitViewController's view to the main window,
[window addSubview:splitViewController.view]; // don't do this
instead set the main window's root controller to the UISplitViewController:
[self.window setRootViewController:(UIViewController*)splitViewController]; // that's the ticket
Adding the splitviewcontroller's view as a subview of the main window (barely) allows it to co-present with sibling views, but it doesn't fly with UISplitViewController's intended use case. A UISplitViewController is a highlander view; there can only be one.
Swift 5.2
iOS 13
Both master and detail view controllers are embedded in navigation controllers
let splitViewController = UISplitViewController()
splitViewController.delegate = self
let masterVC = MasterViewController()
let detailVC = DetailViewController()
let masterNavController = UINavigationController(rootViewController: masterVC)
let detailNavController = UINavigationController(rootViewController: detailVC)
splitViewController.viewControllers = [masterNavController,detailNavController]
You can put this code in your AppDelegate's (or in SceneDelegate if your target is iOS 13.0+)didFinishLaunchingWithOptions function. Just remember to make the splitViewController your rootViewController like this
self.window!.rootViewController = splitViewController
I had just met the same problem.
make sure that your child viewController of splitview can Autorotate to interface orientation.
you can change the function in your childViewController like this:
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
then the master view will be shown.