How to let users upload their photos into your app
Giving your users the ability to upload their own photos into your app is pretty common and yet, can still be tough within Xamarin Forms. Anytime you need to access a camera or work with images (besides just showing one) you’ll need to implement it at the platform level. So what we’ll cover is how to use Dependency Injection to get our images from either the camera or an image gallery (if you need a refresher on this topic you can start here https://developer.xamarin.com/guides/xamarin-forms/dependency-service/).
Source Code
I’ve added a sample project that contains everything, even the recent updated changes, you can find it here. Some of the code in this post might not match 1 to 1 with the sample project but most of the core concepts are the same.
Getting Dependent
Let’s start by creating a new interface that will define two methods that we will implement later on for both iOS and Android. This interface should be placed in the shared code solution of your project.
iOS Setup
In your iOS Solution create a new class that will implement the CameraInterface we just created.
Now we need to start implementing the two methods we declared in the interface, we’ll start with the camera. The first thing we need to do is check if we have permission to access the camera, if this is the first time asking, then the user will get the default iOS prompt where they can give your app camera permission. If they have already declined permission before you’ll want to add a message to the user telling them so and how to turn this back on in their settings.
With camera permission out of the way, we can now launch the camera and wait for the callback once the picture has been taken.
To recap the code snippet above, once the user takes a picture, I keep the original size but compress it by 50% to make the file size smaller. I then turn the image into a byte array so I can send it to a server to be saved. Even if you have no interest in doing that this approach still works really well since even though iOS uses UIImage and Android uses Bitmaps, both of these types can be converted into a byte array so the end result will work for both platforms. Stay tuned for how we will use the Messaging Center and where that is going 🙂
One final note for iOS, you also need to provide two values in the info.plist that basically let’s Apple know your app intends to use the camera and access photos.
You can use any messaging you prefer on the right, just remember that your users will actually see this message when they get the iOS prompt asking permission.
Android Setup
NOTE: Some of the Android code is outdated now with recent changes to Android’s file system etc. If you noticed weird behavior when trying this code that’s why. Updated solution for Android can be found here.
In the Droid solution we need to create a class that implements the CameraInterface just like we did on iOS.
This should look very similar to what we just did, the only difference is the rest of our code will reside in the MainActicity.cs class.
In order to catch the call back from the camera and the image gallery we need to implement the method OnActivityResult inside the MainActivity.cs class.
One of things I encountered when doing all of this Bitmap work, mainly fixing the orientation of the image, took a really long time on Android. So to make the user experience as seamless as possible, I highly recommend doing this on a background thread so your app isn’t staring at a black screen for a few seconds while the phone is processing the bitmaps.
In order to do this I created a “child” class within the MainActivity that implements AsyncTask. If you’ve ever done native Android in Java your probably familiar with this, the only real difference here is of course the syntax. If your not familiar with this, AsyncTask is just a way to run some code on a non UI thread and it gives you a call back once your code is finished to then update your UI. You can find more information here, https://developer.android.com/training/displaying-bitmaps/process-bitmap.html#concurrency.
Again, this code is almost identical to what we did on iOS, except now we are going to do all the processing in the background then send the byte array back up to our UI.
Final thoughts on Android, make sure you update the AndroidManifest.xml file to include Camera and ReadExternalStorage permissions.
Back to the MessagingCenter
Now we can go over how to take these byte arrays and actually get the image to show up in the UI. Since this tutorial is meant for Xamarin Forms, your main UI will be up in the shared code solution, then we call down to the specific platform we’re on to handle the camera and photos, then we send the result back up to the shared code solution using the MessagingCenter.
You can find more about this here https://developer.xamarin.com/guides/xamarin-forms/messaging-center/.
In the UI I have a button that ask to upload a photo, here is the button event.
The code above is how we use the Dependency Injection to then call the camera or launch the photo gallery on each platform.
In order to catch the byte array we keep sending, we’ll need to subscribe and listen for that event (I named it ImageSelected). So inside your ContentPage constructor (or which ever layout you are using for the UI that will be displaying the image), add the last bit for MessagingCenter.
Great write up! Good explanation of the APIs too 🙂 Have you taken a look at my media plugin yet? https://github.com/jamesmontemagno/mediaplugin
Thanks James! I had not, looks very interesting though!
I am a complete novice in the coding world and I loved you explanation!
However, I am wondering how does the BringUpPhotoGallery method actually work, since I did not find it being implemented in either iOS or on Android.
Thanks in advance!
My apologies, I’ve updated the code to include the BringUpPhotoGallery method right below the BringUpCamera method.
Thanks a lot Scott! Also, I might be doing something wrong here, but on Android, it keeps throwing me an error that BringUpPhotoGallery is not implemented even when I add the necessary code to CameraAndroid.cs. Any suggestions for debugging?
Hey James! I checked out your media plugin too! I have a question for you. How exactly do you call crossmedia.current from iOS (It is simple enough on android- just add one line of code to MainActivity.cs)? I searched but failed to find anything for iOS online! Tons of stuff available for Android though.
Thanks in advance!
I’ve updated the android code to get rid of this error (it was old code I forgot to update). Basically, at the very top where the CameraInterface is being created, the method BringUpPhotoGallery is being declared and has no parameters, but in the android implementation the method had two parameters (int photoId, bool saveImage), this is why you got the error because you had not implemented a method called BringUpPhotoGallery that had no parameters. You just need to delete these so you’ll end up with a method that takes in no parameters, public void BringUpPhotoGallery(). If that doesn’t make any sense do a google search on “Overloaded Methods”.
Thank you for taking the time to respond so kindly Scott! I did read up more and it makes sense now! You are awesome! I have got everything working now! 🙂
Hey Scott.
Do you have a working project of this code? would be great if you could share the GitHub link – so we could experiment and modify for our needs. (for both iOS and Android?)
Hello Scott! I found that your code works perfectly when the request code in Android is set to “0” instead of “1” and if(requestCode==1) is not used in MainActivity. I am not sure why it won’t work with requestCode set to “1”. Also, could your code be used for videos instead of pictures? If so, where can I find the documentations regarding the required changes in the code.
i don’t know why if i have more then 1 page in the app, after selected image app always redirect to the main page. (if just 1 page every things work as well)
Can anyone tell how to take multiple pictures from gallery in IOS?. Without using any plugin
This does not seem to work – the subscribe event never gets invoked when photo taken from camera and accepted, it only gets invoked when a photo is chosen from a gallery.
. I noticed the OnActivityResult executes in MainActivity.cs but the Data property of Intent is always null when select photo taken from camera, it’s only set when selecting photo from gallery as before.
Hi John, from this comment it looks like your having trouble with the Android code? Sadly this code no longer works with Android 7.0 and up. I’ll be updating this post to redirect to a new post I just did that handles the Android solution using a FileProvider which should help with any issues you might be having. Hopefully there are no errors as I had to lift and slightly modify some of the code before posting it. https://baglabs.com/2018/01/24/xamarin-forms-taking-pictures-android/