...">
美文网首页MacOS
Mac Catalyst - macOS 控件

Mac Catalyst - macOS 控件

作者: LeungKinKeung | 来源:发表于2021-08-31 11:30 被阅读0次

    顶部状态栏菜单

    (这里以QQ体验版为例)


    MacCatalyst - Menu Bar.png
    // 在AppDelegate.m里实现以下方法
    
    #if TARGET_OS_MACCATALYST
    - (void)buildMenuWithBuilder:(id<UIMenuBuilder>)builder
    {
        // 移除“显示(View)”菜单
        [builder removeMenuForIdentifier:UIMenuView];
        // 移除“格式(Format)”菜单
        [builder removeMenuForIdentifier:UIMenuFormat];
        // 移除“服务(Services)”菜单
        [builder removeMenuForIdentifier:UIMenuServices];
        // 移除“新建(New)”菜单
        [builder removeMenuForIdentifier:UIMenuNewScene];
        
        // 添加"偏好设置",以下采用的是自定义的方案(后面再提供另一种自定义"偏好设置"菜单项的方法,免去翻译的麻烦)
        [builder removeMenuForIdentifier:UIMenuPreferences];
        UIKeyCommand *preferencesItem =
        [UIKeyCommand commandWithTitle:@"偏好设置" // 或:@"Preferences…"
                                 image:nil
                                action:@selector(orderFrontPreferencesPanel:)
                                 input:@","
                         modifierFlags:UIKeyModifierCommand
                          propertyList:nil];
        UIMenu *preferencesMenu     =
        [UIMenu menuWithTitle:@"Preferences"
                        image:nil
                   identifier:UIMenuPreferences
                      options:UIMenuOptionsDisplayInline
                     children:@[preferencesItem]];
        [builder insertSiblingMenu:preferencesMenu afterMenuForIdentifier:UIMenuAbout];
        //[builder insertChildMenu:preferencesMenu atEndOfMenuForIdentifier:UIMenuAbout];// 多级子菜单
        
        // 添加"应用"菜单
        UIKeyCommand *newsItem =
        [UIKeyCommand commandWithTitle:@"动态"
                                 image:nil
                                action:@selector(newsMenuItemClick:)
                                 input:@"1"
                         modifierFlags:UIKeyModifierCommand
                          propertyList:nil];
        
        UIKeyCommand *createGroupsItem =
        [UIKeyCommand commandWithTitle:@"创建群聊"
                                 image:nil
                                action:@selector(createGroupsMenuItemClick:)
                                 input:@"2"
                         modifierFlags:UIKeyModifierCommand
                          propertyList:nil];
        
        UIKeyCommand *addFriendItem =
        [UIKeyCommand commandWithTitle:@"加好友/群"
                                 image:nil
                                action:@selector(addFriendMenuItemClick:)
                                 input:@"3"
                         modifierFlags:UIKeyModifierCommand
                          propertyList:nil];
        
        UIMenu *appMenu =
        [UIMenu menuWithTitle:@"应用"
                        image:nil
                   identifier:@"com.qq.menu.app"
                      options:UIMenuOptionsDestructive
                     children:@[newsItem,createGroupsItem,addFriendItem]];
        
        [builder insertSiblingMenu:appMenu afterMenuForIdentifier:UIMenuEdit];
    }
    
    - (void)orderFrontStandardAboutPanel:(UICommand *)sender 
    {
        // 如果需要自定义"关于"窗口,就实现此方法
    }
    
    - (void)orderFrontPreferencesPanel:(UIKeyCommand *)sender
    {
        // 点击了"偏好设置"
    }
    
    // 其他Action方法不举例了...
    #endif
    

    另一种自定义"偏好设置"菜单项的方法,可免去翻译的麻烦:

    File -> New -> File... (或Command + N)添加一个Settings Bundle

    MacCatalyst - Settings Bundle.png

    然后在AppDelegate.m里实现以下方法(不需要上面创建UIMenu的代码)

    - (void)orderFrontPreferencesPanel:(UIKeyCommand *)sender
    {
        // 显示自定义的"偏好设置"窗口
    }
    
    MacCatalyst - Preferences.png

    生成的语言:

    构建菜单时,菜单使用的语言跟随启动时的系统语言(macOS原生只会生成英文的菜单),使用 [[UIMenuSystem mainSystem] setNeedsRebuild] 后菜单将重置并调用- (void)buildMenuWithBuilder:(id<UIMenuBuilder>)builder方法,但是应用程序启动时系统是什么语言,生成的菜单就是使用什么语言的,就算你切换了别的系统语言

    继承关系:

    UIMenuElement < UIMenu

    UIMenuElement < UICommand < UIKeyCommand(相比UICommand多了个非全局的快捷键功能)

    注意菜单项的propertyList(可以看作菜单项的唯一标识符)为空时action是不可以一样的,否则报错:

    inserted menu has duplicate submenu, command or key command, or a key command is missing input or action

    UIMenuBuilder协议:

    // (待补充)
    @property (nonatomic, readonly) UIMenuSystem *system;
    // 获取菜单项,字符串枚举在UIMenu.h里
    - (nullable UIMenu *)menuForIdentifier:(UIMenuIdentifier)identifier NS_SWIFT_NAME(menu(for:));
    // (待补充)
    - (nullable UIAction *)actionForIdentifier:(UIActionIdentifier)identifier NS_SWIFT_NAME(action(for:));
    // 通过action或propertyList查找菜单项
    - (nullable UICommand *)commandForAction:(SEL)action propertyList:(nullable id)propertyList NS_REFINED_FOR_SWIFT;
    // 替换菜单
    - (void)replaceMenuForIdentifier:(UIMenuIdentifier)replacedIdentifier withMenu:(UIMenu *)replacementMenu NS_SWIFT_NAME(replace(menu:with:));
    // 遍历子菜单
    - (void)replaceChildrenOfMenuForIdentifier:(UIMenuIdentifier)parentIdentifier
                             fromChildrenBlock:(NSArray<UIMenuElement *> *(NS_NOESCAPE ^)(NSArray<UIMenuElement *> *))childrenBlock NS_SWIFT_NAME(replaceChildren(ofMenu:from:));
    // 以展开的形式把菜单插入到指定位置(详情见下图)
    - (void)insertSiblingMenu:(UIMenu *)siblingMenu beforeMenuForIdentifier:(UIMenuIdentifier)siblingIdentifier NS_SWIFT_NAME(insertSibling(_:beforeMenu:));
    - (void)insertSiblingMenu:(UIMenu *)siblingMenu afterMenuForIdentifier:(UIMenuIdentifier)siblingIdentifier NS_SWIFT_NAME(insertSibling(_:afterMenu:));
    // 以收起的形式把菜单插入到指定位置(详情见下图)
    - (void)insertChildMenu:(UIMenu *)childMenu atStartOfMenuForIdentifier:(UIMenuIdentifier)parentIdentifier NS_SWIFT_NAME(insertChild(_:atStartOfMenu:));
    - (void)insertChildMenu:(UIMenu *)childMenu atEndOfMenuForIdentifier:(UIMenuIdentifier)parentIdentifier NS_SWIFT_NAME(insertChild(_:atEndOfMenu:));
    // 移除指定菜单,字符串枚举在UIMenu.h里
    - (void)removeMenuForIdentifier:(UIMenuIdentifier)removedIdentifier NS_SWIFT_NAME(remove(menu:));
    

    Sibling Menu和Child Menu的区别(代码示例)

    UICommand *children1 =
    [UICommand commandWithTitle:@"Children 1"
                          image:nil
                         action:@selector(children1Click:)
                   propertyList:nil];
    UICommand *children2 =
    [UICommand commandWithTitle:@"Children 2"
                          image:nil
                         action:@selector(children2Click:)
                   propertyList:nil];
    UIMenu *menu    =
    [UIMenu menuWithTitle:@"Menu"
                    image:nil
               identifier:@"com.qq.menu.testMenu"
                  options:UIMenuOptionsDisplayInline
                 children:@[children1,children2]];
    //[builder insertSiblingMenu:menu afterMenuForIdentifier:UIMenuAbout]; // Sibling Menu
    //[builder insertChildMenu:menu atEndOfMenuForIdentifier:UIMenuAbout]; // Child Menu
    

    Sibling Menu:

    MacCatalyst - Sibling Menu.png

    Child Menu:

    MacCatalyst - Child Menu.png

    UIMenuElement/UICommand/UIKeyCommand:

    // 标题
    @property (nonatomic, copy) NSString *title API_AVAILABLE(ios(13.0));
    // 显示在标题左边的图标
    @property (nullable, nonatomic, copy) UIImage *image API_AVAILABLE(ios(13.0));
    // 鼠标在上面停留一会后出现的文本提示(效果见下图Discoverability Title)
    @property (nullable, nonatomic, copy) NSString *discoverabilityTitle API_AVAILABLE(ios(9.0));
    // 菜单项的propertyList为空时action是不可以一样的
    @property (nullable, nonatomic, readonly) SEL action;
    // 快捷键(a~Z、1~9...)
    @property (nullable, nonatomic, readonly) NSString *input;
    // 快捷键的修饰键(Command、Shift、Option...)
    @property (nonatomic, readonly) UIKeyModifierFlags modifierFlags;
    // 假如action是一样的时候,使用此属性来区分命令,可看作为标识符
    @property (nullable, nonatomic, readonly) id propertyList API_AVAILABLE(ios(13.0));
    // 状态(效果见下图UIMenuElementAttributes)
    @property (nonatomic) UIMenuElementAttributes attributes API_AVAILABLE(ios(13.0));
    // 标题左边的状态图标(效果见下图UIMenuElementState)
    @property (nonatomic) UIMenuElementState state API_AVAILABLE(ios(13.0));
    

    Discoverability Title(例:discoverabilityTitle = @"Discoverability Title";)

    MacCatalyst - Discoverability Title.png

    UIMenuElementAttributes(UIMenuElementAttributesHidden变为不可见了):

    MacCatalyst - UIMenuElementAttributes.png

    UIMenuElementState:

    MacCatalyst - UIMenuElementState.png
    // 快捷方式使用不同的修饰键执行不同的方法,比如Command + 1执行A方法,Command + Shift + 1执行B方法
    @property (nonatomic, readonly) NSArray<UICommandAlternate *> *alternates API_AVAILABLE(ios(13.0));
    
    // 在上面的例子的基础上修改:
    
    // 添加"应用"菜单
    NSMutableArray *alternates = NSMutableArray.new;
    [alternates addObject:[UICommandAlternate alternateWithTitle:@"动态1"
                                                          action:@selector(newsMenuItemClick1:)
                                                   modifierFlags:UIKeyModifierShift]];
    [alternates addObject:[UICommandAlternate alternateWithTitle:@"动态2"
                                                          action:@selector(newsMenuItemClick2:)
                                                   modifierFlags:UIKeyModifierControl]];
    
    UIKeyCommand *newsItem =
    [UIKeyCommand commandWithTitle:@"动态"
                             image:nil
                            action:@selector(newsMenuItemClick:)
                             input:@"1"
                     modifierFlags:UIKeyModifierCommand
                      propertyList:nil
                        alternates:alternates];
    

    默认情况(快捷方式:Command + 1):

    MacCatalyst - Key Command Alternates Defalut.png

    按下Shift键(快捷方式:Command + Shift + 1):

    MacCatalyst - Key Command Alternates Shift.png

    按下Control键(快捷方式:Command + Control + 1):

    MacCatalyst - Key Command Alternates Control.png

    鼠标跟踪

    UIHoverGestureRecognizer * hover = [[UIHoverGestureRecognizer alloc]initWithTarget:self action:@selector(hoveringWithRecognizer:)];
    [View addGestureRecognizer:hover];
    
    - (void)hoveringWithRecognizer:(UIHoverGestureRecognizer *)recognizer
    {
        switch (recognizer.state) {
            case (UIGestureRecognizerStateBegan):
                NSLog(@"鼠标进入了区域");
                break;
            case (UIGestureRecognizerStateEnded):
                NSLog(@"鼠标离开了区域");
                break;
            default:
                break;
        }
    }
    

    窗口设置

    - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions API_AVAILABLE(ios(13.0)){
        
    #if TARGET_OS_MACCATALYST
        UIWindowScene *windowScene  = (UIWindowScene *)scene;
        // 需要乘以1.3才是实际显示在桌面上的大小
        // minimumSize不能少于515 × 370(需要乘以1.3)
        // 但能通过设置maximumSize无视不能小于515 × 370(需要乘以1.3)的限制
        windowScene.sizeRestrictions.minimumSize =
        windowScene.sizeRestrictions.maximumSize = CGSizeMake(200 * 1.3, 130 * 1.3);
        
        UITitlebar *titlebar        = windowScene.titlebar;
        // 不要显示窗口工具栏的标题
        titlebar.titleVisibility    = UITitlebarTitleVisibilityHidden;
        // 不要显示窗口顶部的工具栏,需隐藏窗口工具栏的标题才能生效
        titlebar.toolbar.visible    = NO;
    #endif
    }
    
    MacCatalyst - Toolbar Hidden.png

    工具栏

    需要先导入AppKit框架

    #if TARGET_OS_MACCATALYST
    #import <AppKit/AppKit.h>
    #endif
    

    遵循<NSToolbarDelegate>协议

    #if TARGET_OS_MACCATALYST
    @interface SceneDelegate () <NSToolbarDelegate>
    #else
    @interface SceneDelegate ()
    #endif
    

    然后在Scene Delegate里添加代码,例:

    - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions API_AVAILABLE(ios(13.0)) {
    
    #if TARGET_OS_MACCATALYST
        UITitlebar *titlebar        = self.window.windowScene.titlebar;
        NSToolbar *toolbar          = [[NSToolbar alloc] initWithIdentifier:@"DefaultToolbar"];
        titlebar.toolbar            = toolbar;
        toolbar.delegate            = self;
        toolbar.displayMode         = NSToolbarDisplayModeIconOnly;
    #endif
    }
    
    #if TARGET_OS_MACCATALYST
    -(NSArray<NSToolbarItemIdentifier> *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar{
        return @[@"ToolbarAddItem",@"ToolbarRemoveItem"];
    }
    
    -(NSArray<NSToolbarItemIdentifier> *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar{
        return @[@"ToolbarAddItem",@"ToolbarRemoveItem"];
    }
    
    - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag {
        
        // 根据item标识返回每个具体的NSToolbarItem对象实例(设置title或image后target需要重新设置,否则出现不能点击的问题)
        NSToolbarItem *toolbarItem  = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
        toolbarItem.bordered        = YES;
        
        if ([itemIdentifier isEqualToString:@"ToolbarAddItem"]) {
            toolbarItem.title       = @"添加";
            toolbarItem.target      = self;
            toolbarItem.action      = @selector(toolbarAddItemClicked:);
            
        } else if ([itemIdentifier isEqualToString:@"ToolbarRemoveItem"]) {
            toolbarItem.title       = @"移除";
            toolbarItem.target      = self;
            toolbarItem.action      = @selector(toolbarRemoveItemClicked:);
            
        } else {
            toolbarItem             = nil;
        }
        return toolbarItem;
    }
    
    - (void)toolbarAddItemClicked:(NSToolbarItem *)sender{
        NSLog(@"点击了 %@",sender.title);
    }
    
    - (void)toolbarRemoveItemClicked:(NSToolbarItem *)sender{
        NSLog(@"点击了 %@",sender.title);
    }
    #endif
    
    MacCatalyst - Toolbar.png

    右键菜单

    需要遵循<UIContextMenuInteractionDelegate>协议,且在视图对象初始化时调用-addInteraction:方法

    UIContextMenuInteraction *interaction = [[UIContextMenuInteraction alloc] initWithDelegate:self];
    [view addInteraction:interaction];
    

    然后实现此方法

    - (UIContextMenuConfiguration *)contextMenuInteraction:(UIContextMenuInteraction *)interaction configurationForMenuAtLocation:(CGPoint)location;
    

    例子:

    @interface ViewController ()<UITableViewDelegate,UITableViewDataSource,UIContextMenuInteractionDelegate>
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.tableview.delegate = self;
        self.tableview.dataSource = self;
    }
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return 10;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"1"];
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:0 reuseIdentifier:@"1"];
            // 视图对象初始化时调用
            if (@available(iOS 13.0, *)) {
                UIContextMenuInteraction *interaction = [[UIContextMenuInteraction alloc] initWithDelegate:self];
                [cell addInteraction:interaction];
            }
        }
        cell.textLabel.text = [NSString stringWithFormat:@"Row = %ld",indexPath.row];
        
        return cell;
    }
    
    - (UIContextMenuConfiguration *)contextMenuInteraction:(UIContextMenuInteraction *)interaction configurationForMenuAtLocation:(CGPoint)location API_AVAILABLE(ios(13.0)) 
    {
        return [UIContextMenuConfiguration configurationWithIdentifier:nil previewProvider:nil actionProvider:^UIMenu * _Nullable(NSArray<UIMenuElement *> * _Nonnull suggestedActions) {
            
            // 一般使用UIAction或UICommand对象
            NSMutableArray <UIMenuElement *>*children = NSMutableArray.new;
            
            [children addObject:[UIAction actionWithTitle:@"菜单项1" image:nil identifier:nil handler:^(__kindof UIAction * _Nonnull action) {
                NSLog(@"点击了菜单项1");
            }]];
            
            [children addObject:[UICommand commandWithTitle:@"菜单项2" image:nil action:@selector(menuItem2Clicked:) propertyList:nil]];
            
            return [UIMenu menuWithTitle:@"Menu" children:children];
        }];
    }
    
    - (void)menuItem2Clicked:(UICommand *)sender API_AVAILABLE(ios(13.0)) 
    {
        NSLog(@"点击了菜单项2");
    }
    
    @end
    
    MacCatalyst - Mouse Right Menu.png

    示例代码:https://github.com/LeungKinKeung/MacCatalyst

    如果需要使用更多AppKit的控件,请参阅《Mac Catalyst - macOS AppKit 插件》

    相关文章

      网友评论

        本文标题:Mac Catalyst - macOS 控件

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