继上一篇已经介绍了如何创建一个无Window、不在Dock上显示、并且只在status bar显示的APP后,现在就开始介绍一下如自定义菜单栏,让菜单栏可以更加实用和更加个性化。
PS:因为是续上一篇文章的,如果需要回顾或者有什么不明白的,可以看看我上一篇文章
文章跳转 ->【MacOS开发系列5-创建StatusBarApp(无窗体、不在Dock显示,只在状态显示)】
自定义菜单视图
demo代码已经上传到GitHub传送门
为了方便阅读,我把菜单栏简化了,只剩下About
和Quit
.同时又添加了一个新的MenuItem
:
同样的,我们在为这个新的MenuItem
添加IBOutlet
到AppDelegate中。
@IBOutlet weak var firstMenuItem: NSMenuItem!
竟然是自定义的菜单视图,所以我们要创建一个NSView。里面的内容你们喜欢。我这里就随便搞搞了。
以下就是我的自定义的View的代码,就很简单就是一个居中显示的label,内容为当前时间:
然后就可以把自定义view的设置到需要自定义菜单栏里面。
回到AppDelegate
中的awakeFromNib
这个函数。
override func awakeFromNib() {
super.awakeFromNib()
print("awakeFromNib")
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
statusItem?.button?.title = "StatusBarAppExample"
let itemImage = NSImage(named: "SplitScreen")
itemImage?.isTemplate = true
statusItem?.button?.image = itemImage
if let menu = menu {
statusItem?.menu = menu
}
if let item = firstMenuItem {
let cView = CustomItemView.init(frame: NSRect.init(x: 0, y: 0, width: menu.size.width, height: 200))
item.view = cView
}
}
完整AppDelegate
如下:
这样就完成了自定义菜单栏了,效果如下:
但是如果想弄成和某宝旗下的旺旺一样的效果呢?
某宝的旺旺App的界面:
那就要使用NSPopover
了。那现在就开始介绍如何使用NSPopover
进行自定义菜单栏NSMenuItem
NSPopover自定义菜单视图
demo代码已经上传到GitHub传送门
因为现在只需要点击一下StatusBar
上的icon
就出现我们NSPopover
,所以菜单栏可以全部删除。效果如下:
然后在Storyboard
上新建一个ViewController
,用于在NSPopover
上显示的。如图:
然后新建一个PopoverViewController.swift
文件,对应Storyboard
上新建的ViewController
。如图:
因为在显示Storyboard
的ViewController
的时候,需要一个ViewController
的ID
。所以需要在Storyboard
上的ViewController
上对应起来。如图:
然后就回到AppDelegate.swift
上写代码了.
- 在
AppDelegate
声明NSStatusItem
、NSPopover
、PopoverViewController
对象:如下:
var statusItem: NSStatusItem?
var popover: NSPopover?
var popoverVC: PopoverViewController?
- 在
AppDelegate
中添加showPopoverVC
这个函数,用于在NSPopover
显示PopoverViewController
@objc func showPopoverVC(){
let storyboard = NSStoryboard(name: "Main", bundle: nil)
guard let vc = storyboard.instantiateController(withIdentifier: .init(stringLiteral: "PopoverViewControllerID")) as? PopoverViewController
else { return }
popoverVC = vc
if let pop = popover, let button = statusItem?.button{
pop.contentViewController = popoverVC
popover?.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
}
}
- 重写
AppDelegate
中的awakeFromNib
这个函数。
override func awakeFromNib() {
super.awakeFromNib()
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
statusItem?.button?.title = "StatusBarAppWithPopoverExample"
let itemImage = NSImage(named: "SplitScreen")
itemImage?.isTemplate = true
statusItem?.button?.image = itemImage
statusItem?.button?.target = self
statusItem?.button?.action = #selector(showPopoverVC)
popover = NSPopover()
popover?.behavior = .transient
}
完整AppDelegate
如下:
这样就完成了使用NSPopover
进行自定义菜单栏了。效果如图:
最后,在PopoverViewController.swift
里面为打开主窗口
的按钮设置IBAction
,然后写上如下代码:
@IBAction func showMainVc(_ sender: NSButton) {
let storyboard = NSStoryboard(name: "Main", bundle: nil)
guard let vc = storyboard.instantiateController(withIdentifier: .init(stringLiteral: "MainViewControllerID")) as? MainViewController
else { return }
guard let appDelegate = NSApplication.shared.delegate as? AppDelegate, let pop = appDelegate.popover else { return }
let window = NSWindow(contentViewController: vc)
window.makeKeyAndOrderFront(sender)
window.level = .floating
window.title = "Main View Controller"
var centerX : CGFloat = 0.0
var centerY : CGFloat = 0.0
let width : CGFloat = 534.0
let height : CGFloat = 319.0
if let screen = window.screen {
centerX = screen.frame.midX - width / 2.0
centerY = screen.frame.midY - height / 2.0
}
window.setFrame(CGRect.init(x: centerX, y: centerY, width: width, height: height), display:true)
pop.close()
}
其中MainViewController
的创建和PopoverViewController
一致。具体参考上面的即可.
对于在NSPopover
显示的ViewController
的点击事件的时候,如果需要操作后,关闭NSPopove
r,一定要调用close()
方法。不然会一直显示的:
pop.close()