[XAMARIN FORMS] Uso de la cámara

En este articulo vamos a ver como hacer uso de la cámara de fotos desde aplicaciones móviles usando Xamarin Forms

Vamos a usar Visual Studio 2017 para crear un proyecto del tipo “Cross-Platform App (Xamarin)”

el cual lanzará un asistente, donde seleccionaremos las opciones:

  • Aplicación en blanco
  • Xamarin.Forms
  • Biblioteca de clases portable

Una vez que Visual Studio crea el proyecto, vamos a interactuar con la cámara de fotos. La forma más sencilla y rápida para tener acceso a la cámara es instalando el paquete NuGet Xam.Plugin.Media en todos los proyectos de la solución.

A continuación, vamos a configurar cada proyecto para solicitar los permisos de acceso a la cámara.

En Android hay que acceder a las propiedades del proyecto, y luego hacer clic en “Manifiesto de Android” y marcar los permisos

  • WRITE_EXTERNAL_STORAGE
  • READ_EXTERNAL_STORAGE
  • CAMERA

Adicionalmente, hay que modificar el fichero MainActivity.cs para añadir el siguiente fragmento de código

        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
        {
            PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }

En IOS, hay que modificar el fichero info.plist para añadir los permisos

  • NSCameraUsageDescription
  • NSPhotoLibraryUsageDescription
  • NSMicrophoneUsageDescription

agregando el texto

<key>NSCameraUsageDescription</key>
<string>This app needs access to the camera to take photos.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs access to photos.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to microphone.</string>

Nota: En lugar de usar el editor predeterminado, hacemos clic derecho sobre “info.plist”, seleccionamos la opción “Abrir Con” y seleccionamos un editor XML.

En UWP, hay que hacer doble clic sobre “Package.manifiest”, despues hacemos clic sobre “Capacidades” y, por último, seleccionamos las opciones “Camara Web” y “Almacenamiento extraible”.

Una vez que hemos configurado el plugin, vamos a añadir la clase ServicioFoto al proyecto de la libreria portable

using Plugin.Media;
using Plugin.Media.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace XamarinCamara.Servicios
{
    public class ServicioFoto
    {
        public ServicioFoto(){
            CrossMedia.Current.Initialize();
        }

        private static ServicioFoto m_Instancia;

        public static ServicioFoto Instancia
        {
            get
            {
                if (m_Instancia == null)
                {
                    m_Instancia = new ServicioFoto();
                }

                return m_Instancia;
            }
        }

        public async Task<MediaFile> SeleccionarFotoAsync()
        {
            MediaFile foto = null;
            try
            {
                
                if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
                {
                    return null;
                }
                else
                {
                    foto = await CrossMedia.Current.PickPhotoAsync();
                }

            }
            catch (TaskCanceledException)
            {
                foto = null;
            }
            return foto;
        }

        public async Task<MediaFile> CapturarFotoAsync()
        {
            MediaFile foto = null;
            try
            {
                if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
                {
                    return null;
                }
                else
                {
                    foto = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions());
                }

            }
            catch (TaskCanceledException)
            {
                foto = null;
            }
            return foto;
        }
    }
}

Esta clase contiene 2 métodos:

  • SeleccionarFotoAsync: usa el método CrossMedia.Current.PickPhotoAsync para seleccionar una foto del almacenamiento del dispositivo
  • SeleccionarFotoAsync: usa el método CrossMedia.Current.TakePhotoAsync para capturar una foto usando la cámara.

Nota: Hay que tener en cuenta que antes usar las funciones del plugin, hay que invocar al método

CrossMedia.Current.Initialize()

A continuación vamos a crear un sencillo ejemplo, usando el patrón diseño MVVM, que haga uso de la cámara. Primero añadimos una viewModel

using Plugin.Media.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using XamarinCamara.Servicios;

namespace XamarinCamara.ViewModels
{
    public class CamaraViewModel : BindableObject
    {
        public static readonly BindableProperty RutaFotoProperty = BindableProperty.Create(
                          "RutaFoto",
                          typeof(string),
                          typeof(CamaraViewModel),
                          default(string));
        public string RutaFoto
        {
            get
            {
                return (string)GetValue(RutaFotoProperty);
            }
            set
            {
                SetValue(RutaFotoProperty, value);
            }
        }

        private MediaFile Foto;

        public Command m_seleccionarFotoComand;

        public Command SeleccionarFotoComand
        {
            get
            {
                return (m_seleccionarFotoComand ?? (m_seleccionarFotoComand = new Command(async () => await SeleccionarFotoAsync())));
            }
        }

        public async Task<bool> SeleccionarFotoAsync()
        {
            int nErrores = 0;
            try
            {
                Foto = await ServicioFoto.Instancia.SeleccionarFotoAsync();
                if (Foto != null)
                {
                    RutaFoto = Foto.Path;
                }
            }
            catch (Exception ex)
            {
                string error = ex.Message;
                nErrores++;
            }
            return (nErrores == 0);
        }

        public Command m_tomarFotoComand;

        public Command TomarFotoComand
        {
            get
            {
                return (m_tomarFotoComand ?? (m_tomarFotoComand = new Command(async () => await TomarFotoAsync())));
            }
        }

        public async Task<bool> TomarFotoAsync()
        {
            int nErrores = 0;
            try
            {
                Foto = await ServicioFoto.Instancia.CapturarFotoAsync();
                if (Foto != null)
                {
                    RutaFoto = Foto.Path;
                }
            }
            catch (Exception ex)
            {
                string error = ex.Message;
                nErrores++;
            }
            return (nErrores == 0);
        }

        public CamaraViewModel()
        {

        }
    }
}

y una view

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamarinCamara.Views.CamaraView">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <ScrollView Orientation="Vertical" Grid.Row="0">
            <StackLayout>
                <Image
                Source="{Binding RutaFoto}"
                Aspect="AspectFit"/>
            </StackLayout>
        </ScrollView>
        <ScrollView Orientation="Vertical" Grid.Row="1">
            <StackLayout>
                <Label Text="Ruta Foto"></Label>
                <Label Text="{Binding RutaFoto}"></Label>
            </StackLayout>
        </ScrollView>        
        <Grid Grid.Row="2">
            <Button Text="Seleccionar Foto" Command="{Binding SeleccionarFotoComand}"/>
        </Grid>
        <Grid Grid.Row="3">
            <Button Text="TomarFoto" Command="{Binding TomarFotoComand}"/>
        </Grid>
    </Grid>
</ContentPage>

cuyo code-behind es

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace XamarinCamara.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class CamaraView : ContentPage
    {
        public CamaraView()
        {
            InitializeComponent();
            this.BindingContext = new ViewModels.CamaraViewModel();
        }
        protected override void OnAppearing()
        {
            base.OnAppearing();
        }

        protected override void OnDisappearing()
        {
            base.OnDisappearing();
        }
    }
}

Nota: observa como en la inicialización de la view se crea una nueva instancia del contexto (viewModel).

En la siguiente imagen se puede ver la captura del proyecto Android funcionando en el emulador.

En el siguiente enlace se puede descargar el proyecto de ejemplo.