When I try launching my app through Spotlight without building it through XCode, the NSWindowController displays the unmodified nib (with the standard gray background color, etc) for a split second before windowDidLoad finishes. This looks awful, since views are in the wrong places and aren't colored correctly.
I tried removing everything from windowDidLoad to see if something in there was slowing it down, but that didn't improve things. I also tried moving the setting of the background color to initWithWindowNibName, but that didn't help either.
Is there a way to delay showing the window while it finishes loading?
Here's the code I'm using to initialize the NSWindowController:
self.windowController = [[WindowController alloc] initWithWindowNibName:#"WindowController"];
[self.windowController showWindow:self];
[[self.windowController window] makeKeyAndOrderFront:nil];
Disable NSWindowController's "visible at launch" property in Interface Builder.
(Of course, I post the question then immediately figure out the answer.)
Related
Objective-C desktop Cocoa ap. The application is a highly specific edge case (a standalone kiosk with a one-button interface). I mention this first in hopes of avoiding the HIG lecture. ;)
I am using a modal sheet view to display a PDF document over top of my usual content. The code is very simple:
[docViewerHostWindow beginSheet:docViewer completionHandler:nil];
DocViewerHostWindow is an NSWindow accessed as the parent of the NSView that calls the sheet. If it matters, "docViewer" is a subclass of NSWindow containing a PDFView.
When the application is running in a window, everything functions as it should. When the application is running fullscreen things work as advertised but there is a visual glitch: the entire content behind the model shifts to the right until the sheet is dismissed. It appears this is deliberate (and animated) but I can't figure out where it is coming from or how to disable it.
I enter fullscreen this way:
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO],NSFullScreenModeAllScreens,nil];
[self enterFullScreenMode:[NSScreen mainScreen] withOptions:options];
This basic NSLog illustrates the problem:
NSLog(#"Before: %f,%f",docViewerHostWindow.frame.origin.x,docViewerHostWindow.frame.origin.y);
[docViewerHostWindow beginSheet:docViewer completionHandler:nil];
NSLog(#"After: %f,%f",docViewerHostWindow.frame.origin.x,docViewerHostWindow.frame.origin.y);
**Log when running windowed (same origin before and after) **
2015-08-09 22:52:46.641 Before: 311.000000,491.000000
2015-08-09 22:52:47.050 After: 311.000000,491.000000
**Log when running fullscreen (origin shifts in fullscreen) **
2015-08-09 22:52:46.641 Before: 0.000000,0.000000
2015-08-09 22:52:47.050 After: 80.000000,-23.000000
I have tried to force the origin of the window both before and after opening the sheet, but this didn't help. I'm not even sure where to look - any clues on what is causing this and why?
Update: To be clear I am NOT talking about the default animation on the sheet itself (as described in this question) rather this is something that happens to the host window, but only in fullscreen.
The effect seen is the application moving out of the way of the (invisible) dock and the menubar (thank you #KenThomases whose comment helped figure that out). In addition to the accepted answer above, I found another solution:
Apple provides a number of kiosk mode tricks which are helpful here, in particular note their example regarding hiding the menubar and dock by adding code to awakeFromNib.
However, this is less than ideal in a non-kiosk ap, what we'd like is to have the kiosk options only in full screen mode. Fortunately this is possible by passing the options to enterFullScreenMode:withOptions:. The regular presentation mode options are restored when we exitFullScreenMode:withOptions
The documentation is a bit thin, but this did the trick:
NSApplicationPresentationOptions kioskOptions = NSApplicationPresentationHideDock + NSApplicationPresentationHideMenuBar;
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO],NSFullScreenModeAllScreens,
[NSNumber numberWithInteger:kioskOptions],NSFullScreenModeApplicationPresentationOptions, nil];
[self enterFullScreenMode:[NSScreen mainscreen] withOptions:options];
The issue is apparently related to the window avoiding the Dock.
I think it's also related to the fact that you're putting a view, not a window, in full-screen mode. When you do that, the view is sort of moved out of its normal window. But then, when you begin a sheet on the view's window, you're bringing that back into the picture, so to speak. (You might check if the view's window property is actually the same window it was before it entered full-screen mode. If it's not, you really have no business attaching a sheet to it.)
I would recommend that you use a borderless window, positioned to cover the screen, at a window level of NSStatusWindowLevel + 1. That's the technique recommended in Apple's OpenGL Programming Guide for Mac: Drawing to the Full Screen (which is, admittedly, a somewhat different context that you're working in). So, you would not use -[NSView enterFullScreenMode:withOptions:]. If you want to set the application's presentationOptions for kiosk-mode-type behavior, you can do that, too.
Last week I spent a good amount of time fiddling around with the following problem. I solved it but since I couldn't find an answer here and I want to share my solution for anybody who runs into this.
Problem: My application runs in the background (menubar) and its main NSWindow contains all the preferences of my application. You close the NSWindow by clicking the close button in the top-left. After closing the NSWindow and reopening it, all the NSControls were not visually responding. The actions worked accordingly, but they where not updating.
I tried the following:
Unchecking "Release on close" in IB for NSWindow
Reloading NSWindow from NIB
Strong references for all NSControls
Calling "Become first responder" on NSControls
Reloading all the parenting NSViews
Implementing all the delegates hooking up each NSControl (Worked for NSControl, but was way too much work to use it for each and every one)
Disabling and then reenabling the NSControls
I'll post my solution below for the records.
My solution to this problem was to subclass NSWindow and overwrite the following methods:
- (void)performClose:(id)sender{
[self orderOut:nil];
}
- (void)close{
[self orderOut:nil];
}
Now the NSWindow is hidden / ordered out and you just have to order it back in when displaying it.
If I create a WebView and add it to NSThemeFrame like so:
Let window be the NSWindow instance I'm adding to:
NSView * themeFrame = window.contentView;
[themeFrame.superview addSubview:myWebView];
Then any :hover tags of elements in the page I load doesn't work anymore. However, the :hover does appear on mouseDown/mouseUp. So maybe this is a refresh issue. I tried calling [myWebView setNeedsDisplay:YES] in mouseMoved: but no luck.
Is there something special about contentView? I tried the same thing with an NSView and overrode its mouseMoved: method while calling setNeedsDisplay and the NSView handled mouseMove fine. Is there something special about :hover?
Any ideas or ramblings are welcome!
I might have the same problem as Cocoa WebView on os X not firing mouse hover events without having to click and hold left mouse key but there's no answer there.
I had a similar problem and after debugging and reading the source of WebKit, I found a solution. The crucial part seems to the WebHTMLView class, specifically the method -[WebHTMLView _updateMouseoverWithEvent:]. There's a check whether the view's window is a key window and sure enough, my window wasn't key even though I called makeKeyWindow on it.
My window was a child window of a document window and to solve the hover issue, I created a NSWindow subclass and overrode isKeyWindow like this:
- (BOOL)isKeyWindow
{
return [super isKeyWindow] || [[self parentWindow] isKeyWindow];
}
This made hover work in WebView in my document's child window.
I have an NSView in IB which sits above the app window. I have a subclass of NSView (AddSource) which I assign to the NSView.
On awakeFromNib I instantiate the view:
//add a new Add Source class
addSourceView = [[AddSource alloc] initWithFrame:NSMakeRect(0.0, 959.0, 307.0, 118.0)];
[[winMain contentView] addSubview:addSourceView];
in addSourceView's drawRect method I am adding a white background to the view:
[[NSColor whiteColor] set];
NSRectFill(rect);
[self setNeedsDisplay:YES];//added this to see if it might solve the problem
In winMain's contentView I have a NSButton that when clicked slides the addSourceView onto the window:
NSRect addSourceViewFrame = [addSourceView frame];
addSourceViewFrame.origin.y = 841.0;
[[addSourceView animator] setFrame:addSourceViewFrame];
But it seems as if the app is painting over the IBOutlets I placed on the NSView in IB. If, in IB, I repoistion the NSView so that it is on screen when the app launches everything works fine, the IBOutlets are there as well as the background color.
I'm not sure why this is happening. I've done this before with no problems. I must be doing something different this time.
Thanks for any help.
*note - on the 3rd screen capture, when I say this is what the app looks like when opened, that's when I hard code the Y position of the NSView. When it is functioning correctly it should open as screen capture 1.
Most likely your buttons and custom view are siblings, i.e. they are both subviews of your window's content view. Since siblings are "Stacked" depending on the order in which they are added, when you add the view in code it is being added on top of the buttons. You should be able to fix it by explicitly specifying where the view should be positioned relative to its new siblings like so:
[[winMain contentView] addSubview:addSourceView positioned:NSWindowBelow relativeTo:nil];
which should place it below any existing subviews of your window's content view. Also, remove the setNeedsDisplay: line in drawRect, that leads to unncessary, possibly infinite, redrawing.
EDIT: OK I see what you're doing.
I would suggest creating a standalove view in the NIB by dragging a "Custom View" object into the left hand side (the vertically-aligned archived objects section) and adding your controls there, that should ensure the controls are actualy subviews of the view, then you can just create a reference to the archived view in code, and add/remove it dynamically as needed.
Honestly though, you should probably be using a sheet for these kinds of modal dialogs. Why reinvent the wheel, and make your app uglier in the process?
You added TWO AddSource views to the window. You added one in IB - this view contains your textFields and buttons that are connected to the IBOutlets and it is positioned outside the window.
Then in -awakeFromNib you create another, blank AddSource view (containing nothing) and animate it into the window.
I can't recommend highly enough the Hillegass as the best introduction to IB and the correct way to build Cocoa Apps.
Also, Assertions can be useful to make sure what you think is happening is actually what is happening.
If you are certain you added a button to your view in IB, assert it is so:-
- (void)awakeFromNib {
NSAssert( myButton, #"did i hook up the outlet?");
}
NSAssert is a macro that has zero overhead in a release build.
Calling [self setNeedsDisplay:YES] from -drawRect just causes the same -drawRect to be called again. This will give you big problems.
I have an NSWindow that I want to display as part of another window. It has to be an NSWindow; I don't want to change it to an NSView or anything... I just don't because it involves accessing foreign nib files and the such.
How should I accomplish this if possible? I was thinking along the lines of grabbing the view of the NSWindow and sticking it as the subview of another view in my main window... but I don't think this is possible. Is it?
If you're okay with the window as-is, just add it as a child window to the window in your own app. That will handle positioning and everything, although you might have to patch up the responder chain and such to really get things working properly.