cocoa windowDidEnterFullScreen not being called - objective-c

I'm writing a Cocoa application which should work in fullscreen mode. I would like to detect the user setting the application window to fullscreen mode.
In Xcode,
in the Attributes Inspector, Full Screen value is "Primary Window"
in the File Inspector, Use Auto Layout is unchecked
the view of the main window has Autoresizes Subviews unchecked
I'm making the AppDelegate an NSWindowDelegate like this in AppDelegate.h
#interface AppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate>
and have a method like this in AppDelegate.m
-(void) windowDidEnterFullScreen:(NSNotification *)notification
{
vuMain.view.frame = NSMakeRect(0, 0, window.frame.size.width,
window.frame.size.height);
NSLog(#"AppDelegate - windowDidEnterFullScreen");
}
But this method is not getting called as there is no NSLog output. A watch set in the method does not get reached.
What am I doing wrong? What am I missing?
Please help! I'm rather new to Cocoa and am struggling for one full day with this.
Thanks

You need to actually make the AppDelegate the window's delegate. You either have to connect the window's delegate outlet to the AppDelegate in the NIB, or you need to set the window's delegate property programmatically.

Related

Why is this delegate method automatically called in Objective-C?

I'm going through this book called "cocoa programming for mac os x" and I just started with delegates. This whole thing with delegates is still a little bit wacky to me but I think I just need to let it settle.
However there was this one exercise where I should implement a delegate of the main window so that if resized height is always 2xwidth.
So I got 4 files:
AppDelegate.h
AppDelegate.m
WindowDelegate.h
WindowDelegate.m
AppDelegate are just the two standard files that get created when you open a new Cocoa project. I had to look up the solution because I didn't quite know how to accomplish this task.
The solution was just to create a new cocoa class, "WindowDelegat.h/.m" and add this to it's implementation file:
- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize {
NSSize newSize = frameSize;
newSize.height = newSize.width * 2;
return newSize;
}
Then I opened the interface builder, added a new object and made it my WindowDelegate. I then had to ctrl drag from the WindowDelegate to the actual window and made it the window's delegate.
Clicked run and it worked. Yay! But why?
First I thought that "windowWillResize" is just one of these callback functions that get's called as soon as the window is resized but it isn't. Normally methods get invoked because the general lifecycle of an program invokes them or because they are an #IBAction, a button or different control elements.
But "windowWillResize" is non of them. So why is it called?
EDIT: Problem solved! Thanks a lot!
Now I'm trying to connect the delegate to the window programmatically. Therefore I deleted the referencing outlet from WindowDelegate to the actual window in interface builder. It works but I just want to verify that this it the correct way how it's done:
AppDelegate.h
#import <Cocoa/Cocoa.h>
#import "WindowDelegate.h"
#interface AppDelegate : NSObject <NSApplicationDelegate>
#end
AppDelegate.m
#import "AppDelegate.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#property (strong) WindowDelegate *winDeleg;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
- (void)awakeFromNib {
[_window setOpaque:NO];
NSColor *transparentColor = [NSColor colorWithDeviceRed:0.0 green:0.0 blue:0.0 alpha:0.5];
[_window setBackgroundColor:transparentColor];
NSSize initialSize = NSMakeSize(100, 200);
[_window setContentSize:initialSize];
_winDeleg = [[WindowDelegate alloc] init];
[_window setDelegate: _winDeleg];
}
#end
WindowDelegate.h
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#interface WindowDelegate : NSObject <NSWindowDelegate>
#end
WindowDelegate.m
#import "WindowDelegate.h"
#implementation WindowDelegate
- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize {
NSSize newSize = frameSize;
newSize.height = newSize.width * 2;
return newSize;
}
- (id)init {
self = [super init];
return self;
}
#end
Why does the #property of WindowDelegate need to be strong?
And isn't my winDeleg an object? Why do I have to access it through _winDeleg when it's an object. I though the underscore is used to access variables?
Thank you for your help!
Clicked run and it worked. Yay! But why?
Because instances of NSWindow have a delegate property that can point to any object that implements the NSWindowDelegate protocol, and that protocol includes the -windowWillResize:toSize: method.
Read that a few times. The reason it's important is that you can create your own object, say that it implements NSWindowDelegate, implement -windowWillResize:toSize:, and set that object as your window's delegate. Then, whenever the user resizes the window, your method will be called and can modify the proposed new size.
Normally methods get invoked because the general lifecycle of an program invokes them or because they are an #IBAction, a button or different control elements. But "windowWillResize" is non of them. So why is it called?
This really isn't so different. Think of delegates as "helper objects." They let you customize the behavior of an object without having to create a whole new subclass. The NSWindowDelegate object is essentially a contract that the NSWindow promises to follow: whenever certain things happen, such as the user resizing the window, the window will call certain methods in its delegate object, if the delegate exists and implements those methods. In the case of NSApplication, a lot of those delegate methods are application lifecycle events, like the app starting up or quitting or getting a message from the operating system. In the case of NSWindow, delegate methods correspond to interesting events that can happen to a window, like the user moving it, hiding it, showing it, maximizing it, moving it to a different screen, etc. Other classes, like text views or network connections or movie players, have their own sets of interesting events and their own delegate protocols to match.
Note that methods marked IBAction really aren't delegate methods, they're just methods that get called by objects like controls that use a target/action paradigm. The IBAction keyword lets the IDE know which methods it should present as possible actions for things like buttons. You often find actions in window controllers and view controllers, and those objects frequently act as a delegate for some other object, but the actions themselves aren't part of the delegate protocol. For example, NSTableView takes a delegate object that determines how the table will act and what's displayed in it. It often makes sense for the view controller that manages the table to be the table's delegate, and that same view controller might also manage some buttons and contain the action methods that said buttons trigger, but the actions aren't part of the NSTableViewDelegate protocol and you therefore wouldn't call them delegate methods.

(OSX/Cocoa) How to set controller for main window

I've created a new cocoa application using .xib files (not storyboarded, the app has to be backwards compatible with mavericks/mountain lion) and I would like to have a custom windowcontroller for the main window. Is this possible? I can't seem to find a way to connect the window to my desired custom controller. The window has a reference outlet in AppDelegate, however I need a custom NSWindowController for this window as it doesn't open on application launch. The application launches silently as a menu bar app and the main application is launched via button press in the drop down from the menu bar.
Is there a way to link a controller to the window in the interface builder? Or do I have to do something along the lines of :
wc = [[CustomWindowController alloc] initWithWindowNibName:#"Main"];
Thanks!
Yes, open up Utilities (the right panel) in Interface Builder, and at the bottom click on the Object Library (circle with square in it).
Search for Object (blue cube), and drag it into your Document Outline (the panel on the left inside of interface builder)
From there, select that object you just created, and change the Class in the Identity Inspector to be the window controller you want.
Finally you can go into the Connections Inspector and connect your window to the window outlet
I can't seem to find a way to connect the window to my desired custom
controller. The window has a reference outlet in AppDelegate, however
I need a custom NSWindowController for this window as it doesn't open
on application launch.
Another way:
1) Delete the window in MainMenu.xib. Delete the window property in AppDelegate.m--because you deleted the window, it is no longer relevant.
2) File>New>File>Cocoa Class. Enter a class name, e.g. MainWindowController; select "Subclass of: NSWindowController"; check "Also create .xib file for user interface".
3) Create an outlet in AppDelegate.m:
#import "AppDelegate.h"
#import "MainWindowController.h"
#interface AppDelegate ()
#property (strong, nonatomic) MainWindowController* windowController;
#end
4) Declare an action in AppDelegate.h:
#interface AppDelegate : NSObject <NSApplicationDelegate>
-(IBAction)launchWindow:(id)sender;
#end
and implement it in AppDelegate.m:
- (void)launchWindow:(id)sender {
[self setWindowController:[[MainWindowController alloc]
initWithWindowNibName:#"MainWindowController"]];
[[self windowController] showWindow:nil];
}
5) In MainMenu.xib, hook up the Menu Item to the launchWindow() action: control drag from the Menu Item to the AppDelegate object and select launchWindow.
Create the controller and make it extend from NSWindowController.
In your xib file select the File's Owner and set it to your custom class.
Select your NSWindow and connect it to the File's Owner.
To open the window:
In your .h:
#property (strong, nonatomic) YourWindowController *yourWinController;
In your .m:
self.yourWinController = [[YourWindowController alloc] initWithWindowNibName:#"YourWindowController"];
[self.yourWinController showWindow: nil];

Is it possible to have a custom NSWindowController on launch?

In this answer is said it is possible to have a custom NSWindowController by removing the window from MainMenu.xib and instantiating the window controller 'manually' from applicationDidFinishLaunching:. But it also says:
To stop the default window from showing I just delete it. There's probably a better way but I don't know that.
Is there a better way? What is that better way, should it exist? Is it considered 'normal' practice to get your own window controller?
To do this, you would usually subclass NSWindowController and change the File's Owner class to your WindowController subclass in the nib.
EDIT:
If you aren't doing a document-based app, and just want an NSWindowController of your own to do on-demand loading of Nibs (completely reasonable), then you'd delete the window from your nib and instantiate an NSWindowController subclass programmatically, using it explicitly to do your window loading...
#implementation MyApplicationDelegate {
MyWindowControllerSubclass *windowController;
}
-(void)applicationDidFinishLaunching:(NSNotification *)notification {
windowController = [[MyWindowControllerSubclass alloc] initWithWindowNibName:#"MyWindowNib"];
[windowController showWindow:nil];
[windowController.window makeKeyAndOrderFront:nil];
}
I was running into the same issue and I want to show you my own solution.
Create a normal Cocoa Application (not Document Based)
Go to MainMenu.xib an delete the Window
Go ahead and create a new file, User Interface -> Window
After that create a subclass of NSWindowController
Open the just created xib file and set the Custom Class in the Identity inspector to the just created subclass of NSWindowController
Right click on File's Owner and connect the window property to the actual window
Now go to the AppDelegate an create an instance variable that holds you CustomWindowController
Last thing you have to do is instantiate your CustomWindowController self.customWindowController = [[AccountWindowController alloc] initWithWindowNibName:#"CustomWindow"]; and show the Window [self.customWindowController showWindow:nil] in - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
Here is an example project https://www.dropbox.com/s/ft3t7w72806tnoe/CustomWindowController.zip
I actually found another way: NSWindowController has the method -initWithWindow:. Because the App Delegate has a property window which is linked to the window from MainMenu.xib on startup, it was easy to link it to my WindowController:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
MyWindowController *wincon = [[MyWindowController alloc] initWithWindow:window];
}
I have yet to research this, but I don't get any errors.

Close button during modal sheet

In Objective C (Cocoa) I have an app running with a modal sheet, but I want to allow the app to quit even when the sheet is displayed (contradicting the definition of modal I think, but I like the animated effect of modal sheets).
I'm already using the -setPreventsApplicationTerminationWhenModal method and it works fine, but I'm wondering... is there any way to keep the close button enabled? The little circle usually red-colored close button that comes with all windows in the top left corner (side by side with minimize and maximize)? Right now it's completely disabled when the sheet is running, and it would be awesome if there is a way to enable it.
Thanks!
Use a delegate method to close the Modal View. You declare the delegate on your modal view controller and that delegate method dismisses the ModalViewController
In the Modal ViewController Interface File:
#protocol MyViewControllerDelegate
-(void)dismissModal;
#end
Then declare the delegate as a class property in the Modal ViewController:
#property (nonatomic, retain) id <MyViewControllerDelegate> delegate;
Now, declare your parent ViewController as a proper delegate implementer for the Modal ViewController:
#interface MyParentViewController : UIViewController
Then in the calling (parent) ViewController implement the delegate method in the implementation file:
-(void)dismissModal
{
// Dismiss the Modal ViewController that we instantiated earlier
[self dismissModalViewControllerAnimated:YES];
}
That should do it. The advised way to handle this is through delegate methods (and delegate methods are so handy to use whenever a process in one controller needs to fire a method in another controller. It is well worth the time to get familiar with using delegates to get work done in Obj C

Objective C Delegate for the Main Application Window

So I'm trying to do this exercise where I need to set a delegate for the main window. The purpose is to make sure that when the user resizes the window, it's always twice as wide as it is high.
This is my AppController.h file:
#import <Cocoa/Cocoa.h>
#interface AppController : NSObject
{
NSWindow *windowWillResize;
}
#end
and this is my AppController.m file:
#import "AppController.h"
#implementation AppController
- (id) init
{
[super init];
windowWillResize = [[NSWindow alloc] init];
[windowWillResize setDelegate:self];
return self;
}
- (NSSize) windowWillResize:(NSWindow *)sender
toSize:(NSSize)frameSize;
{
NSLog(#"size is changing");
return frameSize;
}
#end
However, I can remove the line
[windowWillResize setDelegate:self];
since I set the delegate in Interface Builder, but I'm not sure why this works.
How does windowWillResize know that I'm referring to the main application window since I'm doing a completely new
windowWillResize = [[NSWindow alloc] init];
I have a feeling that I am completely doing this wrong. Could someone point me in the right direction? Thanks!
Indeed, you don't need to create a NSWindow *windowWilResize since a newly created Cocoa app already has a main window. You don't need to implement an -init method either.
You only need to set you appController as a delegate of your main window in Interface Builder and to implement the -windowWillResize: method in your appController.
If you are familiar with french language, you can take a look at a blog entry I have written on this subject: Délégation en Cocoa.
You're leaking an instance of NSWindow. In -init you create an NSWindow instance. However, that is not used because when the NIB loads, it sets up all the connections that you specified in Interface Builder and you start using the window from the NIB instead. Do not create a window object in code - Interface Builder does it for you! :-)
In fact, it's not quite "instead"; your app controller is now the delegate for both NSWindow instances - the one that comes from the NIB and the one you instantiated in -init. However as the in-code NSWindow is never used anywhere else, it's still redundant and should be removed.
If you just want to maintain the aspect ratio of the window you can use either of these two NSWindow methods:
setAspectRatio:(NSSize)
setContentAspectRatio:(NSSize)
The first method locks the entire window size, including the title bar. The second one just the content. You can call this method during the initialization of your window inside the delegate (for example: -applicationDidFinishLaunching)