Question

I'm trying to make a configure sheet appear for a ScreenSaverView subclass. After a long battle with Xcode, I'm finally getting the configure sheet to appear when "Screen Saver Options" is clicked in System Preferences (and my screen saver is selected), and the sheet behaves normally with one exception: the background is black, rendering text invisible (see image).Black background on configure sheet.

This occurs regardless of whether the sheet is an NSPanel or NSWindow class, and whether the panel is a Regular Panel, Utility Panel, or HUD Panel. The only thing I can seem to do is change is the alpha value of the panel, which as expected makes everything more transparent (but the text is still not visible). Interestingly, calling setOpaque or setBackgroundColor on the NSPanel or NSWindow don't seem to have any effect.

Was it helpful?

Solution

To figure out why it's showing up as black, we'd really need to see code for how you're creating the window.

I just did a quick test project and it seems to work OK here. IMO, the easiest solution for creating the window to return in the configureSheet method is to use an NSWindowController subclass to load a nib file in which you've configured the window ahead of time.

So in your ScreenSaverView subclass, you'd define an interface something like the following:

@interface MDScreenSaverFinaglerView : ScreenSaverView {
    MDScreenSaverOptionsWindowController        *optionsWindowController;

    NSInteger                                    screenSaverViewMode;
}

@property (nonatomic, retain) MDScreenSaverOptionsWindowController
                                             *optionsWindowController;

@property (nonatomic, assign) NSInteger screenSaverViewMode;

@end

Your implementation would then look like this for the configureSheet method:

- (NSWindow *)configureSheet {
    if (optionsWindowController == nil) {
        optionsWindowController = [[MDScreenSaverOptionsWindowController alloc]
                                                  initWithScreenSaverView:self];
    }
    return optionsWindowController.window;
}

Basically, you check to see if the optionsWindowController instance exists, creating it if necessary, then return its window.

The interface for the custom NSWindowController subclass would look like the following:

@interface MDScreenSaverOptionsWindowController : NSWindowController {
    IBOutlet NSMatrix                *optionsMatrix;

    MDScreenSaverFinaglerView        *screenSaverView;    // non-retained/weak reference
}

- (id)initWithScreenSaverView:(MDScreenSaverFinaglerView *)aView;

@property (nonatomic, assign) MDScreenSaverFinaglerView *screenSaverView;

- (IBAction)ok:(id)sender;

@end

There's a screenSaverView property which will allow communication back with the ScreenSaverView subclass once the user has clicked the OK button.

The nib file for the MDScreenSaverOptionsWindowController class (named "MDScreenSaverOptionsWindowController.xib") is set up like shown below:

enter image description here

The implementation of the MDScreenSaverOptionsWindowController looks like the following:

@implementation MDScreenSaverOptionsWindowController

@synthesize screenSaverView;

- (id)initWithScreenSaverView:(MDScreenSaverFinaglerView *)aView {
    NSParameterAssert(aView != nil);
    if ((self = [super initWithWindowNibName:NSStringFromClass([self class])])) {
        self.screenSaverView = aView;
    }
    return self;
}

- (void)windowDidLoad {
    [super windowDidLoad];
    [optionsMatrix selectCellWithTag:screenSaverView.screenSaverViewMode];
}

- (IBAction)ok:(id)sender {
    NSInteger viewMode = [optionsMatrix selectedTag];
    [[NSUserDefaults standardUserDefaults] setObject:[NSNumber
                  numberWithInteger:viewMode] forKey:MDScreenSaverViewModeKey];
    screenSaverView.screenSaverViewMode = viewMode;
    [NSApp endSheet:self.window];
}
@end

The end result:

enter image description here

Sample project: ScreenSaverFinagler.zip

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top