MacOS开发-状态栏APP和Popover弹出窗口

开发目的

本人再使用pap.er时发现了一些bug,偶尔还会莫名崩溃,所以想要自己开发一款类似的一款壁纸APP。

以下是我开发这款APP的记录。

创建工程

创建MacOS工程

打开MainMenu.xib文件,因为我们是状态栏APP,所以需要删除默认的Window和Main Menu

MainMenu

设置状态栏

在AppDelegate文件中编写以下代码

1
2
3
4
5
6
7
8
9
10
11
12
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSStatusItem *statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength: 20];
NSImage *icon = [NSImage imageNamed:@"statusBar"];
icon.template = YES;
statusItem.image = icon;
statusItem.button.target = self;
statusItem.button.action = @selector(toggleMenu:);
}

- (void)toggleMenu:(NSStatusBarButton *)button {
NSLog(@"点击菜单");
}

此时我们已经能在状态栏看到我们APP显示了,但是你会发现Dock栏里面也有我们的APP的Icon,这是我们需要在info.plist文件中设置Application is agent (UIElement)YES,再次运行APP即可。

Popver弹出窗口

再次打开AppDelegate文件,编写以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 添加popover属性
- (NSPopover *)popover {
if (!_popover) {
_popover = [[NSPopover alloc] init];
_popover.appearance = [[NSAppearance alloc] initWithAppearanceNamed:NSAppearanceNameVibrantDark bundle:nil];
// 创建一个继承NSViewController的PopoverController作为弹出控制器
_popover.contentViewController = [[PopoverController alloc] init];
_popover.behavior = NSPopoverBehaviorApplicationDefined;
}
return _popover;
}

// 修改toggleMenu:方法
- (void)toggleMenu:(NSStatusBarButton *)button {
if ([self.popover isShown]) {
[self.popover performClose:button];
} else {
[self.popover showRelativeToRect:button.bounds ofView:button preferredEdge:NSRectEdgeMaxY];
}
}

popover

此时你会发现点击弹窗外不会自动收起,这种效果不是我们想要的效果,查看NSpopover的API文档,其有一个behavior属性,值为NSPopoverBehaviorTransient好像可以实现自动收起功能。运行程序发现确实可以实现点击弹窗外面自动收起功能,但是前提是弹窗内有一次点击时间才能做到此效果。

此时我的内心是崩溃的😤,谷歌、百度其上,终于让我找到了解决方案:添加系统事件监视器来实现对交互事件的监测,从而做到弹窗显示后,无论什么时候点击弹窗外面都能自动收起。

新建EventMonitor,编写以下代码

1
2
3
4
5
6
7
// EventMonitor.h
typedef void(^EventMonitorHandler) (NSEvent * _Nullable event);
@interface EventMonitor : NSObject
- (instancetype)initWithMask:(NSEventMask)mask handler:(EventMonitorHandler)handler;
- (void)start;
- (void)stop;
@end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// EventMonitor.m
@interface EventMonitor ()
@property (nonatomic, assign) NSEventMask mask;
@property (nonatomic, copy) EventMonitorHandler handler;
@property (nonatomic, strong) id monitor;
@end

@implementation EventMonitor

- (instancetype)initWithMask:(NSEventMask)mask handler:(EventMonitorHandler)handler {
if (self = [super init]) {
self.mask = mask;
self.handler = handler;
}
return self;
}

- (void)start {
self.monitor = [NSEvent addGlobalMonitorForEventsMatchingMask:self.mask handler:self.handler];
}

- (void)stop {
if (self.monitor != nil) {
[NSEvent removeMonitor:self.monitor];
self.monitor = nil;
}
}
@end

打开AppDelegate,修改Popover弹出事件

1
2
3
4
5
6
7
// applicationDidFinishLaunching:方法添加
__weak typeof(self) weakSelf = self;
self.monitor = [[EventMonitor alloc] initWithMask:NSEventMaskLeftMouseDown | NSEventMaskRightMouseDown handler:^(NSEvent * _Nullable event) {
if (weakSelf.popover.shown) {
[weakSelf.popover performClose:event];
}
}];
1
2
3
4
5
6
7
8
9
10
// 修改toggleMenu:方法如下
- (void)toggleMenu:(NSStatusBarButton *)button {
if ([self.popover isShown]) {
[self.popover performClose:button];
[self.monitor stop];
} else {
[self.popover showRelativeToRect:button.bounds ofView:button preferredEdge:NSRectEdgeMaxY];
[self.monitor start];
}
}

再次运行程序,已经可以实现我们想要的功能了。

文章作者: 落秋
文章链接: https://www.liyb.vip/2019/09/14/MacOS开发-状态栏APP和Popover弹出窗口/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 落秋
打赏
  • 微信
  • 支付宝

评论