Mac OS的選項-NSPopUpButton(多選一)/NSMenu(菜單)



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è)置targetaction

//添加選擇時的響應(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>

選擇時不會彈出根目錄的菜單!


為根目錄添加一級、二級(……)目錄及相應(yīng)項:

//添加一級目錄項
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項

選擇時的打印:

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菜單

效果:
所有項均不可選,因為未配置各項的actionaction為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è)置某項actionnil時,某項不可以被進(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.view

    NSMenu .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

右鍵選擇"New File",創(chuàng)建新文件
選擇"Cocoa Class"類型
繼承自NSImageView,命名為"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:”)!





之前Mac端的代碼是OC寫的,現(xiàn)在看感覺好繁雜。。??

之后盡量用Swift寫吧~









goyohol's essay

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容