You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

Share Extension plist配置

当前代码实现
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

  1. 扩展类型与配置调整
    不要使用SLComposeServiceViewController,直接继承UIViewController并隐藏UI。在Info.plist中配置正确的激活规则,仅接受目标文件类型:

    <key>NSExtensionActivationRule</key>
    <dict>
        <key>NSExtensionActivationSupportsImageWithMaxCount</key>
        <integer>1</integer>
        <key>NSExtensionActivationSupportsFileWithMaxCount</key>
        <integer>1</integer>
    </dict>
    

    同时确保NSExtensionPrincipalClass指向你的自定义ViewController类。

  2. 无UI逻辑处理
    在ViewController的ViewDidLoad中直接处理分享内容,完成后立即关闭扩展:

    public override async void ViewDidLoad()
    {
        base.ViewDidLoad();
        await ProcessSharedItems();
        // 完成扩展请求,无需返回结果
        await ExtensionContext.CompleteRequestAsync(new NSExtensionItem[0], null);
    }
    

二、App Group文件共享问题修复

  1. 配置验证
    确认主应用与扩展的Entitlements.plist中添加了完全相同的App Group ID(格式为group.com.yourcompany.yourapp),且Apple开发者后台的配置文件已启用该App Group,项目签名设置正确关联配置文件。

  2. 文件保存逻辑修正
    你的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}");
          }
      }
      
  3. 主应用读取逻辑
    主应用需使用相同路径获取文件:

    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);
        // 处理图片
    }
    

三、扩展调试问题解决

  1. 真机调试配置
    扩展无法在模拟器正常调试,需选择扩展项目作为启动项目,调试目标设为真机。启动调试后,在Files/Photos中分享图片到你的扩展,调试器会自动附加。

  2. 日志排查
    若调试器无法附加,使用Console.WriteLine打印关键步骤日志,通过XCode的「Devices and Simulators」查看扩展的日志输出。

四、替代方案:不使用Share Extension

若扩展实现成本过高,可通过以下两种方式直接获取文件:

  1. 文档选择器:在主应用中集成UIDocumentPickerViewController,让用户直接从Files/Photos选择文件;
  2. 文件类型关联:在主应用Info.plist中配置CFBundleDocumentTypes,添加图片和PDF类型的支持,其他应用分享时选择「复制到你的应用」,系统会直接打开主应用并传递文件URL。

内容的提问来源于stack exchange,提问作者vITs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 23:19:55