Landing on a "ViewController" on a non-storyboard app on Cocoa - xcode

I am new to cocoa development.
When I create a new project on Xcode using storyboards it uses the iOS model, that I like more, of having a viewController and a delegate by those being separate files. Then I start my project using the viewController and adding stuff to its view.
When I create a new project not using storyboards, the "landing page" to start the project is a window and I start by adding views to it.
What I don't like of this model is that I have to add code to the delegate files. I don't like that. I like to have AppDelegate.h and .m just with the coredata and basic code to start the application and pass command to another class.
I simply will not use storyboards anymore on cocoa because they are a sea of bugs and half cooked implementations on Cocoa. NSSavePanel/NSOpenPanel don't work, SplitViewControllers and Tabs work badly. It is a shame.
How do I do that on non-storyboard apps on cocoa? I mean, is it possible to pass control to a viewController or to have another class separate from the app delegate to be the starting point for the app?

Something like this should work:
In main.m do following:
int main(int argc, const char * argv[]) {
AppDelegate * delegate = [[AppDelegate alloc] init];
[[NSApplication sharedApplication] setDelegate:delegate];
[NSApp run];
}
In AppDelegate.m following:
#interface AppDelegate ()
#property (nonatomic,strong) NSWindow *window;
#property (nonatomic,strong) MainViewController *masterViewController;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
self.masterViewController = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
self.window = [[NSWindow alloc] initWithContentRect:NSMakeRect(200, 200, 200, 200)
styleMask:NSResizableWindowMask
backing:NSBackingStoreBuffered
defer:NO];
[self.window.contentView addSubview:self.masterViewController.view];
self.masterViewController.view.frame = ((NSView*)self.window.contentView).bounds;
[self.window makeKeyAndOrderFront:NSApp];
}
This will give you resizable view controlled by MainViewController. You will need to create that class and configure it either with XIB or without. You can remove MainMenu.xib from target setting as main interface and from project.

Related

Menu items don't appear in nibless StatusMenu app?

