Mac端除了NSButton按钮作为选项外,还可以使用NSPopUpButton(多选一)和NSMenu(菜单)
NSPopUpButton
为NSPopUpButton设置背景色——为红色
let speedPopBtn = NSPopUpButton(frame: NSMakeRect(100.0, 100.0, 100.0, 30.0)) self.view .addSubview(speedPopBtn) //设置背景色(一般不会去设置) speedPopBtn.wantsLayer = true speedPopBtn.layer?.backgroundColor = NSColor.red.cgColor
展示效果:NSPopUpButton背景色在一般情况不会去设置!
初始化NSPopUpButton实例,添加在视图控制器的view上!并设置其数组(所拥有的项)!
let speedPopBtn = NSPopUpButton(frame: NSMakeRect(100.0, 100.0, 100.0, 30.0))
self.view .addSubview(speedPopBtn)
//风格设置是否为下拉菜单(默认为false!设置为ture后需要需要在对应selector里设置选中项对应标题)
speedPopBtn.pullsDown = false//设置为false,选择后标题才改变!
//设置数组(所拥有的项)
let typeArr = ["常速", "2倍速", "4倍速", "8倍速", "16倍速"]
speedPopBtn .addItems(withTitles: typeArr)
效果:有"常速", "2倍速", "4倍速", "8倍速", "16倍速"这些选项,可以进行选择!
移除某一项: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)
效果:当前第一项变为"1/2倍速"!
//打印PopBtn的数组-当前所有项
print(speedPopBtn.itemArray)
打印如下:
[<NSMenuItem: 0x600002903720 1/2倍速>, <NSMenuItem: 0x600002911180 常速>, <NSMenuItem: 0x6000029034f0 2倍速>, <NSMenuItem: 0x600002903480 4倍速>, <NSMenuItem: 0x6000029035d0 8倍速>, <NSMenuItem: 0x600002903640 16倍速>, <NSMenuItem: 0x6000029036b0 32倍速>]
设置NSPopUpButton的选中项:open func selectItem(at index: Int)
//设置 默认选中项——首项:"1/2倍速"
speedPopBtn .selectItem(at: 0)
设置默认选中项的前后效果对比:
设置弹出菜单的位置:open var preferredEdge: NSRectEdge
属性——对应枚举的各项(minX
/minY
/maxX
/maxY
)
public enum NSRectEdge : UInt {
case minX = 0
case minY = 1
case maxX = 2
case maxY = 3
}
对应情况的效果如下图:
//添加选择时的响应事件
speedPopBtn.target = self; speedPopBtn.action = #selector(handleSelectPopBtn)
选择时的响应事件:
@objc func handleSelectPopBtn(popBtn : NSPopUpButton) {//NSPopUpButton选择
print("popBtn.indexOfSelectedItem:",popBtn.indexOfSelectedItem)
}
效果:
可以为NSPopUpButton设置新的自定义弹出菜单:NSMenu实例
[A].含有menu
属性(open var menu: NSMenu?
)的控件
//设置新的自定义弹出菜单
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;
选择自定义弹出菜单中某项时的响应事件:
//注意:响应了设置自定义弹出菜单NSMenu的项NSMenuItem对应方法,就不会响应NSPopUpButton选择的对应方法
@objc func clickThree(item: NSMenuItem) {
print("clickThree!", "item.title is",item.title)
}
UI效果:
注意:响应了设置自定义弹出菜单NSMenu的NSMenuItem项对应方法,就不会响应NSPopUpButton选择的对应方法!
操作效果如下GIF:当选择了"three"项时,该项只响应其自己的#selector(clickThree)
方法!所以只打印了“clickThree! item.title is three”!
NSMenu
NSMenu除了用在上面为NSPopUpButton控件设置新的自定义弹出菜单——[A].含有menu
属性(open var menu: NSMenu?
)的控件;
还可以:[B].添加NSApp对应顶部的菜单栏菜单(mainMenu)、[C].添加顶部的状态栏菜单(NSStatusBar)、[D].添加Dock菜单、[E].用来作为NSEvent事件的菜单!
[B].添加NSApp对应顶部的菜单栏菜单(mainMenu)
//获取NSApp的主目录
let mainMenu = NSApp.mainMenu
let itemRoot = NSMenuItem(title: "根菜单项", action: #selector(clickMenu), keyEquivalent: "")
itemRoot.title = "Load_TEXT"//设置根目录项的title文字无效
let rootMenu = NSMenu(title: "RootMenu-根目录")//文字会以根目录的title来显示
//必须先设置根目录(第一层)——rootMenu、根目录项——itemRoot
mainMenu? .setSubmenu(rootMenu, for: itemRoot)//添加一级目录(根目录),可往下一直添加N级目录
//在末尾添加"根菜单项"
//mainMenu? .addItem(itemRoot)
//在指定位置添加"根菜单项"
mainMenu? .insertItem(itemRoot, at: (0 + 2))
步骤:1.先通过NSApp.mainMenu
获取到NSApp的主目录,2.必须再通过.setSubmenu
设置上根目录(第一层):NSMenu
实例、根目录项:NSMenuItem
实例,3.再通过.addItem
或.insertItem
插入到NSApp的主目录——此时在屏幕顶部App的菜单栏菜单中就有了添加的新项("RootMenu-根目录")!
效果:点击选择时不会弹出根目录的菜单!(因为并未对根目录 添加下一级的菜单!)
//添加一级目录项
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)
选择时响应的方法:
@objc func clickMenu(menuItem: NSMenuItem) {
print("App clickMenu", menuItem.title)
}
效果:
选择各项时的打印:
App clickMenu 菜单1
App clickMenu 菜单2
App clickMenu 菜单3
App clickMenu 菜单3-1
App clickMenu 菜单3-2
[C].添加顶部的状态栏菜单(NSStatusBar)
//为状态栏定义并添加2个项(App是开启状态就会在状态栏上展示出来)
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//是否在状态栏中可见,默认为true
let stsItem2 = NSStatusBar.system .statusItem(withLength: NSStatusItem.squareLength)//标准的长度
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到状态栏(必须调用,才会展示新添加的2个项!)
//NSStatusBar .insertValue(stsItem2, inPropertyWithKey: "statusMenu")
NSStatusBar .system .insertValue(stsItem2, inPropertyWithKey: "statusMenu")
注意:在最后必须用NSStatusBar的.insertValue
或.system .insertValue
任一方法,加入上面任何一项NSStatusItem到状态栏——才会展示新添加的2个项!
效果:为状态栏添加了2个项(App在开启状态时,就会在状态栏上展示出来)!其中stsItem长度为120、stsItem2为标准长度!
为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: "客户权限", action: #selector(statusBarItemClickMenu), keyEquivalent: "", at: 2)
let item3 = statusMenu.items[0+2] as NSMenuItem //类型转换
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)
注意:任何对状态栏中项或菜单的操作,都必须在NSStatusBar的.insertValue
或.system .insertValue
方法之前调用!
选择时响应的方法:
@objc func statusBarItemClickMenu(menuItem: NSMenuItem) {
print("statusBar Item clickMenu", menuItem.title)
}
效果:
选择各项时的打印:
statusBar Item clickMenu 客户头像
statusBar Item clickMenu 客户信息
statusBar Item clickMenu 客户权限
statusBar Item clickMenu 菜单3-1
statusBar Item clickMenu 菜单3-2
Tips:使用自定义视图
之前系统方法被弃用了,选择直接添加到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项无反应、B.警告——'view' was deprecated in macOS 10.14: Use the standard button property instead stsItem2.button?.addSubview(customerV)
效果:
注意:此操作也必须在NSStatusBar的
.insertValue
或.system .insertValue
方法之前调用!
最终的代码:调用NSStatusBar的.insertValue
或.system .insertValue
方法,必须放在最后面!
[D].添加Dock菜单
核心方法:NSApplication的“- (nullable NSMenu *)applicationDockMenu:(NSApplication *)sender;
”方法!Swift中长这样—func applicationDockMenu(_ sender: NSApplication) -> NSMenu?
!
不使用:a.不将“其公开并书写”、b.将返回设置为nil(如下)。
func applicationDockMenu(_ sender: NSApplication) -> NSMenu? {
return nil
}
执行时的效果:
当在“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级目录 //设置后点击"菜单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菜单:当为某一项添加了子菜单后,选择该项不响应对应事件,但选择子项会响应对应事件!
[E].用来作为NSEvent事件的菜单!
当有NSEvent事件时,可使用如下方法传入NSEvent事件后创建一个弹出菜单!
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事件,我用的是鼠标右键点下为例子,需重写NSApplication的- (void)rightMouseDown:(NSEvent *)event;
方法
在override func rightMouseDown(with event: NSEvent) {}
中:
let menuForUsr = NSMenu(title: "编辑用户")
let item1 = NSMenuItem(title: "设置头像", action: nil, keyEquivalent: "")
let item2 = NSMenuItem(title: "基本信息设置", action: nil, keyEquivalent: "")
let item3 = NSMenuItem(title: "权限等级申请", action: nil, keyEquivalent: "")
//对item3项,设置子菜单及相应选项
let permissionMenu = NSMenu(title: "权限等级申请")
let item3_1 = NSMenuItem(title: "支付权限", action: nil, keyEquivalent: "")
let item3_2 = NSMenuItem(title: "对话权限", action: nil, keyEquivalent: "")
permissionMenu .addItem(item3_1)
permissionMenu .addItem(item3_2)
item3.submenu = permissionMenu
let item4 = NSMenuItem(title: "展示在线状态", action: nil, keyEquivalent: "")
//当.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与标题一起
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)!
当.onStateImage、.offStateImage尺寸超出后将把字体覆盖掉!而.image尺寸超出后会按照图片大小的尺寸展示出来!
优化:A.要响应事件必须设置该项的action
!B.设置图片时,必须设置图片展示时相应尺寸!(.onStateImage?.size
、.offStateImage?.size
、.image?.size
)
先添加全局变量:needShowOnline
——用来记录是否“展示在线状态”
public var needShowOnline = false //需要“展示在线状态”
优化后,在override func rightMouseDown(with event: NSEvent) {}
中的代码:
let menuForUsr = NSMenu(title: "编辑用户")
let item1 = NSMenuItem(title: "设置头像", action: #selector(setUpIcon), keyEquivalent: "")
let item2 = NSMenuItem(title: "基本信息设置", action: #selector(setUpBaseInfo), keyEquivalent: "")
let item3 = NSMenuItem(title: "权限等级申请", action: #selector(setUpPermissionLevelApply), keyEquivalent: "")
//设置子菜单及相应选项
let permissionMenu = NSMenu(title: "权限等级申请")
let item3_1 = NSMenuItem(title: "支付权限", action: #selector(setUpPermissionLevelApply), keyEquivalent: "")
let item3_2 = NSMenuItem(title: "对话权限", action: #selector(setUpPermissionLevelApply), keyEquivalent: "")
permissionMenu .addItem(item3_1)
permissionMenu .addItem(item3_2)
item3.submenu = permissionMenu
let item4 = NSMenuItem(title: "展示在线状态", action: #selector(setUpShowOnline), keyEquivalent: "")
item4.onStateImage = NSImage(named: "usr_item_sel")
item4.offStateImage = NSImage(named: "usr_item_nor")
//设置图片时,必须设置图片展示时相应尺寸!
//当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")//与标题一起
//当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选择的响应事件:
//响应:各项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)")
}
效果:图片尺寸正常!所有项均可选,响应相应的事件!
选择"展示在线状态"项时,会根据needShowOnline
状态来展示是普通状态图片(offStateImage
)或选中状态图片(onStateImage
)!
Tips:当需要某项 不可以被进行选择!
例子:让"权限等级申请"项不可被进行选择!
如果是OC,之前就直接使用如下代码实现了:
item3.enabled = NO; //不可点 [item3.menu setAutoenablesItems:NO];//不可点
自己使用Swift中各种属性均不能实现:
item3.isEnabled = false
、item3.menu?.autoenablesItems = false
、item3.setAccessibilityEnabled(false)
、item3.menu?.setAccessibilityEnabled(false)
最后发现了设置某项的
action
为nil
时,某项 就不可以被进行选择!
于是进行了如下封装:通过item的title
来设置相应的action
(也可以使用keyEquivalent
)!func setItemEnable(item: NSMenuItem, enable :Bool) { //var actionUse: Selector? = #selector(setUpIcon) var actionUse = #selector(setUpIcon) switch item.title {//也可以使用item.keyEquivalent case "设置头像": actionUse = #selector(setUpIcon) case "基本信息设置": actionUse = #selector(setUpBaseInfo) case "权限等级申请": actionUse = #selector(setUpPermissionLevelApply) case "展示在线状态": actionUse = #selector(setUpShowOnline) case "客户邮件消息": actionUse = #selector(showEmailInfos) default: break } if enable { item.action = actionUse } else { item.action = nil } }
调用如下:
self .setItemEnable(item: item3, enable: false)//让"权限等级申请"项不可被选择!
效果:"权限等级申请"项不可被选择!
只响应某个控件的鼠标事件,才会弹出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 = "用户设置"
效果:
使用如下任一代码,都可弹出NSMenu菜单(不管哪个视图(NSView()
/self.view
/self.usrImgV!
)上,都会弹出NSMenu菜单)!
-
使用
NSView()
创建一个‘不在Window里面的视图’NSMenu .popUpContextMenu(menuForUsr, with: event, for: NSView())//使用‘不在Window里面的视图’,会弹出菜单-但每一项都不可选!
效果:鼠标右键点下都会弹出NSMenu菜单,每一项 都不可被选择的!
-
使用
self.view
NSMenu .popUpContextMenu(menuForUsr, with: event, for: self.view)//弹出NSMenu菜单
效果:鼠标右键点下都会弹出NSMenu菜单,可选项都是可选择的!
-
使用
self.usrImgV!
NSMenu .popUpContextMenu(menuForUsr, with: event, for: self.usrImgV!)
效果:鼠标右键点下都会弹出NSMenu菜单,可选项都是可选择的!
结论:使用‘在Window里面的视图’,弹出菜单的可选项都是可选的!、使用‘不在Window里面的视图’,弹出菜单的每一项都不可选!
所以通过.popUpContextMenu
方法不可以实现”只响应某个控件的鼠标事件,才会弹出NSMenu菜单“功能!
实现”只响应某个控件的鼠标事件,才会弹出NSMenu菜单“功能:
A.可以通过override func rightMouseDown(with event: NSEvent) { }
方法返回的event,得到对应的(NSPoint)点!再计算usrImgV相对于Window的尺寸和位置,判断(NSPoint)点是否在头像图标(usrImgV)范围(x:50到(100+50),y:50到(100+50))内!
思路如此,相关的代码就不书写了~
此思路的方法只适用于视图层次简单的情况!当视图层次繁杂时计算量就吓人了!!😳
B.自定义一个视图类,其内部声明一个用于值传递的闭包(该闭包传递该视图的NSEvent事件到视图控制器)!
原理:在相应的视图里重写并执行“override func rightMouseUp(with event: NSEvent) {}
”方法,只有在该视图范围内部去进行‘鼠标右键按下’操作才会回调该方法返回(NSPoint)点信息!
(在各个位置的代码中重写并执行“override func rightMouseUp(with event: NSEvent) {}
”方法,都只能拿到该点击相对于Window窗口的位置信息,所以在哪里获取到(NSPoint)点的信息都一样!)
如下自定义一个图片类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)//调用闭包
}
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实例及相关配置:
usrImgV = UsrImgV(frame: NSMakeRect(50, 50, 100, 100))
self.view .addSubview(usrImgV!)
usrImgV!.image = NSImage(named: "usr_Set_Icon")
usrImgV!.toolTip = "用户设置"
usrImgV!.rightMouseUpBlock = { [self] (event: NSEvent) -> () in
let locPt = event .locationInWindow
print("usrImgV!.rightMouseUpBlock point location:",locPt)//打印NSPoint信息
self .createMenuForUser(with: event)
}//闭包回调——对NSEvent事件进行处理
将响应NSEvent事件创建NSMenu菜单的逻辑,封装为如下”func createMenuForUser(with event: NSEvent) { }
“方法:
func createMenuForUser(with event: NSEvent) {
let menuForUsr = NSMenu(title: "编辑用户")
let item1 = NSMenuItem(title: "设置头像", action: #selector(setUpIcon), keyEquivalent: "")
let item2 = NSMenuItem(title: "基本信息设置", action: #selector(setUpBaseInfo), keyEquivalent: "")
let item3 = NSMenuItem(title: "权限等级申请", action: #selector(setUpPermissionLevelApply), keyEquivalent: "")
//设置子菜单及相应选项
let permissionMenu = NSMenu(title: "权限等级申请")
let item3_1 = NSMenuItem(title: "支付权限", action: #selector(setUpPermissionLevelApply), keyEquivalent: "")
let item3_2 = NSMenuItem(title: "对话权限", action: #selector(setUpPermissionLevelApply), keyEquivalent: "")
permissionMenu .addItem(item3_1)
permissionMenu .addItem(item3_2)
item3.submenu = permissionMenu
let item4 = NSMenuItem(title: "展示在线状态", action: #selector(setUpShowOnline), keyEquivalent: "")
item4.onStateImage = NSImage(named: "usr_item_sel")
item4.offStateImage = NSImage(named: "usr_item_nor")
//设置图片时,必须设置图片展示时相应尺寸!
//当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")//与标题一起
//当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
中鼠标右键按下时,UsrImgV类的func rightMouseUp(with event: NSEvent)
方法、ViewContoller类的func rightMouseUp(with event: NSEvent)
方法都会回调(都打印了获取(NSPoint)点的信息——“point location:”和“UsrImgV point location:”)! 菜单的可选项均可选,响应相应的事件!
此时在ViewContoller中的usrImgV!.rightMouseUpBlock
闭包部分有回调,用返回的NSEvent事件创建NSMenu菜单! - 在
self.view
中(并且在usrImgV
范围外)鼠标右键按下时,仅仅是ViewContoller类的func rightMouseUp(with event: NSEvent)
方法会回调(打印了获取(NSPoint)点的信息——“point location:”)!