MauiBlazor iOS端多选照片以及显示本地照片

发布时间 2023-11-25 14:31:31作者: MTony

安卓端参考之前这篇文章:https://www.cnblogs.com/wecareu/p/17855415.html

iOS端,目前Maui提供的原生接口是MediaPicker 应该对应调用的是iOS原生的UIIMagePicker, 效果也不错,但是一次只能选择单张照片

在ios14以后,苹果推出了新的PHPickerViewController,支持多选照片,UI以弹窗的形式出现,效果十分不错。

 

1. 在Platform-iOS里新建IOSPhotoPIckerService.cs, 由于PHPickerViewController是新添加的,所以别忘记引进using PhotosUI;添加如下代码:

[assembly: Dependency(typeof(IOSPhotoPickerService))]
namespace AndroidPhotoPicker.Platforms.iOS
{
    public class IOSPhotoPickerService : IPhotoPickerService
    {
        class PPD : PHPickerViewControllerDelegate
        {
            public Action<PHPickerResult[]> CompletedHandler { get; set; }

            public override void DidFinishPicking(PHPickerViewController picker, PHPickerResult[] results) =>
                CompletedHandler?.Invoke(results?.Length > 0 ? results : null);
        }

        //TaskCompletionSource<Dictionary<string, string>> taskCompletionSource;
        //UIImagePickerController imagePicker;
        PHPickerViewController picPicker;

        async Task<Dictionary<string, string>> IPhotoPickerService.GetImageByIntent()
        {
            var dic = new Dictionary<string, string>();       
            picPicker = new PHPickerViewController(new PHPickerConfiguration
            {
                Filter = PHPickerFilter.ImagesFilter,
                SelectionLimit = 3,
                PreferredAssetRepresentationMode = PHPickerConfigurationAssetRepresentationMode.Automatic,
                
            });
            var window = UIApplication.SharedApplication.KeyWindow;
            var vc = window.RootViewController;
            while (vc.PresentedViewController != null)
            {
                vc = vc.PresentedViewController;
            }
            var tcs = new TaskCompletionSource<(List<string>, List<string>)>();

            picPicker.Delegate = new PPD
            {
                CompletedHandler = async res =>
                {
                    var resultd = await PickerResultsToMediaFile(res);
                    tcs.TrySetResult(resultd);
                }
            };
            await vc.PresentViewControllerAsync(picPicker, true);
            var resultTask = await tcs.Task;
            //var result = await resultTask;
            //var result = resultTask.Item1;
            //string base64String = Convert.ToBase64String(result);
            //var filename = resultTask.Item2;
            var base64Strings = resultTask.Item1;
            var filenames = resultTask.Item2;

            for (int i = 0; i < base64Strings.Count; i++)
            {
                dic.Add(filenames[i], base64Strings[i]);
            }

            await vc.DismissViewControllerAsync(true);
            picPicker?.Dispose();
            //return result;
            return dic;
        }
        private async Task<(List<string>, List<string>)> PickerResultsToMediaFile(PHPickerResult[] res)
        {
            var results = new List<string>();
            List<string> stringList = new List<string>();
            var tcs = new TaskCompletionSource<NSObject>();
            if (res != null)
            {
                foreach (var item in res)
                {
                    try
                    {
                        var provider = item.ItemProvider;
                        var suggestedName = provider.SuggestedName;
                        var identifiers = provider?.RegisteredTypeIdentifiers;

                        var identifier = (identifiers?.Any(i => i.StartsWith(UTType.LivePhoto)) ?? false)
                            && (identifiers?.Contains(UTType.JPEG) ?? false)
                            ? identifiers?.FirstOrDefault(i => i == UTType.JPEG)
                            : identifiers?.FirstOrDefault();
                        if (string.IsNullOrWhiteSpace(identifier))
                            continue;
                        var timestamp = DateTime.UtcNow.Ticks;
                        var fileName = $"{timestamp}{provider?.SuggestedName}.{GetTag(identifier, UTType.TagClassFilenameExtension)}";
                        stringList.Add(fileName);
                        var stream = (await provider.LoadDataRepresentationAsync(identifier))?.AsStream();
                        string tempFolderPath = Path.GetTempPath();
                        string filePath = Path.Combine(tempFolderPath, fileName);
                        using (FileStream fs = new FileStream(filePath, FileMode.Create))
                        {
                            stream.CopyTo(fs);
                            results.Add(filePath);
                            Console.WriteLine(filePath);
                        }

                    }
                    catch (Exception)
                    {
                        continue;
                    }
                }
            }
            
            return (results, stringList);
        }
        protected internal static string GetTag(string identifier, string tagClass)
        => UTType.CopyAllTags(identifier, tagClass)?.FirstOrDefault();

    }
}

 

2. 在上一片文章的基础上,我们在MauiProgram.cs里注册iOS的接口:

#if IOS
builder.Services.AddSingleton<IPhotoPickerService, IOSPhotoPickerService>();
#endif

3. 同样在MainPage.xaml.cs里,补上上一篇文章提到的iOS需要的代码:

    void blazorWebView_BlazorWebViewInitializing(System.Object sender, Microsoft.AspNetCore.Components.WebView.BlazorWebViewInitializingEventArgs e)
    {
#if IOS
        e.Configuration.SetUrlSchemeHandler(new MySchemeHandler(), "myfile");
#endif

    }

#if IOS
    private class MySchemeHandler : NSObject, IWKUrlSchemeHandler
    {
        [Export("webView:startURLSchemeTask:")]
        public void StartUrlSchemeTask(WKWebView webView, IWKUrlSchemeTask urlSchemeTask)
        {
            if (urlSchemeTask.Request.Url == null)
            {
                return;
            }

            var path = urlSchemeTask.Request.Url?.Path ?? "";
            if (File.Exists(path))
            {
                byte[] bytes = File.ReadAllBytes(path);
                using var response = new NSHttpUrlResponse(urlSchemeTask.Request.Url, 200, "HTTP/1.1", null);
                urlSchemeTask.DidReceiveResponse(response);
                urlSchemeTask.DidReceiveData(NSData.FromArray(bytes));
                urlSchemeTask.DidFinish();
            }
        }

        [Export("webView:stopURLSchemeTask:")]
        public void StopUrlSchemeTask(WKWebView webView, IWKUrlSchemeTask urlSchemeTask)
        {
        }
#endif

 4. 在Index.razor里,添加如下测试代码:

@page "/"
@using AndroidPhotoPicker.Service

<h1>Hello, world!</h1>

Welcome to your new app.

<button @onclick="GetImageAsync3">添加图片INTENT</button>


@if (_phoneDictionary.Any())
{
    @foreach (var phone in _phoneDictionary)
    {
        <div style="height: 100%; width: 100%;">
            <img src="@phone.Value" style="height:90px;width:90px;object-fit:cover;" />
        </div>
        <div>图片名称: @phone.Key</div>
    }
}

@code{

    [Inject]
    private IPhotoPickerService _photoPickerService { get; set; } //注入服务接口

    private Dictionary<string, string> _phoneDictionary { get; set; } = new Dictionary<string, string>();//图片路径字典
    private async Task GetImageAsync3()
    {
        var photoDic = await _photoPickerService.GetImageAsyncByIntent();
        foreach (var photo in photoDic)
        {
             string iosUrl = "myfile://" + photo.Value;
            _phoneDictionary.Add(photo.Key, iosUrl ;
        }
        await InvokeAsync(StateHasChanged);
    }

}