I've been working on an application port that must show a status menu and some dynamic items within. It's behaviour is similar to the apple WIFI menu which has an icon, some fixed items and some dynamic items in the center (the available WIFI networks).
For various reasons I decided to go nibless. I've managed to get the menu icon to appear, but I can't seem to get the items to show in the menu when I click the icon.
This is what I have so far:
AppDelegate.h
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
// Strange, if these are not properties and not declared strong,
// the menu flashes momentarily and disappears. Could the dynamic menu items be related to object lifetime?
#property (strong, nonatomic) IBOutlet NSMenu *statusMenu;
#property (strong, nonatomic) NSStatusItem *statusItem;
#end
AppDelegate.m
import "AppDelegate.h"
#implementation AppDelegate
- (IBAction)loginClicked:(id)sender
{
NSLog(#"LoginClicked");
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
NSLog(#"AppDidFinishLaunching!");
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
[self.statusItem setMenu:self.statusMenu];
NSImage *statusImage = [[NSImage alloc] initWithContentsOfFile:#"/tmp/applogo.png"];
//menuImage = [[NSImage alloc] initWithContentsOfFile:[bundle pathForResource:#"applogo" ofType:#"png"]];
[self.statusItem setImage:statusImage];
[self.statusItem setAlternateImage:statusImage];
//[self.statusItem setTitle:#"MyApp"];
[self.statusItem setHighlightMode:YES];
// Add login
NSMenuItem *login = [[NSMenuItem alloc] initWithTitle:#"Login" action:loginClicked keyEquivalent:#""];
[self.statusMenu addItem:login];
NSMenuItem *quit = [[NSMenuItem alloc] initWithTitle:#"Quit" action:nil keyEquivalent:#""];
[self.statusMenu addItem:quit];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
main.m
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
int main(int argc, const char* argv[])
{
#autoreleasepool { // Do I need this or is it on by default?
// make sure the application singleton has been instantiated
NSApplication * application = [NSApplication sharedApplication];
// instantiate our application delegate
AppDelegate * applicationDelegate = [[AppDelegate alloc] init];
// assign our delegate to the NSApplication
[application setDelegate:applicationDelegate];
// call the run method of our application
[application run];
}
// execution never gets here...
return 0;
}
After much mucking around, I tried something that now seems obvious.
I don't know why, but in examples of StatusBar Apps that use nib, IBMenu is not alloc'ed and inited. It appears to be done automatically somehow.
Adding the following seemed to fix it.
self.statusMenu = [[NSMenu alloc] init];
Also, noting my comment on lifetime. If I don't use properties, I have to set retain on statusItem when creating it. Then it doesn't get destroyed. I'm still learning about how objective-C managages object lifetime and still a bit confused about ARC here. But it least it now seems to show my menu items.

appdelegate object in xib

I am trying to follow the apple photopicker example which has an appdelegate object in an .xib file. How does an appdelegate object get into a xib file, and why is it important?
In my app I want a navigation controller like the photopicker example has. My test work has not included a navigation controller and I am having difficulty inserting one into my test work now that it has just a view controller because the interface builder seems to have made up its mind to not allow the addition. Will it be easiest for me to just start over with a new app that has the navigation controller architecture and then add in copies of my test work methods and classes. Or is starting over unnecessary? I suspect that inserting the appdelegate object into the xib would allow me to use the exist test work more directly.
update 0
I think I followed the instructions in your answer. Xcode suggested _viewController and I changed that. My app is for both iPad and iPhone (and I am working only on iPad now), so it is a little more complicated, I think. I compile fine, but I still see no appdelegate object in my xib file even though PhotoPicker's xib file has one.
I suspect I need to hook up some objects still, but I could use some help with that, too. In PhotoPicker the Connections Inspector for the Navigation Controller says that the Referencing Outlet is between navController and AppDelegate, and the Connections Inspector for the AppDelegate says that the Referencing Outlet is between delegate and File's Owner. But because my xib file has no (BS)AppDelegate object, I don't know how to hook up without it.
#interface BSAppDelegate ()
#property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
#end
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
self.viewController = [[BSViewController alloc] initWithNibName:#"BSViewController_iPhone" bundle:nil];
} else {
self.viewController = [[BSViewController alloc] initWithNibName:#"BSViewController_iPad" bundle:nil];
}
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:_viewController];
self.window.rootViewController = navigationController;
[self.window makeKeyAndVisible];
return YES;
}
update 0
Inside (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions, you can add uinavigationcontroller like this:
UIViewController *viewController = … //load your first view controller with the initial xib here
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
self.window.rootViewController = navigationController;

Incorrect Delegate for Controls on Main Application Window

I am developing an application in XCode 4.6.
To get text-change notifications from NSTextField controls I:
Put NSTextField control on window.
Connect control delegate to File's Owner via right-click in IB, drag from delegate to File's Owner.
Implement controlTextDidChange in window class.
For the application, the window class is my AppDelegate and File's Owner is NSApplication. For the modal dialog, the window class an NSWindowController and File's Owner is of the same type.
If I put a breakpoint in controlTextDidChange, in the AppDelegate class, it never fires. If I do the same procedure with a modal dialog it works fine.
I know in the main application window case the delegate for the control is not my AppDelegate.
What am I doing wrong in hooking up my control delegate in the main window? I must be missing something simple. Is File's Owner the correct delegate to set for controls?
Any help would be appreciated.
Here is some code as requested.
// AppDelegate.h
// SimpleApplication
#import <Cocoa/Cocoa.h>
#import "SimpleTest/SimpleTest.h"
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (assign) IBOutlet NSWindow *window;
#property (assign) IBOutlet NSTextField *textField;
#end
// AppDelegate.m
// SimpleApplication
#import "AppDelegate.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Not much to do here for now.
}
// Breakpoint set in this function never fires.
- (void)controlTextDidChange:(NSNotification *)obj
{
NSMutableString* description= [[NSMutableString alloc] init];
id aDelegate= [_textField delegate];
Class delegateClass= [aDelegate class];
[description setString:[delegateClass description]];
[description release];
}
// To provide some information about the delegates.
- (IBAction)textChange:(id)sender
{
NSTextField* theTextField= (NSTextField*)sender;
NSMutableString* description= [[NSMutableString alloc] init];
id aDelegate= [theTextField delegate];
Class delegateClass= [aDelegate class];
[description setString:[delegateClass description]];
[description release];
}
#end
Here is a shot of the right-click information for the NSTextField on the main window -
Identity inspector shows File's Owner as NSApplication, which is what I see in the debugger when I put a breakpoint in textChange and hit return in the text field. However, self, the implementor of controlTextDidChange, is AppDelegate. By contrast, in a modal dialog, self and File's Owner are the same object, derived from NSWindowController.
So, the upshot is that I do not have the correct delegate assigned to the control in the main window - how do I do that?
Can you post some code?
When using delegates make sure you specify that a class implements the required protocol.
#interface MyClass : NSObject <SomeProtocol>
Also make sure you are creating a property to store the delegate.
#property (strong, nonatomic) id<SomeProtocol> delegate;
RE this:
Note that although NSControl defines delegate methods, it does not
itself have a delegate. Any subclass that uses these methods must have
a delegate and the methods to get and set it. In addition, a formal
delegate protocol NSControlTextEditingDelegate Protocol also defines
delegate methods used by control delegates.
...
These include: controlTextDidBeginEditing:, controlTextDidChange:, and controlTextDidEndEditing:
Oh, wow - in adding more detail to my question, I think I figured out the answer. Instead of dragging from text field delegate to File's Owner, just drag to the blue cube that represents App Delegate!

NSWindowController's window released immediately

I'm trying to open a window using a NSWindowController in my app delegate.
I created a basic NSWindowController with an associated NIB and try to show the window that way:
#implementation MyAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Show the main window from a separate nib
MyWindowController * theWindowController = [[MyWindowController alloc] initWithWindowNibName:#"MyWindowController"];
[theWindowController showWindow:self];
}
#end
When I launch the app, the window of MyWindowController only appears for a fraction of second (seems to be released as soon as it launches).
Using ARC, how could I force the window to stick around and not be flushed right away? I do not use NSDocuments and I want to be able to use many of these MyWindowController concurrently.
You need to add a property to your app delegate (or some other object that's going to stick around for the lifetime of your app) that retains theWindowConroller. For example:
#interface MyAppDelegate : NSObject
#property (strong, nonatomic) MyWindowController * windowController;
#end
Then set this property when you initialize the window controller.
#implementation MyAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Show the main window from a separate nib
self.windowController = [[MyWindowController alloc] initWithWindowNibName:#"MyWindowController"];
[theWindowController showWindow:self];
}
#end

Creating a UITabBarController using a NIB outside of AppDelegate?

Still new to iOS programming, and despite copious amounts of research, I have run in to another roadblock.
What I want to implement:
I want a UITabBarController that gets loaded when I navigate from the main UI. I would also like to use a NIB to define its properties.
All of the examples I can find put the UITabBarController in the AppDelegate, but I would not like to load it unless it gets used. I also dont know if all of the UIGestureRecognizers would remain active if I just did it modally (I cant get a working implementation).
What I have so far
First, I load an initial loading view from AppDelegate
AppDelegate.h
#class InitialViewController;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) UIViewController *viewController;
#end
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[InitialViewController alloc] initWithNibName:#"InitialViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
From this view, as I am just making a skeleton of the UI, I have two buttons, one goes to what would be the main interface, and the other to the UITabBarController.
InitialViewController.h
#interface InitialViewController : UIViewController
- (IBAction)toMain:(id)sender;
- (IBAction)toTabs:(id)sender;
#property (strong, nonatomic) UIViewController *mviewController;
#property (strong, nonatomic) UIViewController *tviewController;
#end
InitialViewController.m
- (IBAction)toMain:(id)sender {
self.mviewController = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
[[[UIApplication sharedApplication] delegate] window].rootViewController = self.mviewController;
}
- (IBAction)toTabs:(id)sender {
self.tviewController = [[tabViewController alloc] initWithNibName:#"tabViewController" bundle:nil];
[[[UIApplication sharedApplication] delegate] window].rootViewController = self.tviewController;
}
On loading MainViewController, it behaves exactly like I want. But when I load the tab view, I get one long tab at the bottom and a black background. I can add in things in viewdidload, like changing the background color, but no actual tabs or views linked to the tabs in the XIB.
I suspect there is something I am missing in two areas: in the tab .h, and some linking associated with that in interface builder. Or setting a new rootViewController isnt enough.
tabBarController.h
#import <UIKit/UIKit.h>
#interface iPodViewController : UITabBarController <UITabBarControllerDelegate>
#end
If someone can point me in the right direction and/or show me an implementation that works, I would be most grateful.
-- as a note, when I go in to the tabbar.xib, and use the assistant editor, it opens InitialViewController.h --
Unlike other view controllers (e.g. UITableViewController) you should not subclass the UITabViewController. Therefore, unlike you other view controllers, you don't subclass and then make your subclass the owner of the nib, pointing at the view in the nib, with a customised view.
Instead, for whichever class that you want to own your UITabBarController, add a plain, vanilla UITabBarController as an outlet property on this class. (e.g. your app delegate).
Then create a nib file and drag a UITabBarController object into the nib. Set the owner of the nib to be the class that you want to own your tab bar controller (e.g. your app delegate) and connect the outlet you created as a property to the tab bar controller in the nib.
#interface myTabOwningClass
#property (strong, nonatomic) IBOutlet UITabBarController myTabBarControllerOutlet;
Now at the point you want to create and display your tab bar controller, use the following method:
[[NSBundle mainBundle] loadNibNamed:#"MyTabControllerNib" owner:myTabOwningClass options:nil];
This will initialise the property (i.e. myTabBarControllerOutlet in our example) on the owning class and load the tab bar controller from the nib, including all sub view controllers for each tab etc. that you have defined in the nib.

Resources