GMImagePicker ported to Xamarin.iOS

TL;DR
I ported GMImagePicker to C#. Code here, Nuget here. Happy coding!

This past week I was working on a Xamarin project where we need support for selecting multiple images and/or taking pictures and uploading them to a backend service. The default UIImagePicker control in iOS is ok but not very versatile. It can take a picture with the camera, or lets you select a single image from your gallery but that’s about it. Furthermore, working with the resulting images is quite cumbersome as you’re working with large UIImage objects in memory. A lot of performance and memory issues can happen if you’re not careful.

In order to deal with photos and videos more efficiently and in a much richer manner, Apple introduced the PhotoKit API in iOS 8. Mike Bluestein has written a nice introductory post on this API on the Xamarin blog, so I’m not going to repeat this.

In short, PhotoKit works with the notion of PHAsset objects, which are basically descriptors for media files on the device. There are API’s to query different photo galleries, etcetera. Only once you actually need the image for display or other purposed do you have to retrieve the image using the PHAsset descriptor. Very efficient.

Many apps, such as the Facebook app, allow users to select multiple images in a user friendly manner, and maybe even add pictures on the go by providing access to the camera while they are browsing their gallery. This is something that we also wanted to add to our app. Luckily, there are some nice open source projects around that implement just that. One of the nicest ones is the GMImagePicker component by Guillermo Muntaner Perelló, which uses the PhotoKit API under the hood. The user experience looks like this:

gmimagepickerdemo

That’s slick! You can browse through several collections, and the control is highly customizable, and even contains localized texts for labels, buttons and dialogs. Only, it’s written in Objective-C…

I had two options: bind the API using Xamarin’s Objective Sharpie or port it verbatim to C#. I chose to port it, mainly to have full control over the inner workings of the control and to not have to pull in a “foreign” language into the project. The port has complete feature parity with the Objective-C version and I tried to iron out as many issues as I could. It seems to be working pretty smoothly in my Xamarin app.

The code is up on GitHub and you can use the control by either downloading the code and including the .csproj in your project, or install the Nuget package in your Xamarin.iOS app:

Install-Package GMImagePicker.Xamarin

As I said, the GMImagePicker control is highly customizable. You can change its appearance by specifying colors for different parts of the UI, and you can provide custom titles and confirmation prompts. It’s also possible to filter and limit the types of assets you want the user to select. The whole range of options can be found in the sample app that comes with the control. Here is an overview:

var picker = new GMImagePickerController {
Title = "Custom Title",
CustomDoneButtonTitle = "Finished",
CustomCancelButtonTitle = "Nope",
CustomNavigationBarPrompt = "Take a new photo or select an existing one!",
ColsInPortrait = 3,
ColsInLandscape = 5,
MinimumInteritemSpacing = 2.0f,
DisplaySelectionInfoToolbar = true,
AllowsMultipleSelection = true,
ShowCameraButton = true,
AutoSelectCameraImages = true,
ModalPresentationStyle = UIModalPresentationStyle.Popover,
MediaTypes = new [] { PHAssetMediaType.Image },
// Other customizations to play with:
//ConfirmSingleSelection = true,
//ConfirmSingleSelectionPrompt = "Do you want to select the image you have chosen?",
//PickerBackgroundColor = UIColor.Black,
//PickerTextColor = UIColor.White,
//ToolbarBarTintColor = UIColor.DarkGray,
//ToolbarTextColor = UIColor.White,
//ToolbarTintColor = UIColor.Red,
//NavigationBarBackgroundColor = UIColor.Black,
//NavigationBarTextColor = UIColor.White,
//NavigationBarTintColor = UIColor.Red,
//PickerFontName = "Verdana",
//PickerBoldFontName = "Verdana-Bold",
//PickerFontNormalSize = 14.0f,
//PickerFontHeaderSize = 17.0f,
//PickerStatusBarStyle = UIStatusBarStyle.LightContent,
//UseCustomFontForNavigationBar = true,
};

