Mac OS上,坐標(biāo)系統(tǒng)原點位于左下角!


Mac端除了NSButton按鈕作為選項外,還可以使用NSPopUpButton(多選一)和NSMenu(菜單)
NSPopUpButton
為NSPopUpButton設(shè)置背景色——為紅色
let speedPopBtn = NSPopUpButton(frame: NSMakeRect(100.0, 100.0, 100.0, 30.0)) self.view .addSubview(speedPopBtn) //設(shè)置背景色(一般不會去設(shè)置) speedPopBtn.wantsLayer = true speedPopBtn.layer?.backgroundColor = NSColor.red.cgColor展示效果:NSPopUpButton背景色在一般情況不會去設(shè)置!
初始化NSPopUpButton實例,添加在視圖控制器的view上!并設(shè)置其數(shù)組(所擁有的項)!
let speedPopBtn = NSPopUpButton(frame: NSMakeRect(100.0, 100.0, 100.0, 30.0))
self.view .addSubview(speedPopBtn)
//風(fēng)格設(shè)置是否為下拉菜單(默認(rèn)為false!設(shè)置為ture后需要需要在對應(yīng)selector里設(shè)置選中項對應(yīng)標(biāo)題)
speedPopBtn.pullsDown = false//設(shè)置為false,選擇后標(biāo)題才改變!
//設(shè)置數(shù)組(所擁有的項)
let typeArr = ["常速", "2倍速", "4倍速", "8倍速", "16倍速"]
speedPopBtn .addItems(withTitles: typeArr)
效果:有"常速", "2倍速", "4倍速", "8倍速", "16倍速"這些選項,可以進(jìn)行選擇!

移除某一項:open func removeItem(at index: Int)
//移除某一項
speedPopBtn .removeItem(at: (typeArr.count - 1))//移除最后一項
效果:少了最后一項"16倍速"

在末尾添加新的項:open func addItem(withTitle title: String)
//在末尾添加新的項
speedPopBtn .addItem(withTitle: "16倍速")
speedPopBtn .addItem(withTitle: "32倍速")
效果:又多了"16倍速"、"32倍速"這2項!

指定位置插入某一項:open func insertItem(withTitle title: String, at index: Int)
//指定位置插入某一項
speedPopBtn .insertItem(withTitle: "1/2倍速", at: 0)
效果:當(dāng)前第一項變?yōu)?1/2倍速"!

打印NSPopUpButton實例的數(shù)組-當(dāng)前所有項:
//打印PopBtn的數(shù)組-當(dāng)前所有項
print(speedPopBtn.itemArray)
打印如下:
[<NSMenuItem: 0x600002903720 1/2倍速>, <NSMenuItem: 0x600002911180 常速>, <NSMenuItem: 0x6000029034f0 2倍速>, <NSMenuItem: 0x600002903480 4倍速>, <NSMenuItem: 0x6000029035d0 8倍速>, <NSMenuItem: 0x600002903640 16倍速>, <NSMenuItem: 0x6000029036b0 32倍速>]
設(shè)置NSPopUpButton的選中項:open func selectItem(at index: Int)
//設(shè)置 默認(rèn)選中項——首項:"1/2倍速"
speedPopBtn .selectItem(at: 0)
設(shè)置默認(rèn)選中項的前后效果對比:

設(shè)置彈出菜單的位置:open var preferredEdge: NSRectEdge屬性——對應(yīng)枚舉的各項(minX/minY/maxX/maxY)
public enum NSRectEdge : UInt {
case minX = 0
case minY = 1
case maxX = 2
case maxY = 3
}
對應(yīng)情況的效果如下圖:

添加選擇時的響應(yīng)事件:設(shè)置target和action!
//添加選擇時的響應(yīng)事件
speedPopBtn.target = self; speedPopBtn.action = #selector(handleSelectPopBtn)
選擇時的響應(yīng)事件:
@objc func handleSelectPopBtn(popBtn : NSPopUpButton) {//NSPopUpButton選擇
print("popBtn.indexOfSelectedItem:",popBtn.indexOfSelectedItem)
}
效果:

