problems with creating an NSWindow - objective-c

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

Related

Cannot set view for NSOpenGLContext

(Sorry in advance for the seemingly large amount of code here) I'm trying to create a window with an OpenGL context with Cocoa, but I'm finding that I am unable to set the view property of the NSOpenGLContext that I create.
I cannot simply use an NSOpenGLView, since I need to interface with a C++ drawing backend and use multiple contexts. The code I post here is just me trying to get to grips with handling NSOpenGLContexts, but it will be used in a much larger project. This is why I'm instantiating NSApplication and my NSWindow manually rather than through a NIB/NSApplicationMain.
My main.m file:
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#import "Delegate.h"
int main(int argc, const char * argv[]) {
[NSApplication sharedApplication];
Delegate* dlg = [[Delegate alloc] init];
[NSApp setDelegate:dlg];
[NSApp run];
return 0;
}
I then have my delegate class, and I'll refrain from posting the file Delegate.h since it'll be pretty obvious what's there given these contents of Delegate.m:
#import <Cocoa/Cocoa.h>
#import "Delegate.h"
#import <OpenGL/gl.h>
#implementation Delegate
- (void) draw
{
[self.glContext makeCurrentContext];
glClearColor(1, 0, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
[self.glContext flushBuffer];
}
- (void) applicationDidFinishLaunching:(NSNotification *)notification
{
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
self.win = [[NSWindow alloc] initWithContentRect:NSMakeRect(30, 30, 300, 200)
styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask
backing:NSBackingStoreBuffered
defer:YES];
NSOpenGLPixelFormatAttribute glAttributes[] =
{
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAAccelerated,
0
};
self.glContext = [[NSOpenGLContext alloc] initWithFormat:[[NSOpenGLPixelFormat alloc] initWithAttributes:glAttributes]
shareContext:nil];
[self.glContext setView: [self.win contentView]];
printf("view:%p, contentView:%p\n", [self.glContext view], [self.win contentView]);
[self.win makeKeyAndOrderFront:nil];
[NSTimer
scheduledTimerWithTimeInterval:.1
target:self
selector:#selector(draw)
userInfo:nil
repeats:YES];
}
The window opens just fine. I am able to tell that -applicationDidFinishLaunching and -draw are being called. The window shows up empty however.
The printf call shows that the view property of self.glContext is equal to the address 0x0. I see no documentation or other forum threads about why I would be unable to set the drawable object of an NSOpenGLContext.
I have tried putting the NSOpenGLContext in its own subclass of NSView and adding that subclass as a subview of the window's content view, but with no success.
Try setting the defer parameter of -[NSWindow initWithContentRect:...] to NO. You may also wish to set the GL context's view after ordering the window on-screen.
Basically, -[NSOpenGLContext setView:] can fail if the view's window doesn't yet have a "device". When that's happened to me, it usually logs a message to the console about an "invalid drawable", but I haven't checked in recent versions of the OS.
Also, you need to register as an observer of the NSViewGlobalFrameDidChangeNotification notification from the view and, in response, call -update on the GL context object.

Binding a window created with the Cocoa Interface builder to a programmatically created one

I have an Objective C application which starts with loading a window created with Interface Builder:
//in main()
[NSApplication sharedApplication];
[NSBundle loadNibNamed:#"MainMenu" owner:NSApp];
[NSApp run];
In MainMenu.xib I have a window with a button. I want to create programmatically the second window when that button is pressed.
//in MainMenu.xib Controller.h
#class SecondWindowController;
#interface Controller: NSWindowController {
#private
SecondWindowController *sw;
}
- (IBAction)onButtonPress:(id)object;
#end
//in MainMenu.xib Controller.m
#import "SecondWindowController.h"
#implementation Controller
- (IBAction)onButtonPress:(id)object {
sw = [[SecondWindowController alloc] initWithWindowNibName:#"SecondWindow"];
[sw showWindow:self];
[[self window] orderOut:self];
}
#end
Where SecondWindowController inherits from NSWindowController. In SecondWindowController.h I have:
- (id)initWithWindow:(NSWindow*)window {
self = [super initWithWindow:window];
if (self) {
NSRect window_rect = { {custom_height1, custom_width1},
{custom_height2, custom_width2} };
NSWindow* secondWindow = [[NSWindow alloc]
initWithContentRect:window_rect
styleMask: ...
backing: NSBackingStoreBuffered
defer:NO];
}
return self;
}
And in the SecondWindow.xib I have nothing. When the button of the first window is pressed the first window disappears and the application closes. The reason I don't want to use the Interface builder for the second window is that I want to programmatically initialize it. Is this possible and if so what is the right way to accomplish this?
OK, I was initially confused with your use of initWithWindowNibName:#"SecondWindow" which will attempt to load the window from a NIB file, which you later mention you don't want to do.
Please use this to create your window:
- (IBAction)onButtonPress:(id)object {
if (!sw)
sw = [[SecondWindowController alloc] init];
[sw showWindow:self];
[[self window] orderOut:self];
}
Which will avoid creating multiple copies of the window controller, which you don't want (if you do then you'll need to store them in an array). Note the name sw is incorrect by convention; use either _sw or create setter/getter methods and use self.sw.
Initialize SecondWindowController like this:
- (id)init {
NSRect window_rect = NSMakeRect(custom_x, custom_y,
custom_width, custom_height);
NSWindow* secondWindow = [[NSWindow alloc]
initWithContentRect:window_rect
styleMask: ...
backing: NSBackingStoreBuffered
defer:NO];
self = [super initWithWindow:secondWindow];
if (self) {
// other stuff
}
return self;
}
Note: your variable names for origin/size of the new window were wrong; please review them.

programmatically create initial window of cocoa app (OS X)

Usually I am making iOS app but now I am trying to make an OS X app, and I am lost at the very beginning. Say the style I make the iOS apps are totally programmatic, there's no xib files or whatsoever just because that I have a lot more control by typing than dragging. However in OS X programming, it starts with some xib files with the menu items and a default window. There are quite a lot of items in the menu items so that's probably not something I want to mess around, but I want to programmatically create my first window myself.
So I did this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSUInteger windowStyleMask = NSTitledWindowMask|NSResizableWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask;
NSWindow* appWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(200, 200, 1280, 720) styleMask:windowStyleMask backing:NSBackingStoreBuffered defer:NO];
appWindow.backgroundColor = [NSColor lightGrayColor];
appWindow.minSize = NSMakeSize(1280, 720);
appWindow.title = #"Sample Window";
[appWindow makeKeyAndOrderFront:self];
_appWindowController = [[AppWindowController alloc] initWithWindow:appWindow];
[_appWindowController showWindow:self];
}
So here, I have created a window first, and use that windowController to init this window. The window does show up in this way, but I can only specify the inner elements, like buttons and labels here, but not in the windowController. It makes me feel bad so I tried another way.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
_appWindowController = [[AppWindowController alloc] init];
[_appWindowController showWindow:self];
}
and after this I want to set the other elements in the loadWindow: function in the windowController like this:
- (void)loadWindow
{
[self.window setFrame:NSMakeRect(200, 200, 1280, 720) display:YES];
self.window.title = #"Sample window";
self.window.backgroundColor = [NSColor lightGrayColor];
NSButton* sampleButton = [[NSButton alloc] initWithFrame:NSRectFromCGRect(CGRectMake(100, 100, 200, 23))];
sampleButton.title = #"Sample Button!";
[sampleButton setButtonType:NSMomentaryLightButton];
[sampleButton setBezelStyle:NSRoundedBezelStyle];
[self.window.contentView addSubview:sampleButton];
NSLog(#"Loaded window!");
[self.window makeKeyAndOrderFront:nil];
}
Unfortunately, this never works. the loadWindow: never gets called, nor windowDidLoad:. Where did they go?
And please don't ask why I don't use nibs. I wish to make some highly customized views inside, possibly OpenGL, so I don't think nibs can handle it. I am greatly appreciated if anyone could help. Thanks.
And also, who knows how to even start the menu items from scratch, programmatically?
I am using the latest Xcode.
I spent an entire Sunday digging into this problem myself. Like the person asking the question, I prefer coding iOS and OSX without nib files (mostly) or Interface Builder and to go bare metal. I DO use NSConstraints though. It is probably NOT WORTH avoiding IB if you're doing simpler UIs, however when you get into a more complex UI it gets harder.
It turns out to be fairly simple to do, and for the benefit of the "Community" I thought I'd post a concise up to date answer here. There ARE some older Blog Posts out there and the one I found most useful were the ones from Lap Cat Software. 'Tip O The Hat' to you sir!
This Assumes ARC. Modify your main() to look something like this:
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
int main(int argc, const char *argv[])
{
NSArray *tl;
NSApplication *application = [NSApplication sharedApplication];
[[NSBundle mainBundle] loadNibNamed:#"MainMenu" owner:application topLevelObjects:&tl];
AppDelegate *applicationDelegate = [[AppDelegate alloc] init]; // Instantiate App delegate
[application setDelegate:applicationDelegate]; // Assign delegate to the NSApplication
[application run]; // Call the Apps Run method
return 0; // App Never gets here.
}
You'll note that there is still a Nib (xib) in there. This is for the main menu only. As it turns out even today (2014) apparently no way to easily set the position 0 menu item. That's the one with the title = to your App name. You can set everything to the right of it using [NSApplication setMainMenu] but not that one. So I opted to keep the MainMenu Nib created by Xcode in new projects, and strip it down to just the position 0 item. I think that is a fair compromise and something I can live with. One brief plug for UI Sanity... when you're creating Menus please follow the same basic pattern as other Mac OSX Apps.
Next modify the AppDelegate to look something like this:
-(id)init
{
if(self = [super init]) {
NSRect contentSize = NSMakeRect(500.0, 500.0, 1000.0, 1000.0);
NSUInteger windowStyleMask = NSTitledWindowMask | NSResizableWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask;
window = [[NSWindow alloc] initWithContentRect:contentSize styleMask:windowStyleMask backing:NSBackingStoreBuffered defer:YES];
window.backgroundColor = [NSColor whiteColor];
window.title = #"MyBareMetalApp";
// Setup Preference Menu Action/Target on MainMenu
NSMenu *mm = [NSApp mainMenu];
NSMenuItem *myBareMetalAppItem = [mm itemAtIndex:0];
NSMenu *subMenu = [myBareMetalAppItem submenu];
NSMenuItem *prefMenu = [subMenu itemWithTag:100];
prefMenu.target = self;
prefMenu.action = #selector(showPreferencesMenu:);
// Create a view
view = [[NSTabView alloc] initWithFrame:CGRectMake(0, 0, 700, 700)];
}
return self;
}
-(IBAction)showPreferencesMenu:(id)sender
{
[NSApp runModalForWindow:[[PreferencesWindow alloc] initWithAppFrame:window.frame]];
}
-(void)applicationWillFinishLaunching:(NSNotification *)notification
{
[window setContentView:view]; // Hook the view up to the window
}
-(void)applicationDidFinishLaunching:(NSNotification *)notification
{
[window makeKeyAndOrderFront:self]; // Show the window
}
And Bingo... you're good to go! You can start working from there in the AppDelegate pretty much like you're familiar with. Hope that helps!
UPDATE: I don't create menus in code anymore as I've shown above. I've discovered you can edit MainMenu.xib source in Xcode 6.1. Works nice, very flexible and all it takes is a little experimentation to see how it works. Faster than messing around in code and easy to localize! See the picture to understand what I am on about:
See https://github.com/sindresorhus/touch-bar-simulator/blob/master/Touch%20Bar%20Simulator/main.swift
In main.swift
let app = NSApplication.shared()
let delegate = AppDelegate()
app.delegate = delegate
app.run()
Swift 4:
// File main.swift
autoreleasepool {
// Even if we loading application manually we need to setup `Info.plist` key:
// <key>NSPrincipalClass</key>
// <string>NSApplication</string>
// Otherwise Application will be loaded in `low resolution` mode.
let app = Application.shared
app.setActivationPolicy(.regular)
app.run()
}
// File Application.swift
public class Application: NSApplication {
private lazy var mainWindowController = MainWindowController()
private lazy var mainAppMenu = MainMenu()
override init() {
super.init()
delegate = self
mainMenu = mainAppMenu
}
public required init?(coder: NSCoder) {
super.init(coder: coder) // This will newer called.
}
}
extension Application: NSApplicationDelegate {
public func applicationDidFinishLaunching(_ aNotification: Notification) {
mainWindowController.showWindow(nil)
}
}
Override the -init method in your AppWindowController class to create the window and then call super's -initWithWindow: method (which is NSWindowController's designated initializer) with that window.
But I generally agree with the comments that there's little reason to avoid NIBs.

Best design pattern for a window opening another window in cocoa application

I am learning how to create osx applications with Cocoa/Objective-C. I am writing a simple app which will link together two different tutorials I have been going through. On start up a choice window loads with 2 buttons, one button loads one window and the other loads the other window. When either button is clicked the choice window closes.
The choice window controller object was added to the MainMenu.xib file so it is created at launch. The window is then opened using the awakeFromNib message.
I want the result of one button to open up the 'track controller' tutorial application from the ADC website. The action looks like this:
- (IBAction)trackButton:(id)sender {
TMTrackController *trackController = [[TMTrackController alloc] init];
[self.window close];
}
I added an init method to the TMTrackController class which looks like this:
- (id) init {
if (self = [super init]) {
[self showWindow];
TMTrack *myTrack = [[TMTrack alloc] init];
myTrack.volume = 50;
self.track = myTrack;
[self updateUserInterface];
return self;
}
else {
return nil;
}
}
- (void) showWindow {
if(!self.window) {
[NSBundle loadNibNamed:#"trackWindow" owner:self];
}
[self.window makeKeyAndOrderFront:self];
}
I am not sure this is the best way to be doing this as I know that the choiceController class will be released when it is closed thus getting rid of the TMTrackController class too. However even when I untick the 'release when closed' box of the ChoiceWindow.xib it breaks too.
What is the correct way to do this?
With xib s in the same project use:
#interface
#property (strong) NSWindowController *test;
#implementation
#synthesize test;
test = [[NSWindowController alloc] initWithWindowNibName:#"XIB NAME HERE"];
[test showWindow:self];
[home close];
It is not completely the same but this is my solution for such problems: Stackoverflow
Just ignore my statement in this answer regarding showing the window as a modal window. Everything else is still valid. This way you could have your personal window controller and it controls everything there is within the xib. This is a huge advantage for maintaining the project afterwards (and you keep to the application logic).

How is an AppDelegate instanciated?

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.