Skip to content

Instantly share code, notes, and snippets.

@steipete
Last active September 2, 2022 15:09
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save steipete/b8820036dff1761ad6392e57f5901473 to your computer and use it in GitHub Desktop.
Save steipete/b8820036dff1761ad6392e57f5901473 to your computer and use it in GitHub Desktop.
Create menu in mac Catalyst via UIMenuBuilder (beta 4) - (void)buildMenuWithBuilder:(id<UIMenuBuilder>)builder;
// AppDelegate.swift:
// macCatalyst: Create menu
override func buildMenu(with builder: UIMenuBuilder) {
guard builder.system == .main else { return }
// The format menu doesn't make sense
builder.remove(menu: .format)
// Add Open command
let openKeyCommand = PSVMenuBuilder.keyCommand(withTitle: "Open...", image:nil, action: #selector(openFile), input: "o", modifierFlags: [UIKeyModifierFlags.command], propertyList: nil)
// This results in an infinite loop inside init
// FB6790921: Catalyst: Creating UIKeyCommand in Swift results in an endless loop
//let openKeyCommand = UIKeyCommand(title: "Open...", image:nil, action: #selector(openFile), input: "o", modifierFlags: [UIKeyModifierFlags.command], propertyList: nil)
let identifier = UIMenu.Identifier("open_file")
let menu = UIMenu.init(title: "Open", image: nil, identifier: identifier, options: [.displayInline], children: [openKeyCommand]);
builder.insertSibling(menu, afterMenu: .newScene)
}
// Put that into a separate file and add to your bridging headers. (ObjC)
#import "PSVMenuBuilder.h"
@implementation PSVMenuBuilder
// This is a simple wrapper to work around a bug:
// // FB6790921: Catalyst: Creating UIKeyCommand in Swift results in an endless loop
+ (UIKeyCommand *)keyCommandWithTitle:(NSString *)title
image:(nullable UIImage *)image
action:(SEL)action
input:(NSString *)input
modifierFlags:(UIKeyModifierFlags)modifierFlags
propertyList:(nullable id)propertyList {
return [UIKeyCommand commandWithTitle:title image:image action:action input:input modifierFlags:modifierFlags propertyList:propertyList];
}
// This is a simple wrapper to work around a bug:
// // FB6790921: Catalyst: Creating UIKeyCommand in Swift results in an endless loop
+ (UICommand *)commandWithTitle:(NSString *)title
image:(nullable UIImage *)image
action:(SEL)action
propertyList:(nullable id)propertyList {
return [UICommand commandWithTitle: title image:image action:action propertyList:propertyList];
}
@end
@implementation PSVMenuBuilder
+ (UIKeyCommand *)keyCommandWithTitle:(NSString *)title
image:(nullable UIImage *)image
action:(SEL)action
input:(NSString *)input
modifierFlags:(UIKeyModifierFlags)modifierFlags
propertyList:(nullable id)propertyList {
return [UIKeyCommand commandWithTitle:title image:image action:action input:input modifierFlags:modifierFlags propertyList:propertyList];
}
+ (UICommand *)commandWithTitle:(NSString *)title
image:(nullable UIImage *)image
action:(SEL)action
propertyList:(nullable id)propertyList {
return [UICommand commandWithTitle: title image:image action:action propertyList:propertyList];
}
@end
@steipete
Copy link
Author

FB6790921 - Catalyst: Creating UIKeyCommand in Swift results in an endless loop

Creating a UIKeyCommand in macCatalyst on b4 results in an infinite loop inside UIKeyCommand.init.
Workaround: Wrap creation function in ObjC.

@warrenburton
Copy link

Thanks for the fix

If you want , heres a couple more API that are still looping in B5 and a corrected .h

.h

@import UIKit;

NS_ASSUME_NONNULL_BEGIN

@interface PSVMenuBuilder : NSObject


// This is a simple wrapper to work around a bug:
// // FB6790921: Catalyst: Creating UIKeyCommand in Swift results in an endless loop
+ (UIKeyCommand *)keyCommandWithTitle:(NSString *)title
                                image:(nullable UIImage *)image
                               action:(SEL)action
                                input:(NSString *)input
                        modifierFlags:(UIKeyModifierFlags)modifierFlags
                         propertyList:(nullable id)propertyList;

// This is a simple wrapper to work around a bug:
// // FB6790921: Catalyst: Creating UIKeyCommand in Swift results in an endless loop
+ (UICommand *)commandWithTitle:(NSString *)title
                          image:(nullable UIImage *)image
                         action:(SEL)action
                   propertyList:(nullable id)propertyList;


+ (UIMenu *)menuWithTitle:(NSString *)title
                    image:(nullable UIImage *)image
               identifier:(nullable UIMenuIdentifier)identifier
                  options:(UIMenuOptions)options
                 children:(NSArray<UIMenuElement *> *)children;

+ (UIAction *)actionWithTitle:(NSString *)title
                        image:(nullable UIImage *)image
                   identifier:(nullable UIActionIdentifier)identifier
                      handler:(UIActionHandler)handler;

@end

NS_ASSUME_NONNULL_END

.m

#import "PSVMenuBuilder.h"


@implementation PSVMenuBuilder

+ (UIKeyCommand *)keyCommandWithTitle:(NSString *)title
                                image:(nullable UIImage *)image
                               action:(SEL)action
                                input:(NSString *)input
                        modifierFlags:(UIKeyModifierFlags)modifierFlags
                         propertyList:(nullable id)propertyList {
  return [UIKeyCommand commandWithTitle:title image:image action:action input:input modifierFlags:modifierFlags propertyList:propertyList];
}

+ (UICommand *)commandWithTitle:(NSString *)title
                          image:(nullable UIImage *)image
                         action:(SEL)action
                   propertyList:(nullable id)propertyList {
  return [UICommand commandWithTitle: title image:image action:action propertyList:propertyList];
}


+ (UIMenu *)menuWithTitle:(NSString *)title
                    image:(nullable UIImage *)image
               identifier:(nullable UIMenuIdentifier)identifier
                  options:(UIMenuOptions)options
                 children:(NSArray<UIMenuElement *> *)children {
  return [UIMenu menuWithTitle:title image:image identifier:identifier options:options children:children];
}

+ (UIAction *)actionWithTitle:(NSString *)title
                        image:(nullable UIImage *)image
                   identifier:(nullable UIActionIdentifier)identifier
                      handler:(UIActionHandler)handler {
  return [UIAction actionWithTitle:title image:image identifier:identifier handler:handler];
}

@end

@steipete
Copy link
Author

steipete commented Jul 31, 2019 via email

@bmueller
Copy link

bmueller commented Aug 2, 2019

Have you figured out how to add separators yet?

@steipete
Copy link
Author

steipete commented Aug 2, 2019

This has been fixed in b5. Haven't needed separators yet.

@bmueller
Copy link

bmueller commented Aug 2, 2019

Figured it out in case anyone is curious: you have to make the UIKeyCommands you want to put in a section children of a UIMenu, and use the UIMenuOptionsDisplayInline option for the menu.

@drosenstark
Copy link

@bmueller first I was like "I already did that and it didn't work" but then I was like, "this must be right, try harder" and the answer was... you're right. Some confusing things are that the IDs have to be unique otherwise the menus collapse, you must use insertSibling at some point, and... not sure.

THANKS for the comment here, it worked great for me to get separators in menus.

THANKS @steipete for the code, it's nice. I'm doing it all in Swift now (2022) so I think that's all sorted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment