NSPopover not showing content on macOS Sonoma

I'm experiencing a weird issue where NSPopover doesn't show content. What's also strange is that when inspecting view in Xcode Debug View Hierarchy, it renders all the views as expected.

Here is how the Xcode Debug View Hierarchy looks like:

I just don't understand the issue; why is LoginViewController's view not shown. This issue started with macOS Sonoma.

Here is the Popover setup:

{
    _popover = NSPopover.new;
    _popover.animates = NO;
    _popover.behavior = NSPopoverBehaviorApplicationDefined;

    NSViewController* vc;
    if (user == nil)
    {
        vc = LoginViewController.new;
    }
    else
    {
        vc = MessageViewController.new;
    }
    _popover.contentViewController = [RootViewController.alloc initWithRootViewController:vc];
    [_popover.contentViewController loadView];
    _statusItem = [NSStatusBar.systemStatusBar statusItemWithLength:24];
    _statusItem.button.action = @selector(onPress);
    _statusItem.button.target = self;
    [self reloadPopoverTrayImageForce:YES];
}

And here is how Popover's RootViewController is setup. I mean nothing out-of-standard here, just embedding the child view controller and his view to parent's view controller.

{
    self.view = NSView.new;
    self.view.translatesAutoresizingMaskIntoConstraints = NO;

    [self.view addSubview:self.containerView];
    if (self.rootViewController)
    {
        [self addChildViewController:self.rootViewController];
        [self.containerView addSubview:self.rootViewController.view];
        self.rootViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
        id r = @{ @"root": self.rootViewController.view };
        [self.containerView addFor:r constraints:@"|[root]|"];
        [self.containerView addFor:r constraints:@"V:|[root]|"];
    }
    [self.view addSubview:self.footerView];
    [self reloadConstraints];

    id v = @{
            @"container": self.containerView,
            @"footer": self.footerView
    };

    [self.view addConstraint:[NSLayoutConstraint
            constraintWithItem:self.view attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual
                        toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:WIDTH]];
    [self.view addConstraint:[NSLayoutConstraint
            constraintWithItem:self.view attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual
                        toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:HEIGHT]];

    [self.view addFor:v constraints:@"|[container]|"];
    [self.view addFor:v constraints:@"|[footer]|"];
    [self.view addFor:v constraints:@"V:|[container]-0-[footer(44)]|"];
}
Answered by Cakra Komci in 766420022

I found the root cause. One of the popover view's subviews was my custom NSView which overrides drawRect method:

- (void) drawRect:(NSRect)dirtyRect
{
    NSLog(@"DIRTY RECT: %@", NSStringFromRect(dirtyRect));
    [colorSeparator setFill];
    NSRectFill(dirtyRect);
    [super drawRect:dirtyRect];
}

but it gets weird rect as parameter:

DIRTY RECT: {{-13, -56}, {426, 476}}
DIRTY RECT: {{-13, -411}, {426, 476}}

So I just removed that method and set the background color via layer:

@implementation CDSeparatorView

- (instancetype) init
{
    self = [super init];
    if (self)
    {
        [self setupInstance];
    }
    return self;
}

- (instancetype) initWithFrame:(NSRect)frameRect
{
    self = [super initWithFrame:frameRect];
    if (self)
    {
        [self setupInstance];
    }
    return self;
}

- (instancetype) initWithCoder:(NSCoder*)coder
{
    self = [super initWithCoder:coder];
    if (self)
    {
        [self setupInstance];
    }
    return self;
}

- (void) setupInstance
{
    self.translatesAutoresizingMaskIntoConstraints = NO;
    self.wantsLayer = YES;
    self.layer.backgroundColor = SREG.theme.colorSeparator.CGColor;
}

@end

and now the app is drawing the content just fine.

Hi, did you found the solution, we have the problem

Not yet, but simplifying the addChildViewController logic did show my inner container view but still work to be done to get it to work as before, I hope to fix it today/tomorrow.

- (void) loadView
{
    self.view = NSView.new;
    self.rootViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
    
    [self addChildViewController:self.rootViewController];
    [self.view addSubview:self.rootViewController.view];
    
    id v = @{ @"v": self.rootViewController.view };
    [self.view addFor:v constraints:@"|[v]|"];
    [self.view addFor:v constraints:@"V:|[v]|"];
}
Accepted Answer

I found the root cause. One of the popover view's subviews was my custom NSView which overrides drawRect method:

- (void) drawRect:(NSRect)dirtyRect
{
    NSLog(@"DIRTY RECT: %@", NSStringFromRect(dirtyRect));
    [colorSeparator setFill];
    NSRectFill(dirtyRect);
    [super drawRect:dirtyRect];
}

but it gets weird rect as parameter:

DIRTY RECT: {{-13, -56}, {426, 476}}
DIRTY RECT: {{-13, -411}, {426, 476}}

So I just removed that method and set the background color via layer:

@implementation CDSeparatorView

- (instancetype) init
{
    self = [super init];
    if (self)
    {
        [self setupInstance];
    }
    return self;
}

- (instancetype) initWithFrame:(NSRect)frameRect
{
    self = [super initWithFrame:frameRect];
    if (self)
    {
        [self setupInstance];
    }
    return self;
}

- (instancetype) initWithCoder:(NSCoder*)coder
{
    self = [super initWithCoder:coder];
    if (self)
    {
        [self setupInstance];
    }
    return self;
}

- (void) setupInstance
{
    self.translatesAutoresizingMaskIntoConstraints = NO;
    self.wantsLayer = YES;
    self.layer.backgroundColor = SREG.theme.colorSeparator.CGColor;
}

@end

and now the app is drawing the content just fine.

macOS 14 SDK sets NSView clipToBounds property to NO by default. So it's wrong to use dirtyRect like that, because it could extend outside the NSView bounds.

NSPopover not showing content on macOS Sonoma
 
 
Q