开发目的
本人再使用pap.er时发现了一些bug,偶尔还会莫名崩溃,所以想要自己开发一款类似的一款壁纸APP。
以下是我开发这款APP的记录。
创建工程
打开MainMenu.xib文件,因为我们是状态栏APP,所以需要删除默认的Window和Main Menu
设置状态栏
在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]; } }
|
此时你会发现点击弹窗外不会自动收起,这种效果不是我们想要的效果,查看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]; } }
|
再次运行程序,已经可以实现我们想要的功能了。