美文网首页macOS开发备忘录
MacOS开发——NSMenu应用详解

MacOS开发——NSMenu应用详解

作者: 王辛朋朋 | 来源:发表于2019-06-28 12:08 被阅读28次

    文章来源汇总:
    https://my.oschina.net/u/2340880/blog/1377546
    https://blog.csdn.net/lovechris00
    https://blog.csdn.net/lovechris00/article/details/78002161
    https://blog.csdn.net/henanzhg/article/details/70598302

    一、引言
    NSMenu在Mac桌面软件开发中往往有3个方面的应用,作为程序的主菜单栏使用,作为视图邮件菜单使用和作为Dock菜单使用。

    二、主应用菜单

    使用Xcode新建OX S应用时,可以选择使用Storyboard。Storyboard里面会自动创建一个菜单栏,你可以自行在菜单中进行增删改操作,菜单中的Item触发方法也可以直接与AppDelegate进行关联,实现自定义的菜单逻辑,如图:

    image

    三:Dock菜单

    当一款Mac桌面软件运行时,会在Dock栏上显示一个图标,当在此图标上点击右键时,会出现一个Dock菜单,自定义此Dock菜单也十分容易,直接在AppDelegate中重写如下方法即可:

    -(NSMenu *)applicationDockMenu:(NSApplication *)sender{
        NSMenu * menu = [[NSMenu alloc]initWithTitle:@"Menu"];
        NSMenuItem * item1 = [[NSMenuItem alloc]initWithTitle:@"菜单1" action:@selector(click) keyEquivalent:@""];
        item1.target = self;
        NSMenuItem * item2 = [[NSMenuItem alloc]initWithTitle:@"菜单2" action:@selector(click) keyEquivalent:@""];
        item2.target = self;
        NSMenuItem * item3 = [[NSMenuItem alloc]initWithTitle:@"菜单3" action:@selector(click) keyEquivalent:@""];
        NSMenu * subMenu = [[NSMenu alloc]initWithTitle:@"subMenu"];
        NSMenuItem * item4 = [[NSMenuItem alloc]initWithTitle:@"菜单4" action:@selector(click) keyEquivalent:@""];
        item4.target = self;
        [subMenu addItem:item4];
        [menu addItem:item1];
        [menu addItem:item2];
        [menu addItem:item3];
        [menu setSubmenu:subMenu forItem:item3];
        return menu;
    }
    

    效果如下:

    image

    在系统代理方法中返回该目录

    - (NSMenu *)applicationDockMenu:(NSApplication *)sender
    {
        return self.dockMenu;
    }
    

    四、视图右键弹出菜单

    视图右键弹出菜单是基于NSView视图的,例如:

    - (void)viewDidLoad {
        [super viewDidLoad];
        NSMenu * menu = [[NSMenu alloc]initWithTitle:@"Menu"];
        NSMenuItem * item1 = [[NSMenuItem alloc]initWithTitle:@"菜单1" action:@selector(click) keyEquivalent:@""];
        item1.target = self;
        NSMenuItem * item2 = [[NSMenuItem alloc]initWithTitle:@"菜单2" action:@selector(click) keyEquivalent:@""];
        item2.target = self;
        NSMenuItem * item3 = [[NSMenuItem alloc]initWithTitle:@"菜单3" action:@selector(click) keyEquivalent:@""];
        NSMenu * subMenu = [[NSMenu alloc]initWithTitle:@"subMenu"];
        NSMenuItem * item4 = [[NSMenuItem alloc]initWithTitle:@"菜单4" action:@selector(click) keyEquivalent:@""];
        item4.target = self;
        [subMenu addItem:item4];
        [menu addItem:item1];
        [menu addItem:item2];
        [menu addItem:item3];
        [menu setSubmenu:subMenu forItem:item3];
        [self.view setMenu:menu];
    }
    

    效果如下:

    image

    五、NSMenuItem详解

    NSMenuItem是菜单中的每一个菜单选项对象,其中常用属性方法如下:

    //设置是否启用用户快捷键
    + (void)setUsesUserKeyEquivalents:(BOOL)flag;
    //设置用户快捷键启用状态
    + (BOOL)usesUserKeyEquivalents;
    //创建一个分割线
    + (NSMenuItem *)separatorItem;
    //使用标题,快捷键和方法选择器来对Item进行初始化
    - (instancetype)initWithTitle:(NSString *)string action:(nullable SEL)selector keyEquivalent:(NSString *)charCode;
    //其所在的菜单对象
    @property (nullable, assign) NSMenu *menu;
    //其是否有子菜单
    @property (readonly) BOOL hasSubmenu;
    //子菜单对象
    @property (nullable, strong) NSMenu *submenu;
    //如果此Item是某个子菜单中的,此属性获取与子菜单关联的父item
    @property (nullable, readonly, assign) NSMenuItem *parentItem;
    //Item标题
    @property (copy) NSString *title;
    //富文本标题
    @property (nullable, copy) NSAttributedString *attributedTitle;
    //是否是分隔线Item
    @property (getter=isSeparatorItem, readonly) BOOL separatorItem;
    //绑定的快捷键
    @property (copy) NSString *keyEquivalent;
    //快捷键类型
    /*
    typedef NS_OPTIONS(NSUInteger, NSEventModifierFlags) {
        NSEventModifierFlagCapsLock           = 1 << 16, // Caps lock键
        NSEventModifierFlagShift              = 1 << 17, // shift键
        NSEventModifierFlagControl            = 1 << 18, // control键
        NSEventModifierFlagOption             = 1 << 19, // option键
        NSEventModifierFlagCommand            = 1 << 20, // command键
        NSEventModifierFlagNumericPad         = 1 << 21, // 小键盘任意键
        NSEventModifierFlagHelp               = 1 << 22, // 帮助键
        NSEventModifierFlagFunction           = 1 << 23, // 任意功能按钮
    };
    */
    @property NSEventModifierFlags keyEquivalentModifierMask;
    //Item图标
    @property (nullable, strong) NSImage *image;
    //Item状态
    @property NSInteger state;
    //开启状态下的图标
    @property (null_resettable, strong) NSImage *onStateImage;
    //关闭状态下的图标
    @property (nullable, strong) NSImage *offStateImage;
    //混合状态下的图标
    @property (null_resettable, strong) NSImage *mixedStateImage;
    //是否有效
    @property (getter=isEnabled) BOOL enabled;
    //是否前置
    @property (getter=isAlternate) BOOL alternate;
    //Item缩进级别
    @property NSInteger indentationLevel;
    //设置交互响应者
    @property (nullable, weak) id target;
    //设置交互相应方法
    @property (nullable) SEL action;
    //设置tag值
    @property NSInteger tag;
    //是否高亮
    @property (getter=isHighlighted, readonly) BOOL highlighted;
    //设置是否隐藏
    @property (getter=isHidden) BOOL hidden;
    //设置提示文本
    @property (nullable, copy) NSString *toolTip;
    

    六、NSMenu详解

    //初始化方法
    - (instancetype)initWithTitle:(NSString *)title;
    //标题
    @property (copy) NSString *title;
    //在所在的交互点弹出菜单
    + (void)popUpContextMenu:(NSMenu*)menu withEvent:(NSEvent*)event forView:(NSView*)view;
    + (void)popUpContextMenu:(NSMenu*)menu withEvent:(NSEvent*)event forView:(NSView*)view withFont:(nullable NSFont*)font;
    - (BOOL)popUpMenuPositioningItem:(nullable NSMenuItem *)item atLocation:(NSPoint)location inView:(nullable NSView *)view NS_AVAILABLE_MAC(10_6);
    //设置菜单栏是否可见
    + (void)setMenuBarVisible:(BOOL)visible;
    + (BOOL)menuBarVisible;
    //父菜单
    @property (nullable, assign) NSMenu *supermenu;
    //插入Item
    - (void)insertItem:(NSMenuItem *)newItem atIndex:(NSInteger)index;
    - (NSMenuItem *)insertItemWithTitle:(NSString *)string action:(nullable SEL)selector keyEquivalent:(NSString *)charCode atIndex:(NSInteger)index;
    //添加Item
    - (void)addItem:(NSMenuItem *)newItem;
    - (NSMenuItem *)addItemWithTitle:(NSString *)string action:(nullable SEL)selector keyEquivalent:(NSString *)charCode;
    //删除某个位置的Item
    - (void)removeItemAtIndex:(NSInteger)index;
    //删除Item
    - (void)removeItem:(NSMenuItem *)item;
    //为某个Item设置子菜单
    - (void)setSubmenu:(nullable NSMenu *)menu forItem:(NSMenuItem *)item;
    //删除所有Item
    - (void)removeAllItems;
    //Item数组
    @property (readonly, copy) NSArray<NSMenuItem *> *itemArray;
    //获取Item个数
    @property (readonly) NSInteger numberOfItems;
    //获取某个位置的Item
    - (nullable NSMenuItem *)itemAtIndex:(NSInteger)index;
    //获取某个Item的位置
    - (NSInteger)indexOfItem:(NSMenuItem *)item;
    - (NSInteger)indexOfItemWithTitle:(NSString *)title;
    - (NSInteger)indexOfItemWithTag:(NSInteger)tag;
    - (NSInteger)indexOfItemWithSubmenu:(nullable NSMenu *)submenu;
    - (NSInteger)indexOfItemWithTarget:(nullable id)target andAction:(nullable SEL)actionSelector;
    //根据标题获取item
    - (nullable NSMenuItem *)itemWithTitle:(NSString *)title;
    //根据tag获取Item
    - (nullable NSMenuItem *)itemWithTag:(NSInteger)tag;
    //刷新菜单
    - (void)update;
    //获取菜单高度
    @property (readonly) CGFloat menuBarHeight;
    //取消菜单
    - (void)cancelTracking;
    - (void)cancelTrackingWithoutAnimation;
    //获取高亮的Item
    @property (nullable, readonly, strong) NSMenuItem *highlightedItem;
    //最小宽度
    @property CGFloat minimumWidth;
    //尺寸
    @property (readonly) NSSize size;
    //字体
    @property (null_resettable, strong) NSFont *font;
    

    七、状态栏(NSStatusItem)添加菜单
    Mac OS X有时会考虑添加NSStatusItem的情况,NSStatusItem并非Mac App必须要使用的,但使用NSStatusItem可以把一些简单的拓展功能放到这里,方便用户的使用。

    很多App包括苹果公司本身都使用了NSStatusItem,如上图所示,下面我将分享一些NSStatusItem的使用经验。

    使用NSStatusItem应该作为全局变量,而不是局部变量,如果作为局部变量,将没有效果。

    .h文件中可以进行如下定义:

    @interface AppDelegate : NSObject {
        NSStatusItem *_statusItem;
    }
    

    .m文件进行如下实现:

    _statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
        
        [_statusItem setImage:[NSImage imageNamed:@"image.png"]];
        [_statusItem setToolTip:@"StatusItem"];
        [_statusItem setHighlightMode:YES];
    

    使用位置

    NSMenu *subMenu = [[NSMenu alloc] initWithTitle:@"Load_TEXT"];
        
    [subMenu addItemWithTitle:@"Load1"action:@selector(load1) keyEquivalent:@"E"];
    [subMenu addItemWithTitle:@"Load2"action:@selector(load2) keyEquivalent:@"R"];
    
    statusItem.menu = subMenu;
    

    八、给一个NSView添加右击菜单
    这里的快捷键可能无法直接使用。邮件点击该 View,再使用快捷键有效。
    一个目录可以添加给多个控件。

    - (void)addViewMenu{
    
        NSMenu *newMenu = [[NSMenu alloc] initWithTitle:@"View 的目录"];
        
        NSMenuItem* newItem = [[NSMenuItem alloc] initWithTitle:@"View 的item" action:@selector(load1) keyEquivalent:@"E"];
        
        [newItem setEnabled:YES];
        [newItem setTarget:self];
        
        [newMenu addItem:newItem];
        
        [self.indicater0 setMenu:newMenu];
        [self.indicater1 setMenu:newMenu];
    }
    

    总结:
    1.一级目录 和 二级目录 都属于 NSMenu 类。
    2.一级目录和二级目录的内容属于 NSMenuItem 类。
    3.一个一级目录下,只能有一个二级目录,只能用 setSubmenu 设置一级下的二级目录;
    4.二级目录下面可以有多个 item,可以用 addItemWithTitle 和 insertItemWithTitle 来添加。
    5.keyEquivalent 是配置的快捷键,如果配置 E,则 command + shif + E 可以调用这个方法。 E 区分大小写,小写无效。
    6.insertItem 的时候,需要注意 index 的位置。

    相关文章

      网友评论

        本文标题:MacOS开发——NSMenu应用详解

        本文链接:https://www.haomeiwen.com/subject/pksscctx.html