.NET iOS(原Xamarin.iOS)如何访问Files/Photos分享的图片?
开发.NET iOS(原Xamarin.iOS)应用,需要获取从Files或Photos应用分享的图片。尝试实现无UI的Share Extension未达预期效果,且.NET iOS扩展项目调试器无法正常工作。当前扩展无法将文件保存到App Group,主应用也无法读取该文件,求可行的实现方法及问题解决思路。
- 通过XCode创建分享扩展项目
- 项目生成所有必要文件夹/文件
- 将主类从SLCompose改为UIViewController
- 重写方法,使用NSContext加载图片
- 创建新配置文件和App Group

void LoadImage (NSExtensionContext extensionContext = null) { var image = new NSExtensionItem [0]; NSExtensionItem inputItem; if (extensionContext == null) { inputItem = ExtensionContext.InputItems [0]; } else { inputItem = extensionContext.InputItems [0]; } var outputItem = inputItem.Copy () as NSExtensionItem; var itemProvider = outputItem.Attachments [0]; string imageTypeIdentifier = "public.image"; string pdfTypeIdentifier = "com.adobe.pdf"; var test = itemProvider.GetRegisteredTypeIdentifiers (NSItemProviderFileOptions.OpenInPlace); if (!(itemProvider.HasItemConformingTo (imageTypeIdentifier)) || itemProvider.HasItemConformingTo (pdfTypeIdentifier)) { OpenUrl (NSUrl.FromString ($"myapp://itemtypeimagedoesnotexists)")); return; } itemProvider.LoadItem (imageTypeIdentifier, null, (item, loadError) => { if (item != null && loadError == null) { InvokeOnMainThread (() => { OpenUrl (NSUrl.FromString ($"myapp://{item.ToString ()}")); if (item is UIImage) { SaveImageToAppGroup (item as UIImage, "shared_image_url.txt"); } }); } else { InvokeOnMainThread (() => { OpenUrl (NSUrl.FromString ($"myapp://itemisnullorhasloaderror")); }); } }); itemProvider.LoadItem (pdfTypeIdentifier, null, (item, loadError) => { if (item != null && loadError == null) { InvokeOnMainThread (() => { OpenUrl (NSUrl.FromString ($"myapp://{item.ToString ()}")); if (item is NSData) { SavePdfToAppGroup (item as NSData, "shared_image_pdf.txt"); } }); } else { InvokeOnMainThread (() => { OpenUrl (NSUrl.FromString ($"myapp://itemisnullorhasloaderror")); }); } }); } public void SaveImageToAppGroup (UIImage image, string fileName) { var appGroupPath = NSFileManager.DefaultManager.GetContainerUrl (APP_GROUP_IDENTIFIER); var directoryPath = appGroupPath.Path; if (!NSFileManager.DefaultManager.FileExists (directoryPath)) { NSError createDirError = null; NSFileManager.DefaultManager.CreateDirectory (appGroupPath, true, null, out createDirError); } NSData imgData = image.AsJPEG (); var filePath = Path.Combine (directoryPath, fileName); NSError saveError = null; imgData.Save (filePath, NSDataWritingOptions.Atomic, out saveError); }
一、正确实现无UI Share Extension
扩展类型与配置调整
不要使用SLComposeServiceViewController,直接继承UIViewController并隐藏UI。在Info.plist中配置正确的激活规则,仅接受目标文件类型:<key>NSExtensionActivationRule</key> <dict> <key>NSExtensionActivationSupportsImageWithMaxCount</key> <integer>1</integer> <key>NSExtensionActivationSupportsFileWithMaxCount</key> <integer>1</integer> </dict>同时确保
NSExtensionPrincipalClass指向你的自定义ViewController类。无UI逻辑处理
在ViewController的ViewDidLoad中直接处理分享内容,完成后立即关闭扩展:public override async void ViewDidLoad() { base.ViewDidLoad(); await ProcessSharedItems(); // 完成扩展请求,无需返回结果 await ExtensionContext.CompleteRequestAsync(new NSExtensionItem[0], null); }
二、App Group文件共享问题修复
配置验证
确认主应用与扩展的Entitlements.plist中添加了完全相同的App Group ID(格式为group.com.yourcompany.yourapp),且Apple开发者后台的配置文件已启用该App Group,项目签名设置正确关联配置文件。文件保存逻辑修正
你的SaveImageToAppGroup方法存在两处问题:- 保存图片使用
.txt后缀不符合文件类型规范,应改为.jpg; - 未处理保存错误,添加日志排查:
public void SaveImageToAppGroup (UIImage image, string fileName) { var appGroupUrl = NSFileManager.DefaultManager.GetContainerUrl(APP_GROUP_IDENTIFIER); if (appGroupUrl == null) { Console.WriteLine("App Group容器获取失败,请检查配置"); return; } var directoryPath = appGroupUrl.Path; if (!NSFileManager.DefaultManager.FileExists(directoryPath)) { NSError createDirError = null; NSFileManager.DefaultManager.CreateDirectory(appGroupUrl, true, null, out createDirError); if (createDirError != null) { Console.WriteLine($"创建目录错误: {createDirError.LocalizedDescription}"); return; } } NSData imgData = image.AsJPEG(); var filePath = Path.Combine(directoryPath, fileName); NSError saveError = null; imgData.Save(filePath, NSDataWritingOptions.Atomic, out saveError); if (saveError != null) { Console.WriteLine($"保存图片错误: {saveError.LocalizedDescription}"); } }
- 保存图片使用
主应用读取逻辑
主应用需使用相同路径获取文件:var appGroupUrl = NSFileManager.DefaultManager.GetContainerUrl("group.com.yourcompany.yourapp"); var imagePath = Path.Combine(appGroupUrl.Path, "shared_image.jpg"); if (NSFileManager.DefaultManager.FileExists(imagePath)) { var imageData = NSData.FromFile(imagePath); var image = UIImage.LoadFromData(imageData); // 处理图片 }
三、扩展调试问题解决
真机调试配置
扩展无法在模拟器正常调试,需选择扩展项目作为启动项目,调试目标设为真机。启动调试后,在Files/Photos中分享图片到你的扩展,调试器会自动附加。日志排查
若调试器无法附加,使用Console.WriteLine打印关键步骤日志,通过XCode的「Devices and Simulators」查看扩展的日志输出。
四、替代方案:不使用Share Extension
若扩展实现成本过高,可通过以下两种方式直接获取文件:
- 文档选择器:在主应用中集成
UIDocumentPickerViewController,让用户直接从Files/Photos选择文件; - 文件类型关联:在主应用Info.plist中配置
CFBundleDocumentTypes,添加图片和PDF类型的支持,其他应用分享时选择「复制到你的应用」,系统会直接打开主应用并传递文件URL。
内容的提问来源于stack exchange,提问作者vITs

