« xDev Magazine 22.3 Is… | Home | Preemptive vs. cooper… »

Picking picture on macOS

When your application needs a picture, you may offer the user to pick a picture from the photo library. With the PHPickerViewControllerMBS class in our MBS Xojo iOS Plugin, you can let the user pick a picture from his/her photo library. And with the controller the user doesn't need to grant permission to the photo library.

To open the photo picker, you need to first define what you like it to do and thus create a configuration object. There you can pick video, photo or live photos or a combination of them with anyFilterMatchingSubfilters() function. We limit the user to one picture by setting the selection limit to one. The selection can be default, continuous if you need a lot of images and optional be ordered.

Sub ShowPicker() var configuration As New PHPickerConfigurationMBS configuration.Filter = PHPickerFilterMBS.imagesFilter // set to zero for unlimited configuration.SelectionLimit = 1 configuration.Selection = configuration.SelectionDefault configuration.PreferredAssetRepresentationMode = configuration.AssetRepresentationModeCompatible picker = New PHPickerViewControllerMBS(configuration) AddHandler picker.didFinishPicking, AddressOf PickerFinished picker.Present End Sub

We use AddHandler to connect the PickerFinished function to handle the event without subclassing the PHPickerConfigurationMBS class. This event and our method are called when the user picked something. There is a little thing to handle: Images may come in different variants and we need to request the image asynchronously as it may need to be downloaded or converted. We check the available types and pick heic, png or try jpeg formats.

Sub PickerFinished(controller as PHPickerViewControllerMBS, results() as PHPickerResultMBS) var result as PHPickerResultMBS = results(0) var registeredTypeIdentifiers() As String = result.TypeIdentifiers System.DebugLog "Types: "+String.FromArray(registeredTypeIdentifiers, EndOfLine) // HEIC available? if registeredTypeIdentifiers.IndexOf("public.heic") >= 0 then result.loadFileRepresentationForTypeIdentifier("public.heic", WeakAddressOf LoadFileRepresentationCompleted) elseif registeredTypeIdentifiers.IndexOf("public.png") >= 0 then // try png const identifier = "public.png" result.loadFileRepresentationForTypeIdentifier(identifier, WeakAddressOf LoadFileRepresentationCompleted) else // try jpeg const identifier = "public.jpeg" result.loadFileRepresentationForTypeIdentifier(identifier, WeakAddressOf LoadFileRepresentationCompleted) End If End Sub

We use a callback method to run when the image is loaded. You may show a progress wheel while this happens. When we get the callback with the folderitem for the requested image, we load it as picture and show it in a canvas.

Sub LoadFileRepresentationCompleted(File as FolderItem, error as NSErrorMBS, result as PHPickerResultMBS) if error <> nil then MessageBox error.LocalizedDescription end if if file <> nil then System.DebugLog file.NativePath // normal opening #if MBS.HasMacCIPlugin = false then // load directly. Picture may be rotated pic = Picture.Open(file) Canvas1.Refresh #else // use CIImage to rotate to always be top left orientation var ciImage As CIImageMBS = New CIImageMBS(file) var properties As Dictionary = ciImage.properties If properties.HasKey("Orientation") Then var orientation As Integer = ciImage.properties.Value("Orientation") ciImage = ciImage.imageByApplyingOrientation(orientation) End If var cgPic As CGImageMBS = ciImage.CreateCGImage pic = cgPic.Picture Canvas1.Refresh #EndIf End If End Sub

Sometimes images are actually stored rotated and if we open them with Picture.Open, we get them rotated. So it may be a better way to open them with CIImageMBS class, apply the metadata about the rotation to rotate the image as needed and then ask for the picture.

Please try the above code. You can use it both in your macOS and iOS applications to provide a way for the users to pick a photo from their photo libraries.

06 05 24 - 11:20