可以為NSPopUpButton設(shè)置新的自定義彈出菜單:NSMenu實例
[A].含有menu屬性(open var menu: NSMenu?)的控件
//設(shè)置新的自定義彈出菜單
let menu = NSMenu(title: "Menu")
menu .insertItem(withTitle: "one", action: nil, keyEquivalent: "", at: 0) //指定位置插入
menu .addItem(withTitle: "two", action: nil, keyEquivalent: "") //末尾添加
menu .addItem(withTitle: "three", action: #selector(clickThree), keyEquivalent: "")//末尾添加
speedPopBtn.menu = menu;
選擇自定義彈出菜單中某項時的響應(yīng)事件:
//注意:響應(yīng)了設(shè)置自定義彈出菜單NSMenu的項NSMenuItem對應(yīng)方法,就不會響應(yīng)NSPopUpButton選擇的對應(yīng)方法
@objc func clickThree(item: NSMenuItem) {
print("clickThree!", "item.title is",item.title)
}
UI效果:

注意:響應(yīng)了設(shè)置自定義彈出菜單NSMenu的NSMenuItem項對應(yīng)方法,就不會響應(yīng)NSPopUpButton選擇的對應(yīng)方法!
操作效果如下GIF:當(dāng)選擇了"three"項時,該項只響應(yīng)其自己的#selector(clickThree)方法!所以只打印了“clickThree! item.title is three”!

NSMenu
NSMenu除了用在上面為NSPopUpButton控件設(shè)置新的自定義彈出菜單——[A].含有menu屬性(open var menu: NSMenu?)的控件;
還可以:[B].添加NSApp對應(yīng)頂部的菜單欄菜單(mainMenu)、[C].添加頂部的狀態(tài)欄菜單(NSStatusBar)、[D].添加Dock菜單、[E].用來作為NSEvent事件的菜單!
[B].添加NSApp對應(yīng)頂部的菜單欄菜單(mainMenu)
//獲取NSApp的主目錄
let mainMenu = NSApp.mainMenu
let itemRoot = NSMenuItem(title: "根菜單項", action: #selector(clickMenu), keyEquivalent: "")
itemRoot.title = "Load_TEXT"http://設(shè)置根目錄項的title文字無效
let rootMenu = NSMenu(title: "RootMenu-根目錄")//文字會以根目錄的title來顯示
//必須先設(shè)置根目錄(第一層)——rootMenu、根目錄項——itemRoot
mainMenu? .setSubmenu(rootMenu, for: itemRoot)//添加一級目錄(根目錄),可往下一直添加N級目錄
//在末尾添加"根菜單項"
//mainMenu? .addItem(itemRoot)
//在指定位置添加"根菜單項"
mainMenu? .insertItem(itemRoot, at: (0 + 2))
步驟:1.先通過NSApp.mainMenu獲取到NSApp的主目錄,2.必須再通過.setSubmenu設(shè)置上根目錄(第一層):NSMenu實例、根目錄項:NSMenuItem實例,3.再通過.addItem或.insertItem插入到NSApp的主目錄——此時在屏幕頂部App的菜單欄菜單中就有了添加的新項("RootMenu-根目錄")!
效果:點擊選擇時不會彈出根目錄的菜單!(因為并未對根目錄 添加下一級的菜單?。?/p>

//添加一級目錄項
let item1 = NSMenuItem(title: "菜單1", action: #selector(clickMenu), keyEquivalent: "")
item1.target = self;
let item2 = NSMenuItem(title: "菜單2", action: #selector(clickMenu), keyEquivalent: "")
item2.target = self;
let item3 = NSMenuItem(title: "菜單3", action: #selector(clickMenu), keyEquivalent: "")
item3.target = self;
rootMenu .addItem(item1)
rootMenu .addItem(item2)
rootMenu .addItem(item3)
let subMenu = NSMenu(title: "Menu3-subMenu")
rootMenu .setSubmenu(subMenu, for: item3)//添加二級目錄——subMenu
let item3_1 = NSMenuItem(title: "菜單3-1", action: #selector(clickMenu), keyEquivalent: "")
item3_1.target = self
let item3_2 = NSMenuItem(title: "菜單3-2", action: #selector(clickMenu), keyEquivalent: "")
item3_2.target = self
//添加二級目錄項
subMenu .addItem(item3_1)
subMenu .addItem(item3_2)
選擇時響應(yīng)的方法:
@objc func clickMenu(menuItem: NSMenuItem) {
print("App clickMenu", menuItem.title)
}
效果:

選擇各項時的打?。?/p>
App clickMenu 菜單1
App clickMenu 菜單2
App clickMenu 菜單3
App clickMenu 菜單3-1
App clickMenu 菜單3-2
[C].添加頂部的狀態(tài)欄菜單(NSStatusBar)
//為狀態(tài)欄定義并添加2個項(App是開啟狀態(tài)就會在狀態(tài)欄上展示出來)
let stsItem = NSStatusBar .system .statusItem(withLength: 120)//自定義長度
let img = NSImage(named: "usr_item_email")
if #available(macOS 10.14, *) {
stsItem.button?.image = img
//stsItem.button?.alternateImage = img//選中時的圖片
stsItem.button?.imageScaling = NSImageScaling.scaleProportionallyUpOrDown
} else {
stsItem.image = img
//stsItem.alternateImage = img//選中時的圖片
}
stsItem.isVisible = true//是否在狀態(tài)欄中可見,默認(rèn)為true
let stsItem2 = NSStatusBar.system .statusItem(withLength: NSStatusItem.squareLength)//標(biāo)準(zhǔn)的長度
let img2 = NSImage(named: "usr_Set_Icon")
if #available(macOS 10.14, *) {
stsItem2.button?.image = img2
//stsItem.button?.alternateImage = img2//選中時的圖片
stsItem2.button?.imageScaling = NSImageScaling.scaleProportionallyUpOrDown
} else {
stsItem2.image = img2
//stsItem2.alternateImage = img2//選中時的圖片
}
stsItem2.isVisible = true
//在最后:必須用以下任一方法,加入任何一項NSStatusItem到狀態(tài)欄(必須調(diào)用,才會展示新添加的2個項?。?//NSStatusBar .insertValue(stsItem2, inPropertyWithKey: "statusMenu")
NSStatusBar .system .insertValue(stsItem2, inPropertyWithKey: "statusMenu")
注意:在最后必須用NSStatusBar的.insertValue或.system .insertValue任一方法,加入上面任何一項NSStatusItem到狀態(tài)欄——才會展示新添加的2個項!
效果:為狀態(tài)欄添加了2個項(App在開啟狀態(tài)時,就會在狀態(tài)欄上展示出來)!其中stsItem長度為120、stsItem2為標(biāo)準(zhǔn)長度!

為stsItem2添加NSMenu菜單:
let statusMenu = NSMenu(title: "statusMenu")//添加NSMenu菜單
statusMenu.minimumWidth = 200
stsItem2.menu = statusMenu
statusMenu .insertItem(withTitle: "客戶頭像", action: #selector(statusBarItemClickMenu), keyEquivalent: "", at: 0)
statusMenu .insertItem(withTitle: "客戶信息", action: #selector(statusBarItemClickMenu), keyEquivalent: "", at: 1)
statusMenu .insertItem(withTitle: "客戶權(quán)限", action: #selector(statusBarItemClickMenu), keyEquivalent: "", at: 2)
let item3 = statusMenu.items[0+2] as NSMenuItem //類型轉(zhuǎn)換
let subMenu = NSMenu(title: "Menu3-subMenu")
statusMenu .setSubmenu(subMenu, for: item3)//添加二級目錄——subMenu
let item3_1 = NSMenuItem(title: "菜單3-1", action: #selector(statusBarItemClickMenu), keyEquivalent: "")
item3_1.target = self
let item3_2 = NSMenuItem(title: "菜單3-2", action: #selector(statusBarItemClickMenu), keyEquivalent: "")
item3_2.target = self
//添加二級目錄項
subMenu .addItem(item3_1)
subMenu .addItem(item3_2)
注意:任何對狀態(tài)欄中項或菜單的操作,都必須在NSStatusBar的.insertValue或.system .insertValue方法之前調(diào)用!
選擇時響應(yīng)的方法:
@objc func statusBarItemClickMenu(menuItem: NSMenuItem) {
print("statusBar Item clickMenu", menuItem.title)
}
效果:

選擇各項時的打印:
statusBar Item clickMenu 客戶頭像
statusBar Item clickMenu 客戶信息
statusBar Item clickMenu 客戶權(quán)限
statusBar Item clickMenu 菜單3-1
statusBar Item clickMenu 菜單3-2
Tips:使用自定義視圖
之前系統(tǒng)方法被棄用了,選擇直接添加到NSMenuItem項的.button上//自定義視圖 let sts_w = stsItem2.button?.frame.size.width let sts_h = stsItem2.button?.frame.size.height let customerV = NSView(frame: NSMakeRect(0, 0, sts_w!, sts_h!)) customerV.wantsLayer = true customerV.layer?.backgroundColor = NSColor.red.cgColor //stsItem2.view = customerV//存在問題:A.選擇stsItem2項無反應(yīng)、B.警告——'view' was deprecated in macOS 10.14: Use the standard button property instead stsItem2.button?.addSubview(customerV)效果:
注意:此操作也必須在NSStatusBar的
.insertValue或.system .insertValue方法之前調(diào)用!
最終的代碼:調(diào)用NSStatusBar的.insertValue或.system .insertValue方法,必須放在最后面!

[D].添加Dock菜單
核心方法:NSApplication的“- (nullable NSMenu *)applicationDockMenu:(NSApplication *)sender;”方法!Swift中長這樣—func applicationDockMenu(_ sender: NSApplication) -> NSMenu?!
不使用:a.不將“其公開并書寫”、b.將返回設(shè)置為nil(如下)。
func applicationDockMenu(_ sender: NSApplication) -> NSMenu? {
return nil
}
執(zhí)行時的效果:

當(dāng)在“func applicationDockMenu(_ sender: NSApplication) -> NSMenu?”方法中:添加一些‘dockMenu’項!
let dockMenu = NSMenu(title: "dockMenu")
let item1 = NSMenuItem(title: "菜單1", action: #selector(clickDockMenu), keyEquivalent: "")
item1.target = self;
let item2 = NSMenuItem(title: "菜單2", action: #selector(clickDockMenu), keyEquivalent: "")
item2.target = self;
let item3 = NSMenuItem(title: "菜單3", action: #selector(clickDockMenu), keyEquivalent: "")
item3.target = self;
dockMenu .addItem(item1)
dockMenu .addItem(item2)
dockMenu .addItem(item3)
let subMenu = NSMenu(title: "Menu3-subMenu")
let item3_1 = NSMenuItem(title: "菜單3-1", action: #selector(clickDockMenu), keyEquivalent: "")
item3_1.target = self
let item3_2 = NSMenuItem(title: "菜單3-2", action: #selector(clickDockMenu), keyEquivalent: "")
item3_2.target = self
subMenu .addItem(item3_1)
subMenu .addItem(item3_2)
dockMenu .setSubmenu(subMenu, for: item3)//添加二級目錄,可往下一直添加N級目錄 //設(shè)置后點擊"菜單3"無效,但可點擊"菜單3-1"、"菜單3-2"
展示效果:

選擇時的打印:
dockMenu clickMenu 菜單1
dockMenu clickMenu 菜單2
dockMenu clickMenu 菜單3-1
dockMenu clickMenu 菜單3-2
選擇時效果:"菜單3"無效,但可點擊"菜單3-1"、"菜單3-2"
對于Dock菜單:當(dāng)為某一項添加了子菜單后,選擇該項不響應(yīng)對應(yīng)事件,但選擇子項會響應(yīng)對應(yīng)事件!
[E].用來作為NSEvent事件的菜單!
當(dāng)有NSEvent事件時,可使用如下方法傳入NSEvent事件后創(chuàng)建一個彈出菜單!
open class func popUpContextMenu(_ menu: NSMenu, with event: NSEvent, for view: NSView)
open class func popUpContextMenu(_ menu: NSMenu, with event: NSEvent, for view: NSView, with font: NSFont?)
而獲取NSEvent事件,我用的是鼠標(biāo)右鍵點下為例子,需重寫NSApplication的- (void)rightMouseDown:(NSEvent *)event;方法
在override func rightMouseDown(with event: NSEvent) {}中:
let menuForUsr = NSMenu(title: "編輯用戶")
let item1 = NSMenuItem(title: "設(shè)置頭像", action: nil, keyEquivalent: "")
let item2 = NSMenuItem(title: "基本信息設(shè)置", action: nil, keyEquivalent: "")
let item3 = NSMenuItem(title: "權(quán)限等級申請", action: nil, keyEquivalent: "")
//對item3項,設(shè)置子菜單及相應(yīng)選項
let permissionMenu = NSMenu(title: "權(quán)限等級申請")
let item3_1 = NSMenuItem(title: "支付權(quán)限", action: nil, keyEquivalent: "")
let item3_2 = NSMenuItem(title: "對話權(quán)限", action: nil, keyEquivalent: "")
permissionMenu .addItem(item3_1)
permissionMenu .addItem(item3_2)
item3.submenu = permissionMenu
let item4 = NSMenuItem(title: "展示在線狀態(tài)", action: nil, keyEquivalent: "")
//當(dāng).onStateImage、.offStateImage尺寸超出后將把字體覆蓋掉!而.image尺寸超出后會按照圖片大小的尺寸展示出來!
item4.onStateImage = NSImage(named: "usr_item_sel")
item4.offStateImage = NSImage(named: "usr_item_nor")
let item5 = NSMenuItem(title: "客戶郵件消息", action: nil, keyEquivalent: "")
item5.image = NSImage(named: "usr_item_email")//.image與標(biāo)題一起
menuForUsr .insertItem(item1, at: 0) //指定位置插入某一項
menuForUsr .addItem(item2) //末尾添加某一項
menuForUsr .insertItem(item3, at: (0+2))
menuForUsr .addItem(item4)
menuForUsr .addItem(item5)
menuForUsr.showsStateColumn = true
//menuForUsr.setAccessibilityFrame(NSMakeRect(0, 0, 200, 200))//無效
//不管使用哪個視圖(該視圖是在Window里面),都會彈出NSMenu菜單
NSMenu .popUpContextMenu(menuForUsr, with: event, for: self.view)//彈出NSMenu菜單
效果:
所有項均不可選,因為未配置各項的action(action為nil)!
當(dāng).onStateImage、.offStateImage尺寸超出后將把字體覆蓋掉!而.image尺寸超出后會按照圖片大小的尺寸展示出來!

優(yōu)化:A.要響應(yīng)事件必須設(shè)置該項的action!B.設(shè)置圖片時,必須設(shè)置圖片展示時相應(yīng)尺寸!(.onStateImage?.size、.offStateImage?.size、.image?.size)
先添加全局變量:needShowOnline——用來記錄是否“展示在線狀態(tài)”
public var needShowOnline = false //需要“展示在線狀態(tài)”
優(yōu)化后,在override func rightMouseDown(with event: NSEvent) {}中的代碼:
let menuForUsr = NSMenu(title: "編輯用戶")
let item1 = NSMenuItem(title: "設(shè)置頭像", action: #selector(setUpIcon), keyEquivalent: "")
let item2 = NSMenuItem(title: "基本信息設(shè)置", action: #selector(setUpBaseInfo), keyEquivalent: "")
let item3 = NSMenuItem(title: "權(quán)限等級申請", action: #selector(setUpPermissionLevelApply), keyEquivalent: "")
//設(shè)置子菜單及相應(yīng)選項
let permissionMenu = NSMenu(title: "權(quán)限等級申請")
let item3_1 = NSMenuItem(title: "支付權(quán)限", action: #selector(setUpPermissionLevelApply), keyEquivalent: "")
let item3_2 = NSMenuItem(title: "對話權(quán)限", action: #selector(setUpPermissionLevelApply), keyEquivalent: "")
permissionMenu .addItem(item3_1)
permissionMenu .addItem(item3_2)
item3.submenu = permissionMenu
let item4 = NSMenuItem(title: "展示在線狀態(tài)", action: #selector(setUpShowOnline), keyEquivalent: "")
item4.onStateImage = NSImage(named: "usr_item_sel")
item4.offStateImage = NSImage(named: "usr_item_nor")
//設(shè)置圖片時,必須設(shè)置圖片展示時相應(yīng)尺寸!
//當(dāng)onStateImage、offStateImage尺寸超出后將把字體覆蓋掉!而image尺寸超出后會按照圖片大小的尺寸展示出來!
let img_W: CGFloat = 15.0
item4.onStateImage?.size = NSMakeSize(img_W, img_W)
item4.offStateImage?.size = NSMakeSize(img_W, img_W)
item4.state = (needShowOnline == true ? NSControl.StateValue.on : NSControl.StateValue.off)
let item5 = NSMenuItem(title: "客戶郵件消息", action: #selector(showEmailInfos), keyEquivalent: "")
item5.image = NSImage(named: "usr_item_email")//與標(biāo)題一起
//當(dāng)image尺寸超出后將把字體擠開!
item5.image?.size = NSMakeSize(img_W, img_W)
//item5.image?.size = NSMakeSize(img_W+50, img_W+50)
menuForUsr .insertItem(item1, at: 0) //指定位置插入某一項
menuForUsr .addItem(item2) //末尾添加某一項
menuForUsr .insertItem(item3, at: (0+2))
menuForUsr .addItem(item4)
menuForUsr .addItem(item5)
menuForUsr.showsStateColumn = true
//menuForUsr.setAccessibilityFrame(NSMakeRect(0, 0, 200, 200))//無效
//不管使用哪個視圖(該視圖是在Window里面),都會彈出NSMenu菜單
NSMenu .popUpContextMenu(menuForUsr, with: event, for: self.view)//彈出NSMenu菜單
各項NSMenuItem選擇的響應(yīng)事件:
//響應(yīng):各項NSMenuItem的事件
@objc func setUpIcon(item: NSMenuItem) {
print("setUpIcon", "item.title:\(item.title)")
}
@objc func setUpPermissionLevelApply(item: NSMenuItem) {
print("setUpPermissionLevelApply", "item.title:\(item.title)")
}
@objc func setUpBaseInfo(item: NSMenuItem) {
print("setUpBaseInfo", "item.title:\(item.title)")
}
@objc func setUpShowOnline(item: NSMenuItem) {
needShowOnline = !needShowOnline
print("setUpShowOnline", "item.title:\(item.title)", "needShowOnline:\(needShowOnline)")
}
@objc func showEmailInfos(item: NSMenuItem) {
print("showEmailInfos", "item.title:\(item.title)")
}
效果:圖片尺寸正常!所有項均可選,響應(yīng)相應(yīng)的事件!
選擇"展示在線狀態(tài)"項時,會根據(jù)needShowOnline狀態(tài)來展示是普通狀態(tài)圖片(offStateImage)或選中狀態(tài)圖片(onStateImage)!

Tips:當(dāng)需要某項 不可以被進(jìn)行選擇!
例子:讓"權(quán)限等級申請"項不可被進(jìn)行選擇!
如果是OC,之前就直接使用如下代碼實現(xiàn)了:
item3.enabled = NO; //不可點 [item3.menu setAutoenablesItems:NO];//不可點自己使用Swift中各種屬性均不能實現(xiàn):
item3.isEnabled = false、item3.menu?.autoenablesItems = false、item3.setAccessibilityEnabled(false)、item3.menu?.setAccessibilityEnabled(false)最后發(fā)現(xiàn)了設(shè)置某項的
action為nil時,某項 就不可以被進(jìn)行選擇!
于是進(jìn)行了如下封裝:通過item的title來設(shè)置相應(yīng)的action(也可以使用keyEquivalent)!func setItemEnable(item: NSMenuItem, enable :Bool) { //var actionUse: Selector? = #selector(setUpIcon) var actionUse = #selector(setUpIcon) switch item.title {//也可以使用item.keyEquivalent case "設(shè)置頭像": actionUse = #selector(setUpIcon) case "基本信息設(shè)置": actionUse = #selector(setUpBaseInfo) case "權(quán)限等級申請": actionUse = #selector(setUpPermissionLevelApply) case "展示在線狀態(tài)": actionUse = #selector(setUpShowOnline) case "客戶郵件消息": actionUse = #selector(showEmailInfos) default: break } if enable { item.action = actionUse } else { item.action = nil } }調(diào)用如下:
self .setItemEnable(item: item3, enable: false)//讓"權(quán)限等級申請"項不可被選擇!效果:"權(quán)限等級申請"項不可被選擇!
| 只響應(yīng)某個控件的鼠標(biāo)事件,才會彈出NSMenu菜單 |
|---|
添加全局變量:一張頭像的圖片
var usrImgV: NSImageView? = nil
放在在視圖控制器中:
usrImgV = NSImageView(frame: NSMakeRect(50, 50, 100, 100))
self.view .addSubview(usrImgV!)
usrImgV!.image = NSImage(named: "usr_Set_Icon")
usrImgV!.toolTip = "用戶設(shè)置"
效果:

使用如下任一代碼,都可彈出NSMenu菜單(不管哪個視圖(NSView()/self.view/self.usrImgV!)上,都會彈出NSMenu菜單)!
-
使用
NSView()創(chuàng)建一個‘不在Window里面的視圖’NSMenu .popUpContextMenu(menuForUsr, with: event, for: NSView())//使用‘不在Window里面的視圖’,會彈出菜單-但每一項都不可選!
效果:鼠標(biāo)右鍵點下都會彈出NSMenu菜單,每一項 都不可被選擇的!

-
使用
self.viewNSMenu .popUpContextMenu(menuForUsr, with: event, for: self.view)//彈出NSMenu菜單效果:鼠標(biāo)右鍵點下都會彈出NSMenu菜單,可選項都是可選擇的!
可選項都是可選擇的! -
使用
self.usrImgV!NSMenu .popUpContextMenu(menuForUsr, with: event, for: self.usrImgV!)效果:鼠標(biāo)右鍵點下都會彈出NSMenu菜單,可選項都是可選擇的!
可選項都是可選擇的!
結(jié)論:使用‘在Window里面的視圖’,彈出菜單的可選項都是可選的!、使用‘不在Window里面的視圖’,彈出菜單的每一項都不可選!
所以通過.popUpContextMenu方法不可以實現(xiàn)”只響應(yīng)某個控件的鼠標(biāo)事件,才會彈出NSMenu菜單“功能!
實現(xiàn)”只響應(yīng)某個控件的鼠標(biāo)事件,才會彈出NSMenu菜單“功能:
A.可以通過override func rightMouseDown(with event: NSEvent) { }方法返回的event,得到對應(yīng)的(NSPoint)點!再計算usrImgV相對于Window的尺寸和位置,判斷(NSPoint)點是否在頭像圖標(biāo)(usrImgV)范圍(x:50到(100+50),y:50到(100+50))內(nèi)!

思路如此,相關(guān)的代碼就不書寫了~
此思路的方法只適用于視圖層次簡單的情況!當(dāng)視圖層次繁雜時計算量就嚇人了!!??
B.自定義一個視圖類,其內(nèi)部聲明一個用于值傳遞的閉包(該閉包傳遞該視圖的NSEvent事件到視圖控制器)!
原理:在相應(yīng)的視圖里重寫并執(zhí)行“override func rightMouseUp(with event: NSEvent) {}”方法,只有在該視圖范圍內(nèi)部去進(jìn)行‘鼠標(biāo)右鍵按下’操作才會回調(diào)該方法返回(NSPoint)點信息!
(在各個位置的代碼中重寫并執(zhí)行“override func rightMouseUp(with event: NSEvent) {}”方法,都只能拿到該點擊相對于Window窗口的位置信息,所以在哪里獲取到(NSPoint)點的信息都一樣?。?/p>
如下自定義一個圖片類UsrImgV:



在“UsrImgV.swift”文件中的代碼:
import Cocoa
public typealias rightMouseUpClosure = (_ event: NSEvent) -> ()//聲明閉包-用于值的傳遞
class UsrImgV: NSImageView {
var rightMouseUpBlock : rightMouseUpClosure?//把閉包聲明為屬性
override func rightMouseUp(with event: NSEvent) {
let locPt = event .locationInWindow
print("UsrImgV point location:",locPt)
//在各個位置的代碼中,都只能拿到相對于Window窗口的位置信息,所以在哪里獲取都一樣!
rightMouseUpBlock?(event)//調(diào)用閉包
}
deinit {
print("UsrImgV deinit")
rightMouseUpBlock = nil
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// Drawing code here.
}
}
在ViewController中,將全局變量
usrImgV的類型從NSImageView換為UsrImgV!
將var usrImgV: NSImageView? = nil換為
var usrImgV: UsrImgV? = nil
初始化UsrImgV實例及相關(guān)配置:
usrImgV = UsrImgV(frame: NSMakeRect(50, 50, 100, 100))
self.view .addSubview(usrImgV!)
usrImgV!.image = NSImage(named: "usr_Set_Icon")
usrImgV!.toolTip = "用戶設(shè)置"
usrImgV!.rightMouseUpBlock = { [self] (event: NSEvent) -> () in
let locPt = event .locationInWindow
print("usrImgV!.rightMouseUpBlock point location:",locPt)//打印NSPoint信息
self .createMenuForUser(with: event)
}//閉包回調(diào)——對NSEvent事件進(jìn)行處理
將響應(yīng)NSEvent事件創(chuàng)建NSMenu菜單的邏輯,封裝為如下”func createMenuForUser(with event: NSEvent) { }“方法:
func createMenuForUser(with event: NSEvent) {
let menuForUsr = NSMenu(title: "編輯用戶")
let item1 = NSMenuItem(title: "設(shè)置頭像", action: #selector(setUpIcon), keyEquivalent: "")
let item2 = NSMenuItem(title: "基本信息設(shè)置", action: #selector(setUpBaseInfo), keyEquivalent: "")
let item3 = NSMenuItem(title: "權(quán)限等級申請", action: #selector(setUpPermissionLevelApply), keyEquivalent: "")
//設(shè)置子菜單及相應(yīng)選項
let permissionMenu = NSMenu(title: "權(quán)限等級申請")
let item3_1 = NSMenuItem(title: "支付權(quán)限", action: #selector(setUpPermissionLevelApply), keyEquivalent: "")
let item3_2 = NSMenuItem(title: "對話權(quán)限", action: #selector(setUpPermissionLevelApply), keyEquivalent: "")
permissionMenu .addItem(item3_1)
permissionMenu .addItem(item3_2)
item3.submenu = permissionMenu
let item4 = NSMenuItem(title: "展示在線狀態(tài)", action: #selector(setUpShowOnline), keyEquivalent: "")
item4.onStateImage = NSImage(named: "usr_item_sel")
item4.offStateImage = NSImage(named: "usr_item_nor")
//設(shè)置圖片時,必須設(shè)置圖片展示時相應(yīng)尺寸!
//當(dāng)onStateImage、offStateImage尺寸超出后將把字體覆蓋掉!而image尺寸超出后會按照圖片大小的尺寸展示出來!
let img_W: CGFloat = 15.0
item4.onStateImage?.size = NSMakeSize(img_W, img_W)
item4.offStateImage?.size = NSMakeSize(img_W, img_W)
item4.state = (needShowOnline == true ? NSControl.StateValue.on : NSControl.StateValue.off)
let item5 = NSMenuItem(title: "客戶郵件消息", action: #selector(showEmailInfos), keyEquivalent: "")
item5.image = NSImage(named: "usr_item_email")//與標(biāo)題一起
//當(dāng)image尺寸超出后將把字體擠開!
item5.image?.size = NSMakeSize(img_W, img_W)
//item5.image?.size = NSMakeSize(img_W+50, img_W+50)
menuForUsr .insertItem(item1, at: 0) //指定位置插入某一項
menuForUsr .addItem(item2) //末尾添加某一項
menuForUsr .insertItem(item3, at: (0+2))
menuForUsr .addItem(item4)
menuForUsr .addItem(item5)
menuForUsr.showsStateColumn = true
//menuForUsr.setAccessibilityFrame(NSMakeRect(0, 0, 200, 200))//無效
//不管使用哪個視圖(該視圖是在Window里面),都會彈出NSMenu菜單
//NSMenu .popUpContextMenu(menuForUsr, with: event, for: self.view)//彈出NSMenu菜單
NSMenu .popUpContextMenu(menuForUsr, with: event, for: self.view)//彈出NSMenu菜單
}
效果:
- 在
usrImgV中鼠標(biāo)右鍵按下時,UsrImgV類的func rightMouseUp(with event: NSEvent)方法、ViewContoller類的func rightMouseUp(with event: NSEvent)方法都會回調(diào)(都打印了獲取(NSPoint)點的信息——“point location:”和“UsrImgV point location:”)! 菜單的可選項均可選,響應(yīng)相應(yīng)的事件!
此時在ViewContoller中的usrImgV!.rightMouseUpBlock閉包部分有回調(diào),用返回的NSEvent事件創(chuàng)建NSMenu菜單! - 在
self.view中(并且在usrImgV范圍外)鼠標(biāo)右鍵按下時,僅僅是ViewContoller類的func rightMouseUp(with event: NSEvent)方法會回調(diào)(打印了獲取(NSPoint)點的信息——“point location:”)!




