Search code examples
c#xamarin.formsxamarin.iosphotosframework

RequestContentEditingInput completionHandler do not run


I have a app where i need to pick images from the phone and display them in the order the user picked them (i need img's in the order picked , not "most recent edited" that is why i am not using UIImagePickerController)

To do this i have a page that shows the latest 50 images with the possibility to load more.

I am getting the Image path and orientation with this code below.

My problem: The CompletionHandler (in RequestContentEditingInput) will only run 17 times... I can see that the the RequestContentEditingInput is called for every image (on this phone 138 times)

I have tried to wait but nothing happens.

The code works as intended when i have less than 17 images...

I have tried to implement a SemaphoreSlim lock in the CompletionHandler code to control that i would only process 10 images at the time but the end result was ether the same or no images was processed at all.

UpdateInfo: It seems its all the LivePhotos that is not processed.

Do anyone have an idea on what is happening here?

An idea for a fix?

Is there another way i can get all image paths and orientation?

Or a totally other solution to let the user pick images from the phone and get the order the user picked them in?

    public class GalleryHelper : IGalleryHelper
    {
        private List<(string, string, string)> _retval;
        private nint _numberofImg = 0;
        private nint _currentImg = 0;

        public List<(string, string, string)> GetAllImgFromGallery()
        {
            _retval = new List<(string, string, string)>();
            _currentImg = 0;
            GetAllImg();
            return _retval;
        }

        private void GetAllImg()
        {
            var fetchOptions = new PHFetchOptions();

            PHFetchResult allPhotos = PHAsset.FetchAssets(PHAssetMediaType.Image, fetchOptions);
            _numberofImg = allPhotos.Count;

            Debug.WriteLine("Total img no + " + _numberofImg);

            for (nint i = 0; i < allPhotos.Count; i++)
            {
                Debug.WriteLine("Starting " + i);
                (allPhotos[i] as PHAsset).RequestContentEditingInput(new PHContentEditingInputRequestOptions(), CompletionHandler);
            }
        }

        private void CompletionHandler(PHContentEditingInput contentEditingInput, NSDictionary requestStatusInfo)
        {
            Debug.WriteLine("Starting CompletionHandler " + (1 + _currentImg));
            var path = contentEditingInput.FullSizeImageUrl.Path;
            if (path != null)
            {
                var orientation = contentEditingInput.FullSizeImageOrientation;

                switch (orientation)
                {
                    case CIImageOrientation.TopLeft:
                        //Standard position
                        _retval.Add((path, null, "0"));
                        break;

                    case CIImageOrientation.LeftBottom:
                        //Rotate 90 degrees clockwise
                        _retval.Add((path, null, "-90"));
                        break;

                    case CIImageOrientation.RightTop:
                        //Rotate 90 degrees counterclockwise
                        _retval.Add((path, null, "90"));
                        break;

                    case CIImageOrientation.BottomRight:
                        //Rotate 180 degrees
                        _retval.Add((path, null, "180"));
                        break;

                    case CIImageOrientation.BottomLeft:
                        //Mirror image rotated 180 degrees
                        _retval.Add((path, null, "180"));
                        break;

                    case CIImageOrientation.TopRight:
                        //Mirror image
                        _retval.Add((path, null, "0"));
                        break;

                    case CIImageOrientation.LeftTop:
                        //Mirror image rotate 90 degrees clockwise
                        _retval.Add((path, null, "-90"));
                        break;

                    case CIImageOrientation.RightBottom:
                        //Mirror image rotate 90 degrees counterclockwise.
                        _retval.Add((path, null, "90"));
                        break;

                    default:
                        _retval.Add((path, null, "0"));
                        break;
                }
            }
            _currentImg++;
            Debug.WriteLine("Images done " + _currentImg);
            MessagingCenter.Send((App)Xamarin.Forms.Application.Current, "ImagesReady", _retval);

        }
    }

A paste from my Debug Output on a run:

[0:] Total img no + 138
[0:] Starting 0
[0:] Starting 1
(...)
[0:] Starting 137
[0:] Starting CompletionHandler 1
[0:] Images done 1
[0:] Starting CompletionHandler 2
[0:] Images done 2
[0:] Starting CompletionHandler 3
[0:] Images done 3
(...)
[0:] Starting CompletionHandler 15
[0:] Images done 15
[0:] Starting CompletionHandler 16
[0:] Images done 16
[0:] Starting CompletionHandler 17
[0:] Images done 17
Thread started: #17
Thread finished: #15
The thread 0xf has exited with code 0 (0x0).
Thread finished: #3

Solution

  • If anyone in the future has the same problem. Use PHImageManager.DefaultManager.RequestImageData() instead of PHAsset.RequestContentEditingInput() as it do not crash if you ask it to process alot of images.

    I do something like:

        var fetchOptions = new PHFetchOptions();
        PHFetchResult allPhotos = PHAsset.FetchAssets(PHAssetMediaType.Image, fetchOptions);
        _numberofImg = allPhotos.Count;
        Debug.WriteLine("Total img no + " + _numberofImg);
        await Task.Yield();
        await Task.Run(() => ImgProcess(allPhotos));
        return true;
    
    private void ImgProcess(PHFetchResult allPhotos)
    {
        //await Task.Run(() =>
        //{
            for (nint i = 0; i < allPhotos.Count; i++)
            {
                Debug.WriteLine("Starting " + i);
                var phasset = allPhotos[i] as PHAsset;
                var options = new PHImageRequestOptions()
                {
                    Synchronous = true,
                    NetworkAccessAllowed = false,
                    DeliveryMode = PHImageRequestOptionsDeliveryMode.FastFormat
                };
                PHImageManager.DefaultManager.RequestImageData(phasset, options, ComplManager);
            }
        //});
        //return;
    }
    

    even if this solution still have problems when the number of images get over something like 300...