单击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只是一个空引用。
答案 0 :(得分:0)
这里有几个问题。
从你在这里附带的代码中,你实际上从未设置过PopoverContentViewController的parent
,所以在-showPreferencesWindow:
中它仍然是nil。在StatusViewController中创建_mfp
时,您应该看到mfp的父级是自己的。
即使这样,得到-showPreferenceWindow的实例的parent
:按钮调用它仍然是零。这不是因为代码中的任何内容,而是nib nib中的问题。
按钮的目标被连接成一个额外的PopoverContentViewController对象,而不是文件的所有者(它对应于由StatusViewController创建的PopoverContentViewController对象)。删除该额外的控制器对象并将目标设置为文件的所有者(并使文件的所有者成为PopoverContentViewController类型)可以解决该问题。