Is anyone able to give a short, self-contained example on how to access the camera with Xamarin.Forms 1.3.x? Simply calling the native camera application and retrieving the resulting picture would be great. Displaying a live view on the Xamarin.Forms page would be awesome!
I already tried to use Xamarin.Mobile and Xamarin.Forms.Labs, but I couldn't get any solution to work on both platforms (focussing on Android and iOS for now). Most code snippets found on the web (including stackoverflow) are incomplete, e.g. not showing the implementation of an IMediaPicker object or where to anchor the method for taking pictures.
I finally created a minimum solution for iOS and Android.
First, let's look into the shared code. For an easy interaction between the shared App
class and the platform-specific code we store a static Instance
within the public static App
:
public static App Instance;
Furthermore, we will display an Image
, which will be filled with content later. So we create a member:
readonly Image image = new Image();
Within the App
constructor we store the Instance
and create the page content, which is a simple button
and the aforementioned image
:
public App()
{
Instance = this;
var button = new Button {
Text = "Snap!",
Command = new Command(o => ShouldTakePicture()),
};
MainPage = new ContentPage {
Content = new StackLayout {
VerticalOptions = LayoutOptions.Center,
Children = {
button,
image,
},
},
};
}
The button's click handler calls the event ShouldTakePicture
.
It is a public member and the platform-specific code parts will assign to it later on.
public event Action ShouldTakePicture = () => {};
Finally, we offer a public method for displaying the captured image:
public void ShowImage(string filepath)
{
image.Source = ImageSource.FromFile(filepath);
}
On Android we modify the MainActivity
.
First, we define a path for the captured image file:
static readonly File file = new File(Environment.GetExternalStoragePublicDirectory(Environment.DirectoryPictures), "tmp.jpg");
At the end of OnCreate
we can use the static Instance
of the created App
and assign an anonymous event handler, which will start a new Intent
for capturing an image:
App.Instance.ShouldTakePicture += () => {
var intent = new Intent(MediaStore.ActionImageCapture);
intent.PutExtra(MediaStore.ExtraOutput, Uri.FromFile(file));
StartActivityForResult(intent, 0);
};
Last but not least, our activity has to react on the resulting image. It will simply push its file path to the shared ShowImage
method.
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
App.Instance.ShowImage(file.Path);
}
That's about it! Just don't forget to set the "Camera" and the "WriteExternalStorage" permission within "AndroidManifest.xml"!
For the iOS implementation we create a custom renderer. Therefore, we add a new file "CustomContentPageRenderer" and add the corresponding assembly attribute right after the using statements:
[assembly:ExportRenderer(typeof(ContentPage), typeof(CustomContentPageRenderer))]
The CustomContentPageRenderer
inherits from PageRenderer
:
public class CustomContentPageRenderer: PageRenderer
{
...
}
We override the ViewDidAppear
method and add the following parts.
Create a new image picker controller referring to the camera:
var imagePicker = new UIImagePickerController { SourceType = UIImagePickerControllerSourceType.Camera };
Present the image picker controller, as soon as the ShouldTakePicture
event is raised:
App.Instance.ShouldTakePicture += () => PresentViewController(imagePicker, true, null);
After taking the picture, save it to the MyDocuments
folder and call the shared ShowImage
method:
imagePicker.FinishedPickingMedia += (sender, e) => {
var filepath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "tmp.png");
var image = (UIImage)e.Info.ObjectForKey(new NSString("UIImagePickerControllerOriginalImage"));
InvokeOnMainThread(() => {
image.AsPNG().Save(filepath, false);
App.Instance.ShowImage(filepath);
});
DismissViewController(true, null);
};
And finally, we need to handle a cancellation of the image taking process:
imagePicker.Canceled += (sender, e) => DismissViewController(true, null);