UIMenuController在macOS Monterey系统触发时崩溃的问题咨询
问题根因
这是macOS 12.x版本运行「设计为iPad」iOS应用时的系统原生bug:UIKitMacHelper框架在将UIMenuController的菜单项转换为 macOS 原生NSMenuItem时,部分系统自动生成的菜单项title属性为nil,向可变字典插入空值时触发了NSInvalidArgumentException异常。该问题在macOS 13及以上版本已经被官方修复,仅影响macOS 12.x系统。
可用规避方案
以下方案按实现成本从低到高排序:
- 方案1:禁用macOS环境下的右键触发逻辑
仅保留iOS原生的长按唤起菜单逻辑,拦截右键手势即可避免触发该bug:- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { if (@available(iOS 14.0, *)) { // 判断是否运行在Apple硅Mac的iOS应用环境 if ([NSProcessInfo processInfo].isiOSAppOnMac && NSProcessInfo.processInfo.operatingSystemVersion.majorVersion == 12) { // 拦截右键点击手势 if ([gestureRecognizer isKindOfClass:NSClassFromString(@"UIContextMenuInteractionClickPresentationGestureRecognizer")]) { return NO; } } } return [super gestureRecognizerShouldBegin:gestureRecognizer]; } - 方案2:完全自定义UIMenuController菜单项
不使用系统默认生成的复制、粘贴等菜单项,手动构造所有需要的菜单项,显式指定非空的title:UIMenuItem *copyItem = [[UIMenuItem alloc] initWithTitle:@"复制" action:@selector(customCopyAction:)]; UIMenuItem *pasteItem = [[UIMenuItem alloc] initWithTitle:@"粘贴" action:@selector(customPasteAction:)]; UIMenuController.sharedMenuController.menuItems = @[copyItem, pasteItem]; - 方案3:Runtime兜底修复
不想修改原有菜单逻辑的话,可以通过runtime交换方法,拦截空title的场景补默认值,从根源避免崩溃:#import <objc/runtime.h> @implementation MacOS12MenuFix + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if (@available(iOS 14.0, *)) { // 仅在macOS 12的iOS应用环境下生效 if (![NSProcessInfo processInfo].isiOSAppOnMac || NSProcessInfo.processInfo.operatingSystemVersion.majorVersion != 12) { return; } Class targetClass = NSClassFromString(@"_UIMenuBarItem"); SEL originalSel = NSSelectorFromString(@"properties"); SEL fixSel = @selector(fix_properties); Method originalMethod = class_getInstanceMethod(targetClass, originalSel); Method fixMethod = class_getInstanceMethod(self, fixSel); if (originalMethod && fixMethod) { method_exchangeImplementations(originalMethod, fixMethod); } } }); } - (NSDictionary *)fix_properties { NSDictionary *origin = [self fix_properties]; NSMutableDictionary *mutable = origin.mutableCopy; if (!mutable[@"title"]) { mutable[@"title"] = @""; } return mutable.copy; } @end
内容的提问来源于stack exchange,提问作者Vlad Grigorov
相关产品推荐
相关产品推荐

