【问题标题】:Xamarin IOS file picker : unable to get selected file from icloud driveXamarin IOS 文件选择器:无法从 icloud 驱动器获取选定的文件
【发布时间】:2018-04-07 08:57:46
【问题描述】:

我已经从 Nuget 安装了文件选择器控件,并添加了尝试从 MonoTouch10 文件夹添加引用,后来从 github 到我的 xamarin.ios 项目。

FileData file = await CrossFilePicker.Current.PickFile();

if (file != null) { }

这是我添加到浏览按钮的代码,从 iCloud 驱动器中选择文件后,控制永远不会进入“如果条件”。

当我第二次点击浏览按钮时,应用程序崩溃说“一次只能激活一个操作”。

【问题讨论】:

标签: xamarin.ios xamarin.forms icloud-drive icloud-documents


【解决方案1】:

为了回答我自己的问题,我在插件中自定义了,比如

  1. 使用DocumentPicker_DidPickDocumentAtUrls 事件而不是DocumentPicker_DidPickDocument

  2. 在返回使用的选定文件时

     new FileData(e.FilePath, e.FileName, () =>
            {
                var url = new Foundation.NSUrl(e.FilePath);
                return new FileStream(url.Path, FileMode.Open, FileAccess.Read);
            })
    

这解决了我的问题。谢谢。

【讨论】:

  • 我找到了你的答案,并尝试实现插件的修改离线解决方案。在所有情况下,我都得到: System.NotImplementedException:此功能未在此程序集的可移植版本中实现。有关如何使其适用于需要将存储库中的哪些项目添加到我的 Xamarin 解决方案和究竟需要引用什么?
【解决方案2】:

FilePicker Xamarin 插件有几个分支。我推荐以下项目,因为它是维护最积极的项目: https://github.com/jfversluis/FilePicker-Plugin-for-Xamarin-and-Windows(注意:我是该项目的贡献者之一)。

使用这个版本的插件文件选择应该可以工作。来自sandeep 答案的示例代码已合并到最新版本的插件中。如果遇到问题,请务必阅读 README.md 的故障排除页面。

