单击其contentViewController中的按钮时关闭NSPopover

时间:2014-11-30 21:39:47

标签: objective-c nspopover

单击contentViewController中的按钮时,我无法关闭NSPopover。

我有一个NSViewController(称为StatusViewController),它将NSStatusView放入菜单栏中。单击StatusView时,它会显示NSPopover,在NSPopover的contentViewController中有一个按钮。 contentViewController有一个父项的@property,当StatusViewController实例化popover时,它将自己指定为contentViewController的父项。但是,这个参考文献似乎并不完整。

我为此处的所有代码道歉,但作为菜单栏应用程序,它似乎增加了复杂性。我真的努力将其归结为它的本质。

这是我的StatusViewController.h

#import <Cocoa/Cocoa.h>


@interface StatusViewController : NSViewController <NSMenuDelegate, NSPopoverDelegate>

-(void)showPopover;
-(void)hidePopover;
-(void)togglePopover;
-(IBAction)showSettings;

@end

我的StatusViewController.m

#import "StatusViewController.h"
#import "PopoverContentViewController.h"
#import "StatusView.h"

#define ImageViewWidth 22

@interface StatusViewController ()
{
    BOOL _statusViewIsActive;
    NSImageView *_imageView;
    NSStatusItem *_statusItem;
    NSMenu *_statusItemMenu;
    NSPopover *_popover;
    PopoverContentViewController *_mfp;
    StatusView *_statusView;
    NSEvent *_popoverTransiencyMonitor;
}

@property (nonatomic) NSEvent *popoverTransiencyMonitor;


- (void)setStatusViewIsActive:(BOOL)active;


@end




@implementation StatusViewController

- (id)init
{

    self = [super init];
    if (self) {

        _statusViewIsActive = NO;
        _statusView = [[StatusView alloc] init];
        _statusView.delegate = self;

        _statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
        _statusItem.view = _statusView;

        _statusItemMenu = [[NSMenu alloc] init];

        _popover = [[NSPopover alloc] init];
        _popover.animates = NO;
        _popover.delegate = self;
        _popover.behavior = NSPopoverBehaviorSemitransient;



        _statusView.imageView.image = [NSImage imageNamed:@"icon"];
    }
    return self;
}



-(void)togglePopover {

    NSLog(@"%s",__func__);
    _mfp = [[PopoverContentViewController alloc] initWithNibName:@"PopoverContentViewController" bundle:nil];
    if (_statusViewIsActive) {
        [self hidePopover];
    } else {
        _popover.contentViewController = _mfp;
        [_mfp.settingsButton setAction:@selector(showSettings)];
        [_popover showRelativeToRect:_statusView.frame
                              ofView:_statusView
                       preferredEdge:NSMinYEdge];
        if (_popoverTransiencyMonitor == nil) {
            _popoverTransiencyMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:(NSLeftMouseDownMask | NSRightMouseDownMask | NSKeyUpMask) handler:^(NSEvent* event) {
                [NSEvent removeMonitor:_popoverTransiencyMonitor];
                _popoverTransiencyMonitor = nil;
                [_popover close];
            }];
        }
    }
}

-(IBAction)showSettings {
    NSLog(@"%s",__func__);
}



- (void)setStatusViewIsActive:(BOOL)active
{
    _statusViewIsActive = active;

}



- (void)showPopover
{

    NSLog(@"%s",__func__);

    if (!_popover.isShown) {
        _popover.contentViewController = _mfp;
        [_mfp.settingsButton setAction:@selector(showSettings)];
        [_popover showRelativeToRect:_statusView.frame
                              ofView:_statusView
                       preferredEdge:NSMinYEdge];


        if (_popoverTransiencyMonitor == nil) {
            _popoverTransiencyMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:(NSLeftMouseDownMask | NSRightMouseDownMask | NSKeyUpMask) handler:^(NSEvent* event) {
                [NSEvent removeMonitor:_popoverTransiencyMonitor];
                _popoverTransiencyMonitor = nil;
                [_popover close];
            }];
        }
    }
    NSLog(@"_mfp.delegate in showPopover: %@",[_mfp.parent class]);
}



-(void)popoverDidShow:(NSNotification *)notification {
    NSLog(@"%s",__func__);
    [self setStatusViewIsActive:!_statusViewIsActive];
}



- (void)popoverDidClose:(NSNotification *)notification {
    [self setStatusViewIsActive:!_statusViewIsActive];
    _popover.contentViewController = nil;
}


- (void)hidePopover
{
    NSLog(@"%s",__func__);
    if (_popover != nil && _popover.isShown) {
        [_popover close];
    }
}

@end

和PopoverContentViewController.h

#import <Cocoa/Cocoa.h>
#import "StatusViewController.h"

@class StatusViewController;

@interface PopoverContentViewController : NSViewController {
    IBOutlet NSButton *_settingsButton;
}

@property (nonatomic, strong) StatusViewController *parent;
@property (nonatomic) IBOutlet NSButton *settingsButton;



@end

和PopoverContentViewController.m

#import "PopoverContentViewController.h"



@implementation PopoverContentViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {

    }

    return self;
}


- (IBAction)showPreferencesWindow:(id)sender {
    NSLog(@"%s",__func__);
    NSLog(@"[self.parent class]: %@", [self.parent class]);
    [self.parent togglePopover];    
}

@end

基本上,PopoverContentViewController的[self.parent类]变为null。我怀疑它是某种内存管理或弱引用的东西。我把代码放在Bitbucket上,如果你想运行它,看看我在说什么。 https://bitbucket.org/nspaul/menubar-popover-stackoverflow/src/67d0ea348713ee87c4df40bbdde072996fb63e53?at=master

那么当点击settingsButton时,如何让它实际调用[self.parent togglePopover]呢?这就像self.parent只是一个空引用。

1 个答案:

答案 0 :(得分:0)

这里有几个问题。

从你在这里附带的代码中,你实际上从未设置过PopoverContentViewController的parent,所以在-showPreferencesWindow:中它仍然是nil。在StatusViewController中创建_mfp时,您应该看到mfp的父级是自己的。

即使这样,得到-showPreferenceWindow的实例的parent:按钮调用它仍然是零。这不是因为代码中的任何内容,而是nib nib中的问题。

按钮的目标被连接成一个额外的PopoverContentViewController对象,而不是文件的所有者(它对应于由StatusViewController创建的PopoverContentViewController对象)。删除该额外的控制器对象并将目标设置为文件的所有者(并使文件的所有者成为PopoverContentViewController类型)可以解决该问题。