C# Xamarin.iOS UIDocumentBrowser无法在第三方容器中创建文件
我一直在尝试使用UIDocumentBrowserViewController在Xamarin的iOS上实现一个“保存文件”对话框。在手机和iCloud中创建和选择文件时,一切都很有效。但在创建文档时,它在OneDrive中以静默方式失败(在您尝试在其他地方访问它之前,full-on会假装工作),并在Google Drive中显示一条错误消息(“操作无法完成。(com.apple.DocumentManager error 1.)”) 从我的阅读中可以看出,如果它在iCloud中工作,那么它应该在一个驱动器和Google驱动器中工作,但我似乎不知道我可能缺少什么。我已经在下面发布了代码;它松散地基于Xam.Filepicker nugetC# Xamarin.iOS UIDocumentBrowser无法在第三方容器中创建文件,c#,ios,swift,xamarin,xamarin.ios,C#,Ios,Swift,Xamarin,Xamarin.ios,我一直在尝试使用UIDocumentBrowserViewController在Xamarin的iOS上实现一个“保存文件”对话框。在手机和iCloud中创建和选择文件时,一切都很有效。但在创建文档时,它在OneDrive中以静默方式失败(在您尝试在其他地方访问它之前,full-on会假装工作),并在Google Drive中显示一条错误消息(“操作无法完成。(com.apple.DocumentManager error 1.)”) 从我的阅读中可以看出,如果它在iCloud中工作,那么它应该
public class SaveDocumentBrowser : UIDocumentBrowserViewControllerDelegate
{
//Request ID for current picking call
private int requestId;
//Task returned when all is completed
private TaskCompletionSource<FilePlaceholder> completionSource;
//Event which is invoked when a file was picked. Used elsewhere
internal EventHandler<FilePlaceholder> Handler { get; set; }
//Extensions used for choosing file names
private string[] _extensions;
//Called when a file has been chosen or created
private async void OnFilePicked(NSUrl destination, bool creation = false)
{
if (destination == null || !destination.IsFileUrl)
{
this.Handler?.Invoke(this, null);
return;
}
var document = new GenericDocument(destination);
var success = await document.OpenAsync();
if (!success)
{
this.Handler?.Invoke(this, null);
return;
}
async Task StreamSetter(Stream stream, FilePlaceholder placeholder)
{
document.DataStream = stream;
try
{
if (!await document.SaveAsync(destination, creation ? UIDocumentSaveOperation.ForCreating : UIDocumentSaveOperation.ForOverwriting))
{
throw new Exception("Failed to Save Document.");
}
}
finally
{
await document.CloseAsync();
}
}
var placeHolder = new FilePlaceholder(destination.AbsoluteString, destination.LastPathComponent, StreamSetter, b => document.Dispose());
this.Handler?.Invoke(null, placeHolder);
}
//Delegate for when user requests document creation
public override void DidRequestDocumentCreation(UIDocumentBrowserViewController controller, Action<NSUrl, UIDocumentBrowserImportMode> importHandler)
{
//this is a custom view for choosing a name for the new file
var editController = new FileNameInputViewController(_extensions);
void OnEditControllerOnOnViewDidDisappear(object sender, EventArgs args)
{
editController.OnViewDidDisappear -= OnEditControllerOnOnViewDidDisappear;
if (string.IsNullOrEmpty(editController.FileName))
{
importHandler(null, UIDocumentBrowserImportMode.None);
return;
}
try
{
var documentFolder = Path.GetTempPath();
var tempFileName = editController.FileName;
var path = Path.Combine(documentFolder, tempFileName);
var tempFile = File.Create(path);
tempFile.Dispose();
var url = NSUrl.CreateFileUrl(path, false, null);
importHandler(url, UIDocumentBrowserImportMode.Move);
}
catch(Exception e)
{
Debug.WriteLine("Failed to create temp doc: " + e);
var dialog = UIAlertController.Create("Error", "Error creating temp file: " + e.Message, UIAlertControllerStyle.Alert);
dialog.AddAction(UIAlertAction.Create("Done", UIAlertActionStyle.Cancel, null));
controller.PresentViewController(dialog, false, null);
importHandler(null, UIDocumentBrowserImportMode.None);
}
}
editController.OnViewDidDisappear += OnEditControllerOnOnViewDidDisappear;
controller.PresentViewController(editController, true, null);
}
//Delegate for when user picks file
public override void DidPickDocumentUrls(UIDocumentBrowserViewController controller, NSUrl[] documentUrls)
{
var dialog = UIAlertController.Create("Overwriting file", "Are you sure you want to overwrite this file?", UIAlertControllerStyle.Alert);
dialog.AddAction(UIAlertAction.Create("Yes", UIAlertActionStyle.Default, action => OnFilePicked(documentUrls[0])));
dialog.AddAction(UIAlertAction.Create("No", UIAlertActionStyle.Cancel, null));
controller.PresentViewController(dialog, false, null);
}
//Delegate for when user picks files (not used at this time)
public override void DidPickDocumentsAtUrls(UIDocumentBrowserViewController controller, NSUrl[] documentUrls)
{
var dialog = UIAlertController.Create("Overwriting file", "Are you sure you want to overwrite this file?", UIAlertControllerStyle.Alert);
dialog.AddAction(UIAlertAction.Create("Yes", UIAlertActionStyle.Default, action => OnFilePicked(documentUrls[0])));
dialog.AddAction(UIAlertAction.Create("No", UIAlertActionStyle.Cancel, null));
controller.PresentViewController(dialog, false, null);
}
//Delegate for when created document successfully import
public override void DidImportDocument(UIDocumentBrowserViewController controller, NSUrl sourceUrl, NSUrl destinationUrl)
{
OnFilePicked(destinationUrl);
}
//Delegate for when created document fails import
public override void FailedToImportDocument(UIDocumentBrowserViewController controller, NSUrl documentUrl, NSError error)
{
Debug.WriteLine("Failed to import doc: " + error);
var dialog = UIAlertController.Create("Error", "Error creating file: " + error, UIAlertControllerStyle.Alert);
dialog.AddAction(UIAlertAction.Create("Done", UIAlertActionStyle.Cancel, null));
controller.PresentViewController(dialog, false, null);
}
/// <summary>
/// File picking implementation
/// </summary>
/// <param name="allowedTypes">list of allowed types; may be null</param>
/// <returns>picked file data, or null when picking was cancelled</returns>
public Task<FilePlaceholder> PickMediaAsync(string[] allowedTypes)
{
var id = this.GetRequestId();
var ntcs = new TaskCompletionSource<FilePlaceholder>(id);
if (Interlocked.CompareExchange(ref this.completionSource, ntcs, null) != null)
{
throw new InvalidOperationException("Only one operation can be active at a time");
}
var allowedUtis = new string[]
{
UTType.Content,
UTType.Item,
"public.data"
};
if (allowedTypes != null)
{
allowedUtis = allowedTypes.Where(x => x[0] != '.').ToArray();
_extensions = allowedTypes.Where(x => x[0] == '.').ToArray();
}
else
{
_extensions = null;
}
//This is only custom so we can hook onto the dismissal event
var documentBrowser = new CustomDocumentBrowserViewController(allowedUtis)
{
AllowsDocumentCreation = true,
AllowsPickingMultipleItems = false,
Delegate = this,
};
void OnDocumentBrowserOnOnViewDidDisappear(object sender, EventArgs args)
{
OnFilePicked(null);
}
documentBrowser.OnViewDidDisappear += OnDocumentBrowserOnOnViewDidDisappear;
UIViewController viewController = GetActiveViewController();
viewController.PresentViewController(documentBrowser, false, null);
this.Handler = (sender, args) =>
{
documentBrowser.OnViewDidDisappear -= OnDocumentBrowserOnOnViewDidDisappear;
documentBrowser.DismissViewController(false, null);
var tcs = Interlocked.Exchange(ref this.completionSource, null);
tcs?.SetResult(args);
};
return this.completionSource.Task;
}
//Get current view controller for presentation
private static UIViewController GetActiveViewController()
{
UIWindow window = UIApplication.SharedApplication.KeyWindow;
UIViewController viewController = window.RootViewController;
while (viewController.PresentedViewController != null)
{
viewController = viewController.PresentedViewController;
}
return viewController;
}
//increment to a new id for Task completion sources
private int GetRequestId()
{
var id = this.requestId;
if (this.requestId == int.MaxValue)
{
this.requestId = 0;
}
else
{
this.requestId++;
}
return id;
}
//custom inheritance solely so we can see if user dismisses the view
private class CustomDocumentBrowserViewController : UIDocumentBrowserViewController
{
public event EventHandler OnViewDidDisappear;
public override void ViewDidDisappear(bool animated)
{
base.ViewDidDisappear(animated);
OnViewDidDisappear?.Invoke(this, null);
}
public CustomDocumentBrowserViewController(string[] contentTypes) : base(contentTypes)
{
}
}
}
public类SaveDocumentBrowser:UIDocumentBrowser服务控制器delegate
{
//当前分拣呼叫的请求ID
私有int请求id;
//全部完成后返回的任务
私有TaskCompletionSource completionSource;
//事件,该事件在拾取文件时调用。在别处使用
内部事件处理程序{get;set;}
//用于选择文件名的扩展名
私有字符串[]_扩展名;
//在选择或创建文件时调用
私有异步无效OnFilePicked(NSUrl目标,bool创建=false)
{
if(destination==null | |!destination.IsFileUrl)
{
this.Handler?.Invoke(this,null);
返回;
}
var文件=新的通用文件(目的地);
var success=wait document.OpenAsync();
如果(!成功)
{
this.Handler?.Invoke(this,null);
返回;
}
异步任务流设置器(流、文件占位符占位符)
{
document.DataStream=流;
尝试
{
如果(!await document.SaveAsync(目标,创建?UIDocumentSaveOperation.ForCreating:UIDocumentSaveOperation.ForOverwrite))
{
抛出新异常(“保存文档失败”);
}
}
最后
{
等待document.CloseAsync();
}
}
var placeHolder=newfileplaceholder(destination.AbsoluteString、destination.LastPathComponent、StreamSetter、b=>document.Dispose());
this.Handler?.Invoke(null,占位符);
}
//当用户请求创建文档时的委托
public override void DidRequestDocumentCreation(UIDocumentBrowserViewController控制器,操作导入器)
{
//这是为新文件选择名称的自定义视图
var editController=新文件名InputViewController(_扩展名);
void OneDitControllerOnViewDidEnglish(对象发送者,事件参数)
{
editController.OnViewDidEnglish-=OneDitControllerOnViewDidEnglishe;
if(string.IsNullOrEmpty(editController.FileName))
{
importHandler(null,UIDocumentBrowserImportMode.None);
返回;
}
尝试
{
var documentFolder=Path.GetTempPath();
var tempFileName=editController.FileName;
var path=path.Combine(documentFolder,tempFileName);
var tempFile=File.Create(路径);
tempFile.Dispose();
var url=NSUrl.CreateFileUrl(路径,false,null);
导入器(url,UIDocumentBrowserImportMode.Move);
}
捕获(例外e)
{
Debug.WriteLine(“未能创建临时文档:+e”);
var dialog=UIAlertController.Create(“错误”,“创建临时文件时出错:”+e.Message,UIAlertControllerStyle.Alert);
AddAction(UIAlertAction.Create(“完成”,UIAlertActionStyle.Cancel,null));
controller.PresentViewController(对话框,false,null);
importHandler(null,UIDocumentBrowserImportMode.None);
}
}
editController.OnViewDidDesile+=OneDitControllerOnViewDidDesile;
controller.PresentViewController(editController,true,null);
}
//当用户选择文件时的委托
public override void DidPickDocumentUrls(UIDocumentBrowserViewController控制器,NSUrl[]documentUrls)
{
var dialog=UIAlertController.Create(“覆盖文件”、“是否确实要覆盖此文件?”,UIAlertControllerStyle.Alert);
AddAction(UIAlertAction.Create(“是”,UIAlertActionStyle.Default,action=>OnFilePicked(documentUrls[0]));
AddAction(UIAlertAction.Create(“否”,UIAlertActionStyle.Cancel,null));
controller.PresentViewController(对话框,false,null);
}
//用户拾取文件时的委托(此时不使用)
public override void didpickDocumentsThatls(UIDocumentBrowserViewController控制器,NSUrl[]documentUrls)
{
var dialog=UIAlertController.Create(“覆盖文件”、“是否确实要覆盖此文件?”,UIAlertControllerStyle.Alert);
AddAction(UIAlertAction.Create(“是”,UIAlertActionStyle.Default,action=>OnFilePicked(documentUrls[0]));
AddAction(UIAlertAction.Create(“否”,UIAlertActionStyle.Cancel,null));
controller.PresentViewController(对话框,false,null);
}
//成功导入创建的文档时的委托
public override void DidImportDocument(UIDocumentBrowserViewController控制器,NSUrl源URL