【讨论】:

    【解决方案3】:

    修改iOS平台的FilePickerImplementation插件的源代码是这样的:

    using Foundation;
    using MobileCoreServices;
    using Plugin.FilePicker.Abstractions;
    using System;
    using System.IO;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using UIKit;
    using System.Diagnostics;
    
    namespace Plugin.FilePicker
    {
        /// <summary>
        /// Implementation for FilePicker
        /// </summary>
        public class FilePickerImplementation : NSObject, IUIDocumentMenuDelegate, IFilePicker
        {
            private int _requestId;
            private TaskCompletionSource<FileData> _completionSource;
    
            /// <summary>
            /// Event which is invoked when a file was picked
            /// </summary>
            public EventHandler<FilePickerEventArgs> Handler
            {
                get;
                set;
            }
    
            private void OnFilePicked(FilePickerEventArgs e)
            {
                Handler?.Invoke(null, e);
            }
    
            public void DidPickDocumentPicker(UIDocumentMenuViewController documentMenu, UIDocumentPickerViewController documentPicker)
            {
                documentPicker.DidPickDocument += DocumentPicker_DidPickDocument;
                documentPicker.WasCancelled += DocumentPicker_WasCancelled;
                documentPicker.DidPickDocumentAtUrls += DocumentPicker_DidPickDocumentAtUrls;
                UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(documentPicker, true, null);
            }
    
            private void DocumentPicker_DidPickDocumentAtUrls(object sender, UIDocumentPickedAtUrlsEventArgs e)
            {
                var control = (UIDocumentPickerViewController)sender;
                foreach (var url in e.Urls)
                    DocumentPicker_DidPickDocument(control, new UIDocumentPickedEventArgs(url));
    
                control.Dispose();
            }
    
            private void DocumentPicker_DidPickDocument(object sender, UIDocumentPickedEventArgs e)
            {
                var securityEnabled = e.Url.StartAccessingSecurityScopedResource();
                var doc = new UIDocument(e.Url);
                var data = NSData.FromUrl(e.Url);
                var dataBytes = new byte[data.Length];
    
                System.Runtime.InteropServices.Marshal.Copy(data.Bytes, dataBytes, 0, Convert.ToInt32(data.Length));
    
                string filename = doc.LocalizedName;
                string pathname = doc.FileUrl?.ToString();
    
                // iCloud drive can return null for LocalizedName.
                if (filename == null)
                {
                    // Retrieve actual filename by taking the last entry after / in FileURL.
                    // e.g. /path/to/file.ext -> file.ext
    
                    // filesplit is either:
                    // 0 (pathname is null, or last / is at position 0)
                    // -1 (no / in pathname)
                    // positive int (last occurence of / in string)
                    var filesplit = pathname?.LastIndexOf('/') ?? 0;
    
                    filename = pathname?.Substring(filesplit + 1);
                }
    
                OnFilePicked(new FilePickerEventArgs(dataBytes, filename, pathname));
            }
    
            /// <summary>
            /// Handles when the file picker was cancelled. Either in the
            /// popup menu or later on.
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            public void DocumentPicker_WasCancelled(object sender, EventArgs e)
            {
                {
                    var tcs = Interlocked.Exchange(ref _completionSource, null);
                    tcs.SetResult(null);
                }
            }
    
            /// <summary>
            /// Lets the user pick a file with the systems default file picker
            /// For iOS iCloud drive needs to be configured
            /// </summary>
            /// <returns></returns>
            public async Task<FileData> PickFile()
            {
                var media = await TakeMediaAsync();
    
                return media;
            }
    
            private Task<FileData> TakeMediaAsync()
            {
                var id = GetRequestId();
    
                var ntcs = new TaskCompletionSource<FileData>(id);
    
                if (Interlocked.CompareExchange(ref _completionSource, ntcs, null) != null)
                    throw new InvalidOperationException("Only one operation can be active at a time");
    
                var allowedUtis = new string[] {
                    UTType.UTF8PlainText,
                    UTType.PlainText,
                    UTType.RTF,
                    UTType.PNG,
                    UTType.Text,
                    UTType.PDF,
                    UTType.Image,
                    UTType.UTF16PlainText,
                    UTType.FileURL
                };
    
                var importMenu =
                    new UIDocumentMenuViewController(allowedUtis, UIDocumentPickerMode.Import)
                    {
                        Delegate = this,
                        ModalPresentationStyle = UIModalPresentationStyle.Popover
                    };
    
                UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(importMenu, true, null);
    
                var presPopover = importMenu.PopoverPresentationController;
    
                if (presPopover != null)
                {
                    presPopover.SourceView = UIApplication.SharedApplication.KeyWindow.RootViewController.View;
                    presPopover.PermittedArrowDirections = UIPopoverArrowDirection.Down;
                }
    
                Handler = null;
    
                Handler = (s, e) => {
                    var tcs = Interlocked.Exchange(ref _completionSource, null);
    
                    tcs?.SetResult(new FileData(e.FilePath, e.FileName, () => { var url = new Foundation.NSUrl(e.FilePath); return new FileStream(url.Path, FileMode.Open, FileAccess.Read); }));
                };
    
                return _completionSource.Task;
            }
    
            public void WasCancelled(UIDocumentMenuViewController documentMenu)
            {
                var tcs = Interlocked.Exchange(ref _completionSource, null);
    
                tcs?.SetResult(null);
            }
    
            private int GetRequestId()
            {
                var id = _requestId;
    
                if (_requestId == int.MaxValue)
                    _requestId = 0;
                else
                    _requestId++;
    
                return id;
            }
    
            public async Task<bool> SaveFile(FileData fileToSave)
            {
                try
                {
                    var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                    var fileName = Path.Combine(documents, fileToSave.FileName);
    
                    File.WriteAllBytes(fileName, fileToSave.DataArray);
    
                    return true;
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                    return false;
                }
            }
    
            public void OpenFile(NSUrl fileUrl)
            {
                var docControl = UIDocumentInteractionController.FromUrl(fileUrl);
    
                var window = UIApplication.SharedApplication.KeyWindow;
                var subViews = window.Subviews;
                var lastView = subViews.Last();
                var frame = lastView.Frame;
    
                docControl.PresentOpenInMenu(frame, lastView, true);
            }
    
            public void OpenFile(string fileToOpen)
            {
                var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
    
                var fileName = Path.Combine(documents, fileToOpen);
    
                if (NSFileManager.DefaultManager.FileExists(fileName))
                {
                    var url = new NSUrl(fileName, true);
                    OpenFile(url);
                }
            }
    
            public async void OpenFile(FileData fileToOpen)
            {
                var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
    
                var fileName = Path.Combine(documents, fileToOpen.FileName);
    
                if (NSFileManager.DefaultManager.FileExists(fileName))
                {
                    var url = new NSUrl(fileName, true);
    
                    OpenFile(url);
                }
                else
                {
                    await SaveFile(fileToOpen);
                    OpenFile(fileToOpen);
                }
            }
        }
    }
    

    【讨论】:

    • 如何使它与 iOS 的 Xamarin 表单项目一起工作? FilePicker 插件中的哪些项目需要在本地添加,哪些项目需要引用到 Xamarin.iOS 项目中?在所有情况下我都会收到此错误:{System.NotImplementedException:此程序集的可移植版本中未实现此功能。您应该从您的主应用程序项目中引用 NuGet 包,以便引用特定于平台的实现。
    猜你喜欢
    • 1970-01-01
    • 2021-04-22
    • 1970-01-01
    • 2021-07-09
    • 1970-01-01
    • 1970-01-01
    • 2015-01-08
    • 2019-05-01
    • 2018-03-17
    相关资源
    最近更新 更多