Sheets are not displaying properly on OSX - macos

I am very much new to OSX development. Consider this as my first app. I want to display a sheet when a button is clicked on the main window. I am using Nib
Following is my code for .h file
#import <Foundation/Foundation.h>
#import "WebKit/Webkit.h"
#interface MainViewObject : NSObject
- (IBAction)accountButtonPressed:(id)sender;
- (IBAction)cancelSheetButtonPressed:(id)sender;
.m file as follows
#import "MainViewObject.h"
#implementation MainViewObject
- (IBAction)accountButtonPressed:(id)sender {
[NSApp beginSheet:self.accountSheet
modalForWindow:[mainWindowView window]
modalDelegate:nil
didEndSelector:nil
contextInfo:nil];
[NSApp runModalForWindow:self.accountSheet];
[NSApp endSheet:self.accountSheet];
[self.accountSheet orderOut:self];
}
- (IBAction)cancelSheetButtonPressed:(id)sender {
// Return to normal event handling
[NSApp endSheet:self.accountSheet];
// Hide the sheet
[self.accountSheet orderOut:sender];
}
When I run the app I get something like this:
http://i.imgur.com/DzJJ6.png
I am stuck and I have no idea what wrong in this. I am not able to get the sheet and the not able to even close the app. I have referred to some examples on internet.

- (IBAction)accountButtonPressed:(id)sender {
[NSApp beginSheet:self.accountSheet
modalForWindow:[mainWindowView window]
modalDelegate:nil
didEndSelector:nil
contextInfo:nil];
[NSApp runModalForWindow:self.accountSheet];
[NSApp endSheet:self.accountSheet];
[self.accountSheet orderOut:self];
}
Wow, looking at that, it's no surprise the screenshot looks as it is.
Let's walk through that one line at a time. When you click the Accounts button, you're doing 4 things immediately in succession:
You're telling the application to begin showing the sheet attached to your main window. This is OK, and is actually the only code you want in that accountButtonPressed: method.
Right after beginning that sheet, you tell the application you want to also show that sheet all by itself (not attached to any windows but right in the middle of the screen), in an application-modal fashion, which blocks all other events from being processed in the application. In other words, this line doesn't really make sense. You either show a window as a sheet in a "document-modal" fashion (which only ties up the window that the sheet is attached to) or in an "application-modal" fashion, but not both at the same time. ;-)
Immediately after having just shown the sheet, you tell NSApp to stop showing the sheet. Now, you do want to do this eventually, but dismissing the sheet 0.0005 seconds after having just shown it will likely leave your users a little frustrated.
You now tell the sheet to hide itself. This needs to be done from your didEndSelector: method, which brings us to the problems in your first method.
-
[NSApp beginSheet:self.accountSheet
modalForWindow:[mainWindowView window]
modalDelegate:nil
didEndSelector:nil
contextInfo:nil];
This is good but read the documentation for beginSheet:modalForWindow:modalDelegate:didEndSelector:contextInfo: and also the Sheet Programming Topics: Using Custom Sheets. (The Companion guides links at the top of Class Reference pages are especially helpful for learning how to use the APIs in the real world. They were extremely helpful when I was learning).
Specifying nil for the modalDelegate: means you don't have anything that's waiting to be notified about when the sheet has stopped being shown (this happens when you call [NSApp endSheet:sheet]). You also haven't specified the #selector you want called when the sheet is ended. A selector is kind of like a function, aka a "method".
Your code should look something like this:
#implementation MDAppDelegate
- (IBAction)showSheet:(id)sender {
[NSApp beginSheet:self.sheet
modalForWindow:self.window
modalDelegate:self
didEndSelector:#selector(sheetDidEnd:returnCode:contextInfo:)
contextInfo:NULL];
}
- (IBAction)cancel:(id)sender {
[NSApp endSheet:self.sheet];
}
- (IBAction)ok:(id)sender {
[NSApp endSheet:self.sheet];
}
- (void)sheetDidEnd:(NSWindow *)sheet
returnCode:(NSInteger)returnCode
contextInfo:(void *)contextInfo {
[sheet orderOut:nil];
}
#end
In this example, you click the Show Sheet button, and the sheet starts being shown attached to the main window. In the sheet, there is a Cancel and an OK button, which both call their respective methods. In each of these methods, you call [NSApp endSheet:self.sheet]. This tells NSApp that it should then call the sheetDidEnd:returnCode:contextInfo: method on the object specified to be the modal delegate. In sheetDidEnd:returnCode:contextInfo: you then tell the sheet to hide itself.
EDIT:
Every NSWindow has a "Visible at launch" flag that can be set in Interface Builder. If this flag is set, the window will be visible at the time the nib file is loaded. If it isn't set, the window is hidden until you programmatically show it. Just edit the flag in the nib file like shown:

