objective-cxcodexcode4screensavernspanel

NSPanel has black background and text is invisible


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.


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