Open NSPopover in windowDidLoad - macos

I am trying to display an NSPopover in a mac application when the window opens to give the user an instruction, but the popover will not display.
If I call the exact same function from a button press then the popover successfully displays, but when I call it in windowDidLoad it doesn't.
I can see that the control I am presenting it from has a bounds, so I don't think that is the problem. I've also checked that the behaviour of the popover is not transient, so it shouldn't close without intervention.
In my function I'm passing some variables into a custom initialiser, but it is basically just this:
CustomViewController *instruction = [[CustomViewController alloc] init];
[instruction.popover showRelativeToRect:[aField bounds] ofView:aField preferredEdge:NSMaxXEdge];
The init method simply calls the following, and the custom view and controller are hooked up in the NIB.
[super initWithNibName:#"InstructionalView" bundle:nil]
Has anyone come accross this?

You're programmatically allocating and init-ing your CustomViewController object, but the popover doesn't have a chance to load from the nib before you display it (on the line directly after the alloc & init).
Between those two lines, force a load via loadView, like this:
[instruction loadView];
You may also want to make certain that "instruction.popover" isn't nil when you try to display it, too.

Related

Print is printing entire window, not the View

I have a simple program, for making sure the print works.
-Subclassed NSObject for a Controller, "ViewController.
-Subclassed NSView, View
Added a custom view to the window, set class to View.
Made ViewController delegate for View.
Added Object in IB, set class to ViewController.
Added IBOutlet to Delegate section of ViewController, connected it to the the custom view on the window.
In View, have a simple [myString drawInRect: rect], where myString is defined in the init as #"Hi".
When I run, the program prints "Hi" in the view. When I click the "Print" menu item, the print preview shows the entire window.
More: I hade an earlier test program which had no print code, I ran it and the print preview showed only the view. I've got through both codes and cannot find a difference, so I am lost at why one is working and the other is not.
Do any of you know why the print preview would show the entire window instead of the view?
[EDIT]-----
I also created an extremely simple program to check and have the same issue.
Subclassed NSView, MainView
Added Custom View to Window, set class to MainView
Added [str drawInRect: dirtyRect withAttributes:nil];
(note; NSString *str = #"Hello";).
[More Information]-------
I added a print method as follows to the NSView object:
-(void)printPDF{
NSRect r = [self bounds];
[[NSPrintOperation printOperationWithView:self] runOperation];
[self dataWithPDFInsideRect:r];
}
Added a button to the window, linked it to an IBAction method in the ViewController:
-(IBAction)printToPDF:(id)sender{
[view printPDF];
}
In the ViewController I have:
IBOutlet View *view;
This works! So, why does the default "print" menu item print the entire Window?
By default, you don't have to write any printing code and the printing just works. But, it provides default behavior (aka, print the entire window).
If you want custom printing behavior, you'd have to write your own printing method.(like you did in your extremely simple program). And you can link the default print menu item to your own printing method.

NSWindow tracking

I would like to track each time a certain window appears (becomes visible to the user) in a OS X app. Where would be the most adequate place to call the tracker?
windowWillLoad, maybe?
I expected to find something like windowWillAppear but it seems I'm thinking too much iOS.
How about getting notification such as NSWindowDidBecomeMainNotification, By main I guess the one which is top most on screen directly visible by user.
see : Apple Documentation
Yes, one would expect that a window would notify its delegate or its controller with a windowWillAppear or windowDidAppear message, or post a documented notification like NSWindowDidAppearNotification. But alas, none of those exist. I filed a bug report with Apple and was given the advice to use a storyboard and a view controller instead. This is unhelpful in legacy apps that already use a bunch of window controllers and xibs.
You could subclass NSWindow and override orderWindow:relativeTo: to send a notification. Most, but not quite all, of the messages that make a window show itself ultimately go through this method, including orderBack:, orderFront:, makeKeyAndOrderFront:, and -[NSWindowController showWindow:]. But orderFrontRegardless does not go through orderWindow:relativeTo:, so you would also want to override that for completeness.
Another way to be notified is to make a subclass of NSViewController that controls some view that's always visible in the window. The view controller will receive viewWillAppear and viewDidAppear.
If you're subclassing NSWindow or NSViewController already for some other reason, either of these is a reasonable solution.
If you're not subclassing NSWindow already, and don't have an NSViewController subclass for a view that's always visible in the window, then another way is to use Cocoa bindings to connect the window's visible binding to a property one of your objects. For example, I have a custom NSWindowController subclass. I gave it a windowIsVisible property:
#interface MyWindowController ()
#property (nonatomic) BOOL windowIsVisible;
#end
and I implemented the accessors like this:
- (BOOL)windowIsVisible { return self.window.visible; }
- (void)setWindowIsVisible:(BOOL)windowIsVisible {
NSLog(#"window %# became %s", self.window, windowIsVisible ? "visible" : "hidden");
}
and in awakeFromNib, I bind the window's visible binding to the property like this:
- (void)awakeFromNib {
[super awakeFromNib];
[self.window bind:NSVisibleBinding toObject:self withKeyPath:NSStringFromSelector(#selector(windowIsVisible)) options:nil];
}
When the window becomes visible, the setWindowIsVisible: setter is called with an argument of YES. Note that if the whole app is hidden and reappears, the setter is called again, even though it wasn't called with argument NO when the app was hidden. So be careful not to assume the window was previously hidden.
Also, the binding might create a retain cycle, so you should probably unbind it when the window is closed, unless you want to keep the window and controller around. Note that the window does post NSWindowWillCloseNotification when it's closing, so you don't need any special magic to detect that.

Receive window notifications

I have an NSWindow set up in Interface Builder. I have set the class of File's Owner to my NSWindowController and linked the window property of the controller to my NSWindow.
My controller implements NSWindowDelegate.
Now, in my controller, I have added the following:
- (void)windowDidLoad
{
[super windowDidLoad];
[self.window setDelegate:self];
}
- (void)windowDidBecomeMain:(NSNotification *)notification
{
NSLog(#"Did become main.");
}
Still, -windowDidBecomeMain: isn't called. Does anyone know why this is?
EDIT:
Trying to show a window from AppDelegate on launch. The main nib (declared in Info.plist) contains a menu item only which is linked to the AppDelegate. In the application delegate, I show an icon on the status bar and when this icon is clicked, I display the menu from the main nib.
In the application delegate, I also want to display a window which should have a window controller assigned to take care of the logic.
I believe that when this works, I will receive my window notifications.
Now, the following code doesn't show the window and I can't figure out why.
DemoWindowController *dwc = [[DemoWindowController alloc] initWithWindowNibName:#"DemoWindowController"];
[dwc showWindow:self];
Note that self is the application delegate.
I suspect your problem is due to the fact that your window controller is not actually the object that is the nibs file owner.
When you change the class in interface builder you are telling it what outlets and actions are available (which is why you are able to drag to the window outlet) but you are still responsible for passing in this object yourself.
In the case of a non-document based application, you will have a main method which calls NSApplicationMain. What this does is basically look up and load the window nib that is specified in your info.plist file and pass the current NSApplication instance to this nib as the files owner (so even though you changed the class type to NSWindowController, the object being passed in is actually of type NSApplication).
The easiest way to fix your problem is to get rid of your window controller for now (as it isn't actually doing anything yet).
You should implement the -windowDidBecomeMain: method in your app delegate. Then Ctrl+drag from your window to your appDelegate to set it as the delegate of the window to get your notifications.
Update
To answer your question regarding the WindowController beware of the following two issues:
You are creating your window controller variable (dwc) in your applicationDidFinishLaunching: method. This is released the moment you leave the method taking your window with it. Create an instance variable to hold onto the window controller instead.
Ensure that your second window nib has its file owner set to NSWindowController (or your window controller type) and that its window outlet is connected to the window in the nib file.
Your window should now display.

Cocoa: setters in an NSViewController view

I'm using an NSViewController class with a single view in it to display a progress indicator bar and some text fields. I'm trying to use progressIndicator setMaxValue:and theTextField setStringValue: but neither of these are doing anything.
I've done this before and I've checked and rechecked, it's fairly straightforward, the fact that it's not working makes me think that it has to do with the fact that the class is NSViewController. Which is why I tried
Timers *aTimer = [[Timers alloc] init];
[aTimer.timerNameLabel setStringValue:#"name"];
[aTimer.progressIndicator setMaxValue:x];
in the app delegate which is an NSObject class, but that didn't work either.
I've tried looking around the NSViewController documentation but I can't find anything that says it can't set those values so I don't know what's happening. What am I doing wrong?
You probably want to use -initWithNibName:bundle: instead of a regular init to initialize your custom nib.
EDIT: It seemed the problem was due to the view not being queried before getting other objects. By calling [myController view] you actually load the nib, which isn't done automatically when you initialize the view controller. So before you can use any element of the view, you need to call [myController view]

NSTableView guarantee data display

The main GUI of my app has a NSTableView, controlled by its own controller class. This class loads data into the table once by launching and parsing the output of a process. This is done in the
-(void)awakeFromNib
method of the controller class. The table shows up right on startup, so it means that before the GUI is displayed, the parsing of the process output should be done.
Now the problem is that when the GUI is shown (my app startup is slow, which means that process has been launched), the table is sometimes empty or is sometimes loaded with data. This is random. So how do I get the table to be always loaded with data on startup?
reloadData does not work! and I also use [NSTask waitUntilExit];
This is how I often make an app "load completely" before the window shows:
Uncheck the "visible at startup" for your window in Interface Builder. Then create an IBOutlet NSWindow *window; in your AppDelegate. And finally, in your AppDelegate, implement the applicationDidFinishLaunching: method and add [window makeKeyAndOrderFront:self]; into this method.
This should work, because applicationDidFinishLaunching: will always be called after awakeFromNib.

Resources