Related

NSWindow won't draw immediately after app launch

The problem: Attempting to display a window with text from applicationWillFinishLaunching will NOT draw itself if other processor-intensive non-UI code is immediately called.
Background: I have a helper app that when launched may or may not interact with the end user. While it is "deciding" if it needs to put up a window to ask user questions, there may be anywhere from 1 second to 10 seconds that elapse (after launch it's off in non-UI capable library code communicating over the internet).
So I wanted to be kind to the user and put up a "mini-alert"* window with "working, please wait...", prior to heading into that library code, which I will dismiss once that processing has elapsed.
It seems as if the app itself doesn't have time after launch to even draw this mini-alert (it's just an NSWindow, with an NSView, some text, and no buttons).
If after the library code returns and want to put up either an error alert or a query window for the user -- then at that point the mini-alert draws as expected. However, if I close the mini-alert (see below) and then put up an NSAlert -- the mini-alert doesn't have enough time to dismiss itself.
- (void)applicationWillFinishLaunching:(NSNotification *)notification
{
[NSApp activateIgnoringOtherApps:YES];
briefAlertWindowController = [[NSWindowController alloc] initWithWindowNibName:#"BriefAlertWindow"];
[[briefAlertWindowController window] center];
[briefAlertWindowController showWindow:self ];
[[briefAlertWindowController window] orderFront:self ];
[[briefAlertWindowController window] display];
[[briefAlertWindowController window] makeKeyAndOrderFront:nil];
}
and dismissing the mini-alert:
- (void)dismissMiniAlert
{
NSWindow * theWindow = [briefAlertWindowController window];
[theWindow orderOut:nil];
}
NOTE that neither NSWindow not NSWindowController have been derived/subclassed for this mini-alert.
I'm using the term "mini-alert", because I've noticed people get annoyed about the concept of a "splash screen". While the functionality IS similar -- I'm really just trying to let the user know that an unavoidably long operation is taking place.
It sounds like a threading problem. The splash window can't draw itself on the main thread because the main thread is busy doing the processor-intensive operation. Properly, your processor-intensive stuff should all be happening on a background thread. If you can't do that, you need at least to get off the main thread long enough to give the runloop a chance to draw your window. Just introduce a delay.

How to display sheet view in NSWindow

How do I implement the view in following image.
The view which appears when + button is clicked in System Preferences > Network
I have following questions:
Does this view system has a specific name (like popover), because I have seen it in many places in Mac.
How to implement it in IB ?
Can this be done in a popover window instead of NSWindow ?(or is it only possible in NSWindow like toolbar)
Update:
Updating the title for better visibility
In Cocoa these are called sheets. Take a look at the sheet programming guide, however, this is terribly out of date!
You need to call -beginSheet:completionHandler: on the window you want to display the sheet. If you have single-window application you can ask the AppDelegate for the window and launch the sheet like so,
// This code should be in AppDelegate which implement the -window method
NSWindow *targetWindow = [self window]; // the window to which you want to attach the sheet
NSWindow *sheetWindow = self.sheetWindowController.window // the window you want to display at a sheet
// Now start-up the sheet
[targetWindow beginSheet:sheetWindow completionHandler:^(NSModalResponse returnCode) {
switch (returnCode) {
case NSModalResponseCancel:
NSLog(#"%#", #"NSModalResponseCancel");
break;
case NSModalResponseOK:
NSLog(#"%#", #"NSModalResponseOK");
break;
default:
break;
}
}];
You will notice that when the sheet completes it will return a certain modal response --- we will return to this point in a shortly.
Next you need to implement the content that you want to display in the sheet; this must be done in an NSWindow. I find it much easier to use a NSWindowController and implement the window in a separate XIB file. For example, see below,
Now you need to implement the code in your custom NSWindowController (or plain NSWindow if you are old-school and love to manage your own NIB loading) which will issue the correct modal response. Here I have hooked up the cancel and OK buttons to the following actions methods,
- (IBAction)cancelButtonAction:(id)sender {
[[[self window] sheetParent] endSheet:self.window returnCode:NSModalResponseCancel];
}
- (IBAction)OKButtonAction:(id)sender {
[[[self window] sheetParent] endSheet:self.window returnCode:NSModalResponseOK];
}
The model response will get sent to your completion handler block.
Sample project on github.

Why is the NSStatusItem displaying multiple times?

A NSStatusItem has a NSMenu attached, and one of the buttons of the NSMenu opens a NSWindow. Whenever one of these buttons is clicked, the window opens as expected and works properly, but another display of the NSStatusItem is opened.
The NSStatusItem is a clock, so I can see that it is updating correctly. However, the cloned NSStatusItem doesn't have its own menu. If I push the button that makes the window more times, more cloned versions of the NSStatusItem pop up.
Everything works fine except for this.
That's not a whole lot of information to go off of, but there's nothing else I can think of that could potentially help you. I would be happy to provide more information or try something.
EDIT: Every time the button is clicked, awakeFromNib is somehow called, which is why another half-working NSStatusItem happens.
EDIT: Temporary workaround is to put the awakeFromNib method in a dispatch_once.
EDIT: Added method that is triggered when button is clicked, as suggested by #zpasternack
- (IBAction)preferences:(id)sender {
self.windowController = [[NSWindowController alloc] initWithWindowNibName:#"PreferencesWindow"];
[[self windowController] showWindow:self];
}
Is the NSStatusItem contained in the PreferencesWindow nib? That might explain it, since you're loading the nib each time the button is clicked.
Also, is there a reason you need to recreate that window each time the button is clicked? Maybe you could only do it the first time?
- (IBAction)preferences:(id)sender {
if( self.windowController == nil ) {
self.windowController = [[NSWindowController alloc] initWithWindowNibName:#"PreferencesWindow"];
}
[[self windowController] showWindow:self];
}

How to make it from top ? (Cocoa sheet)

I have implemented my sheet (NSPanel) with the following method:
- (void)showInWindow:(NSWindow *)mainWindow {
if (!finestra1)
[NSBundle loadNibNamed:#"XibName" owner:self];
[NSApp beginSheet:finestra1 modalForWindow:mainWindow modalDelegate:nil didEndSelector:nil contextInfo:nil];
[NSApp runModalForWindow:finestra1]; //This call blocks the execution until [NSApp stopModal] is called
}
The sheet is appearing in the middle of my screen, how to attach it to the top of my main window and make it appear with a "slide down" effect instead?
Thanks in advance.
Have you tried simply removing the -runModalForWindow: line?
The way I was taught, the sheet you're presenting should be an NSWindow, not an NSPanel.
Let me know if you'd like me to upload a simple demo project.

Can't change Text Label on Sheet

I've created a sheet which I would like to display various messages while the program is doing data crunching. The sheet opens and closes correctly and I have a Text Label on the sheet which is connected to my main controller (the owner of the sheet) with an IB Outlet.
The Nib loads correctly, open and closes correctly, but the static text label is never updated. The connected IBOutlet was defined this way:
IBOutlet id mySheetText;
The call I am using to try and modify the text on the sheet is this:
[mySheetText setStringValue:#"Some text message..."];
This format works fine if the Label is in the main window, but does nothing if the Label is on the sheet.
All the connections in IB appear to be correct. I'm sure that I'm missing something very simple, and i'm guessing that it has something to do with the fact that the sheet is a different "window" than the main window, but I can't seem to find anything in the docs to point me in the right direction.
Incidentally, here is the way I connected the sheet...
NSWindow *mySheet;
#property (assign) IBOutlet NSWindow *mySheet;
#synthesize mySheet;
...and opened it:
if (!serverSyncSheet) {
[NSBundle loadNibNamed:#"mySheetNibFile" owner:self];
}
[NSApp beginSheet: self.mySheet
modalForWindow: [[NSApp delegate] mainWindow]
modalDelegate: self
didEndSelector: NULL
contextInfo: NULL];
Any ideas?
* EDIT *
Ok, so it turns out that I am partially correct. If I attempt to read the text value, it turns out that it IS setting it to the correct text, but the sheet is not being updated to DISPLAY the change. I suspect that I need to tell the window to redraw... never done that before. Back to the docs to see if I can find it. If anyone knows the method to call, let me know. :) Thanks!
Well, my suspicions were correct... it was something simple.
I just had to redraw the window:
[mySheet display];
Doh! :)

Resources