如何实现让自定义分享操作显示在iOS其他应用的分享面板中?
我完全懂你的需求——就像ILovePDF、Chrome那样,让自己App的自定义操作(比如“用我的App打开”“用我的App编辑”)出现在其他App的iOS分享面板第三排对吧?网上大多教程都是教你给自己的App加分享操作,确实容易搞混,我来一步步给你理清楚实现方法:
一、实现“Open in 你的App”这类跳转操作
这种是让用户把其他App里的内容直接导入到你的App中,核心是告诉iOS系统你的App能处理哪些类型的内容:
在主App的Info.plist中声明支持的文件类型
你需要在Info.plist里添加CFBundleDocumentTypes,指定你的App能处理的UTI(统一类型标识符,比如PDF、图片、文本等)。
右键点击Info.plist选择「Open As -> Source Code」,添加类似以下代码:<key>CFBundleDocumentTypes</key> <array> <dict> <key>CFBundleTypeName</key> <string>PDF文档</string> <key>CFBundleTypeRole</key> <string>Viewer</string> <!-- 这里填Viewer/Editor,根据你的App功能选择 --> <key>LSHandlerRank</key> <string>Alternate</string> <!-- 设为Alternate会让App出现在自定义操作区,Default是默认处理者 --> <key>LSItemContentTypes</key> <array> <string>public.pdf</string> <!-- 支持PDF,你可以添加更多UTI,比如public.image、public.text --> </array> </dict> </array>处理App间的数据传递
当用户在其他App选择“Open in 你的App”时,iOS会把文件传递过来,你需要在SceneDelegate(或AppDelegate,取决于你的项目架构)里处理这个请求:func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { guard let sourceURL = URLContexts.first?.url else { return } // 把传入的文件复制到自己App的沙盒目录,避免原文件被删除 let docsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let destinationURL = docsDir.appendingPathComponent(sourceURL.lastPathComponent) do { try FileManager.default.copyItem(at: sourceURL, to: destinationURL) // 这里跳转到你的App对应的页面展示文件即可 } catch { print("复制文件失败: \(error.localizedDescription)") } }
二、实现“Edit with 你的App”这类编辑操作
这种是用户在其他App里调用你的App处理内容,处理完成后还能返回原App,需要用到Action Extension:
给项目添加Action Extension
在Xcode中选择「File -> New -> Target」,找到「Action Extension」,输入名称(比如EditWithMyApp),点击Finish完成创建。配置Extension的Info.plist
打开Extension的Info.plist,找到NSExtension字典,配置以下关键项:<key>NSExtension</key> <dict> <key>NSExtensionAttributes</key> <dict> <key>NSExtensionActivationRule</key> <dict> <!-- 配置支持的内容类型,这里以PDF为例,可添加更多 --> <key>NSExtensionActivationSupportsPDFWithMaxCount</key> <integer>1</integer> <key>NSExtensionActivationSupportsImageWithMaxCount</key> <integer>1</integer> </dict> </dict> <key>NSExtensionPrincipalClass</key> <string>$(PRODUCT_MODULE_NAME).ActionViewController</string> <!-- 处理逻辑的ViewController --> <key>NSExtensionPointIdentifier</key> <string>com.apple.ui-services</string> </dict>实现Extension的处理逻辑
在自动生成的ActionViewController中,编写内容处理代码,处理完成后返回结果给原App:override func viewDidLoad() { super.viewDidLoad() // 获取其他App传入的内容 guard let inputItems = extensionContext?.inputItems as? [NSExtensionItem] else { return } for item in inputItems { guard let attachments = item.attachments else { continue } for provider in attachments { if provider.hasItemConformingToTypeIdentifier("public.pdf") { provider.loadItem(forTypeIdentifier: "public.pdf", options: nil) { [weak self] data, error in DispatchQueue.main.async { guard let self = self, let fileURL = data as? URL else { return } // 这里编写你的PDF编辑逻辑,比如修改内容后生成新的文件URL let editedFileURL = self.processPDF(fileURL) // 将处理后的结果返回给原App let outputItem = NSExtensionItem() let outputProvider = NSItemProvider(contentsOf: editedFileURL)! outputItem.attachments = [outputProvider] self.extensionContext?.completeRequest(returningItems: [outputItem], completionHandler: nil) } } } } } } // 示例:模拟PDF处理方法 private func processPDF(_ url: URL) -> URL { // 这里替换成你的真实处理逻辑 return url }
测试注意事项
- 要把主App和Extension一起安装到设备/模拟器上,单独安装Extension是不行的;
- 如果你的操作没有出现在分享面板,检查UTI配置是否和测试的文件类型匹配,
NSExtensionActivationRule是否正确; - 对于Extension,记得在Capabilities里开启App Groups(如果需要和主App共享数据的话)。
备注:内容来源于stack exchange,提问作者prashant dixit