// You can limit which galleries are available to browse through
picker.CustomSmartCollections = new [] {
PHAssetCollectionSubtype.SmartAlbumUserLibrary,
PHAssetCollectionSubtype.AlbumRegular
};

// Event handling
picker.FinishedPickingAssets += Picker_FinishedPickingAssets;
picker.Canceled += Picker_Canceled;

// Other events to implement in order to influence selection behavior:
// Set EventArgs::Cancel flag to true in order to prevent the action from happening
picker.ShouldDeselectAsset += (s, e) => { /* allow deselection of (mandatory) assets */ };
picker.ShouldEnableAsset += (s, e) => { /* determine if a specific asset should be enabled */ };
picker.ShouldHighlightAsset += (s, e) => { /* determine if a specific asset should be highlighted */ };
picker.ShouldShowAsset += (s, e) => { /* determine if a specific asset should be displayed */ };
picker.ShouldSelectAsset += (s, e) => { /* determine if a specific asset can be selected */ };
picker.AssetSelected += (s, e) => { /* keep track of individual asset selection */ };
picker.AssetDeselected += (s, e) => { /* keep track of individual asset de-selection */ };

// The GMImagePicker can be treated as a PopOver as well:
var popPC = picker.PopoverPresentationController;
popPC.PermittedArrowDirections = UIPopoverArrowDirection.Any;
popPC.SourceView = gmImagePickerButton;
popPC.SourceRect = gmImagePickerButton.Bounds;

await PresentViewControllerAsync(picker, true);

The extensibility is very convenient. For example: if you want to set a maximum to the total size of the images you want to allow the user to select, you can handle the AssetSelected event, keep track of the total size selected, and handle ShouldSelectAsset, to prevent selection if a maximum threshold has been reached. This is exactly what we wanted to have in our app.

Once the user has finished selecting assets, you can use a PHImageManager to retrieve the actual images in whatever size you like:

void FinishedPickingAssets (object s, MultiAssetEventArgs e)
{
  PHImageManager imageManager = new PHImageManager();

  foreach (var asset in e.Assets) {
    imagePreview.Image = null;

    imageManager.RequestImageForAsset (asset, 
      new CGSize(asset.PixelWidth, asset.PixelHeight), 
      PHImageContentMode.Default, 
      null, 
      (image, info) => {
        // do something with the image (UIImage), e.g. upload to server
        // you can get the JPEG byte[] via image.AsJPEG()
      });
  }
}

Very nice, and it’s now available for Xamarin.iOS developers as well 🙂

photo-1437419764061-2473afe69fc2
Photo by Andrew Illarionov (https://unsplash.com/photos/-WW8jBak7bo)

Many thanks to Guillermo for letting me port his excellent code and publish it. I’d love to hear your feedback on the code and would love to see your PR’s for improvements.

Advertisements

6 thoughts on “GMImagePicker ported to Xamarin.iOS”

  1. can you please explain how to iterate through the list of images selected and upload the images ?and since you have SaveToPhotosAlbum for an image does it strips out location data from the image?

    1. Hi Kittu, I have updated the last code snippet in the blogpost above to show how you can iterate through the selected images. They are basically a set of PHAsset objects, which are “descriptors” of photos in the photo library. Through the PHImageManager class you can retrieve them in a desired size. You will get a UIImage object that you can then use for display or other purposes.

      Uploading them could be done by getting the JPEG representation of the UIImage, via .AsJPEG(). As far as I know it will not automatically strip out the metadata. You could use multipart uploads for example. This is a nice blogpost describing this: http://stacktips.com/tutorials/xamarin/upload-bitmap-image-to-server-using-http-multipart-in-xamarin-android

      It focuses on Android but once you have the byte[] from the UIImage.AsJPEG() method, the process is basically the same.

  2. Hi, is it possible to change selection style? Now I have only blue tint, without these round icons at the top right corners of selected previews. How can I display them?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s