¿Cómo colaborar con Xamarin.Forms?

Contribuir en Xamarin.Forms

Una de los puntos a favor de Xamarin es su increíble comunidad. Una comunidad activa haciendo constantemente actividades (eventos online, eventos presenciales, etc.) y creando nuevo contenido (artículos, ejemplos, plugins, etc.). Con el paso del tiempo es fantástico ver más y más plugins además de diferentes retos de UI y otro tipo de contenidos que son de ayuda para todos.

Por otro lado, Xamarin.Forms es un proyecto Open Source. Esto tiene una serie de implicaciones entre las que podemos destacar:

  • El código esta disponible. Cualquiera puede revisarlo, compilarlo e incluso aplicar modificaciones.
  • Las issues y PRs son abiertas. De nuevo, cualquiera puede añadir una opinión en una PR o en una futura nueva funcionalidad.
  • Los proyectos de GitHub son públicos. Se puede ver en cada momento en que se esta trabajando, que esta pendiente, etc.
  • La Wiki con aspectos como el Roadmap es público.

Todos los factores anteriores han provocado que cada vez haya un número mayor de colaboraciones de miembros de la comunidad en el repositorio oficial de Xamarin.Forms.

Como miembro del equipo de Xamarin.Forms y habiendo sido colaborador previamente es algo que me encanta ver y fomentar. Por otro lado, recibo en ocasiones correos o mensajes en varios medios (Slack, Skype, etc.) preguntando como puede ayudar a corregir X issue o sencillamente como compilar Xamarin.Forms.

En este artículo, voy a hacer un repaso en las diferentes formas en las que se puede contribuir en Xamarin.Forms, así como compilar el proyecto, poder tomar issues o mejoras, etc.

¿Te interesa?. Vamos a por ello!.

Consejos antes de comenzar

Antes de empezar, colaborar en proyectos Open Source tiene partes positivas como:

  • Es una forma divertida de aprender.

Sin embargo, si nunca has colaborado antes probablemente tengas algunas dudas. La primera de ellas será como hacerlo. Para eso espero que este artículo sea de ayuda. El segundo de los posibles problemas esta en el conocido “síndrome del impostor”. Dudas si lo que haces es correcto,  etc. En este punto, no te preocupes!. Si hay algo que se pueda mejorar, lo verás en el feedback. Es parte de la “gracia” de colaborar así, aprenderás posiblemente cosas nuevas.

Tan solo recuerda:

  • No tengas dudas. Aprenderás cosas nuevas como yo y todos lo hacemos a diario.
  • Puedes preguntar al crear la PR lo que necesites!.

Formas de contribuir

Lo primero de todo, contribuir tiene varias definiciones aunque en este caso:

“se trata de ayudar y concurrir con otros al logro de un cierto fin”

Se puede ayudar de muchas formas!. No tienes porque implementar la espectacular funcionalidad X para poder contribuir.

Vamos a repasar las diferentes opciones disponibles:

  • Reportar una issue.
  • Aportar ideas para nueva funcionalidad.
  • Correcciones en la documentación.
  • Corregir una issue.
  • Nueva funcionalidad.

Reportar una issue

Es una de las formas más sencillas de contribuir. Tienes un problema usando Xamarin.Forms, algo que no se comporta como debería. Abre una issue. De esta forma, avisarás que algo no funciona como debe; se corregirá y en el futuro otros devs tendrán la corrección gracias a que avisaste el problema.

Los pasos a seguir son sencillos:

  1. Logra reproducir un problema de forma constante.
  2. Escribe una Issue explicando el problema. Lo fundamental es detallar en pasos como reproducir el problema.
  3. Detallada cual es el comportamiento que estas obteniendo y cual es el esperado (ayuda a determinar si es una Issue o no).
  4. Añade la versión de Xamarin.Forms utilizada así como la plataforma o plataformas afectadas.
  5. Si es posible, añade algunas capturas.
  6. Si es posible (ayuda muchísimo), añade un pequeño ejemplo adjunto donde reproducir el problema.

Voila!. Sencillo, ¿verdad?.

Recomiendo revisar las issues con el tag excellent-report para ver ejemplos de buenos reportes (buena descripción, pasos, ejemplo adjunto, etc.).

excellent-report

Aportar ideas, añadir nueva funcionalidad

“Estaría genial tener esta propiedad en este control”. Haz una propuesta. Es sencillo, al final es como escribir una issue pero con ligeros cambios.

Los pasos a seguir serían:

  1. Piensa, analiza y piensa de nuevo tu idea.
  2. Abre una issue con el título “[Enhancement] …” y añade una descripción detallada de lo que buscas con esta mejora.
  3. Si has pensado una posible definición para la API añadela.
  4. Si puedes, añade un pequeño ejemplo (Scenario) donde enseñar como se usaría la nueva funcionalidad.
  5. ¿Afecta a alguna API existente?. Indícalo.
  6. Si puedes, añade el nivel de dificultad necesario para implementar la nueva funcionalidad (bajo, medio, alto).
  7. Añade el tag enhancement.

Todo listo!. Puedes ver ejemplos de mejoras buscando en GitHub en las Issues por el tag enhancement. Veamos un ejemplo:

Mejoras

NOTA: Una vez enviada una propuesta, puedes seguir su estado. Revisa el siguiente enlace para conocer los posibles estados (aceptada, rechazada, en progreso, etc.).

Actualizar documentación

¿Encontraste algún error en la documentación?. Puedes enviar también correcciones en la documentación de la Wiki.

Corregir una issue

En este nivel de contribución se ha seleccionado una issue específica a corregir, cuentas con el código fuente sincronizado y ya te dispones a modificar código. Vamos a ver con más detalles este proceso un poco más adelante.

Añadir una nueva funcionalidad

Similar al caso anterior. La diferencia radica que en este caso creas una funcionalidad nueva en lugar de corregir una issue.

Compilar el proyecto

Para compilar el proyecto debemos comenzar por tener el mismo. Es sencillo:

git clone https://github.com/xamarin/Xamarin.Forms.git c:\XamarinForms

Si estas usando Visual Studio para Windows, tras obtener el proyecto no deberás hacer nada especial para compilarlo. Abre la solución Xamarin.Forms.sln y pulsa F5.

Aunque, la solución de Xamarin.Forms tiene varios tipos de proyectos con dependencias del SDK de UWP, .NET Core, etc. Si tienes errores al cargar este tipo de proyectos, primero hay que instalar algunas de las dependencias.

En caso de utilizar Visual Studio para macOS, para evitar problemas de compilación, mi compañero Shane Neuville ha creado un script.

#!/bin/bash

# If you are on Visual Studio for Mac 2017 Turn off automatic package restore before running this script
# If you are on Visual Studio for Mac 2019 you do not need to turn off automatic package restore
# Visual Studio => Preferences => Nuget => General => uncheck the Package Restore box
# Needing to turn restore off isn't necessary in the latest preview releases of VS Mac

# Clean
git clean -dxf

# NuGet restore
msbuild /t:restore Xamarin.forms.sln

# Build XF build tasks
msbuild Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj

# ensure resources are all in sync otherwise first run might cause a Resource ID not found error
msbuild Xamarin.Forms.ControlGallery.Android/Xamarin.Forms.ControlGallery.Android.csproj /t:rebuild

# open in vsmac and should be able to build/run from here
open Xamarin.Forms.sln

NOTA: Visual Studio para macOS soportará MultiTargeting lo que facilitará la compilación de la solución sin script pronto.

Si compilando sigues con problemas, recomiendo:

git clean -xdf

Conociendo el proyecto

La solución de Xamarin.Forms es relativamente grande así que, vamos a ver las claves para que puedas centrar el foco en lo importante.

La solución de Xamarin.Forms

Xamarin.Forms

Cuando se buscan las implementaciones de las abstracciones de controles, bindings , triggers y otro código que no sea específico de plataforma, estos proyectos son la clave.

Xamarin.Forms.Xaml

Extensiones de marcado y otros conceptos clave de XAML lo encontrarás en este proyecto.

Xamarin.Forms.Material

Aquí encontrarás lo necesario relacionado con Visual (de momento Material).

Xamarin.Forms.Maps

Por el nombre probablemente ya sepas que aquí encontrarás a logica del control de mapas.

Platforms

Estos proyectos contienen las implementaciones específicas de cada plataforma. Al agregar nuevos controles o corregir errores relacionados con los controles, aquí es donde efectuará esos cambios.

Control Gallery

Estos proyectos son en realidad una aplicación Xamarin.Forms a modo de galería que incluye todos los controles de Xamarin.Forms disponibles y, lo que es más importante, cuentan con una gran cantidad de UITests de errores previos. Cuando se trabaja en la corrección de un error, o simplemente para investigar cómo se espera que funcione un control, esta aplicación es lo que buscas!.

Xamarin.Forms Control Gallery

Trabajar en una corrección

Ya tienes el proyecto sincronizado y tienes una Issue seleccionada. Ahora si, manos a la obra. Pero antes de comenzar, crea una nueva rama partiendo de lo último disponible en master.

No se requiere cumplir un patrón específico a la hora de crear la rama. Mis recomendaciones serían:

  • Si es un fix, puedes crear una rama “fix{id}” donde el id es el identificador de la Issue.
  • Puedes usar un nombre descriptivo. Por ejemplo, si estas corrigiendo un error al seleccionar un elemento en el ListView podría ser algo como “fix_listview_selectitem”.
  • También puedes usar una combinación de las ideas anteriores. Por ejemplo: “fix_1234_listview_selectitem”.

A la hora de trabajar con el código recuerda cumplir las guías de estilo de código usadas en la .NET Foundation.

Una vez aplicados los cambios necesarios, considera lo siguiente:

  • Si es una corrección, es posible que un test unitario o UITest encaje y ayude a evitar regresiones en el futuro.
  • Si has añadido algo nuevo (por ejemplo, una propiedad nueva al Label), modifica Controls Gallery para reflejar la nueva opción.

El momento de enviar la PR

Añade un título descriptivo junto con una buena descripción (si hay cambios visuales o añadiste algun ejemplo o UITest, añade capturas de pantalla). Si has corregido una issue (o issues) añade el enlace a las mismas.

A continuación, si hay cambios en API añade los detalles. Por último, rellena el checklist indicando si hay UITests, etc.

Enviar PRs

Todo listo para enviar!. Una vez enviado, miembros del equipo así como de la comunidad comenzarán a revisar todo. Una vez pasadas revisiones, builds y otras validaciones. Enhorabuena, tus cambios estarán incluidos en Xamarin.Forms!.

Tras añadir tus cambios en una nueva release, aparecerás nombrado junto a todos los miembros de comunidad que han colaborado al lanzar la versión!.

Gracias

F100s

A la hora de abordar nueva funcionalidad es dificil saber por donde comenzar. Por ese motivo se ha creado la etiqueta F100. Bajo esta etiqueta encontrarás un listado de funcionalidad que cumplen una serie de características:

  • Suele tratarse de funcionalidad no muy amplia y con una buena especificación (explicación detallada, API definida, etc.).
  • El alcance esta bien definido. No hay que modificar o añadir un volumen alto de clases (normalmente).

F100

Si quieres añadir nueva funcionalidad, el listado de F100 es un gran punto de partida!.

Hasta aquí. Creo que hemos realizado un buen resumen de como contribuir en Xamarin.Forms. Además de todo lo expuesto, si puedo ayudar en algo tan solo avísame. Por otro lado, estoy pensando en la idea de hacer algun streaming en Twitch abordando este tema, ¿te resultaría interesante?. Cualquier feedback es bienvenido en los comentarios de la entrada!.

Más información

Anuncios

[Xamarin.Forms] Nueva versión de Xamanimation repleta de novedades

Xamanimation

En todas las plataformas, las aplicaciones móviles incluyen animaciones que otorgan movimiento, fluidez y focalizan la atención del usuario en las zonas deseadas. Actualmente no son un extra o añadido en las aplicaciones, sino una parte importante en la experiencia y usabilidad de las mismas.

Como desarrolladores, debemos no solo cuidar por supuesto el correcto funcionamiento de la aplicación, sino que debemos preocuparnos también por la usabilidad y la experiencia otorgada, donde entran en juego las animaciones.

Xamanimation es una librería destinada para Xamarin.Forms que tiene como objetivo facilitar el uso de animaciones a los desarrolladores. Añade un conjunto de animaciones de uso muy sencillo tanto desde código C# como desde código XAML.

Desde la versión anterior he recibido feedback, sugerencias y peticiones. Las principales son:

  • Poder retrasar animaciones.
  • Poder lanzar animaciones de forma indeterminada (repetir).
  • Poder gestionar facilmente animaciones dependiendes del valor de un scroll, carousel, etc.
  • Parallax.
  • Facilitar lanzar animaciones aún más desde XAML (más Triggers).
  • Transiciones. La principal petición esta relacionada con la navegación.

Peticiones

Con todas las sugerencias y peticiones, había motivos más que suficientes para una actualización de la librería. De esta forma, llega la versión 1.3 de Xamanimation.

Novedades

A continuación, vamos a repasar las novedades principales de la nueva actualización.

Retrasar animaciones

La primera opción añadida es la posibilidad de retrasar animaciones. Se ha incluido la propiedad Delay para poder indicar en milisegundos el tiempo a esperar antes de lanzar una animación.

Retrasar animaciones

Animaciones infinitas

Otra opción muy pedida es la posibilidad de repetir de forma indefinida animaciones. Casos muy típicos como un fondo animado o por ejemplo, en un reproductor de música girar el disco durante la reproducción.

Se ha añadido la propiedad RepeatForever de tipo bool.

Repetir animaciones

Nuevos triggers

Hasta ahora, podíamos crear una animación o StoryBoard y lanzar la animación con un Trigger al cambiar una propiedad o ejecutar un evento directamente desde XAML. Pero, ¿podemos simplificar este proceso?.

Ahora se cuenta con nuevos Triggers que simplifican la ejecución de animaciones.

Más Triggers!

Entre las opciones que tenemos disponibles:

  • AnimateInt
  • AnimateColor
  • AnimateCornerRadius
  • AnimateDouble
  • AnimateThickness

Dependiendo del tipo de de la propiedad, usaremos un Trigger u otro. El uso es sencillo. Indicamos:

  • TargetPropperty: La propiedad a animar.
  • To: El valor final de la propiedad que queremos animar.
  • Duration: Duración de la animación en milisegundos.
  • Delay: Si queremos retrasar la animación. De nuevo, se indica en milisegundos.

NOTA: La propiedad From ya tendrá el valor actual de la propiedad, no es necesario establecer el valor salvo que la animación se quiera realizar desde otro valor diferente.

Animaciones progresivas

De las peticiones recibidas, añadir animaciones progresivas era una de las más repetidas. Hablamos de casos donde animamos una propiedad en base a un valor específico que va cambiando en base a una interacción por parte del usuario.

Por ejemplo, animar propiedades de elementos al hacer scroll para conseguir efecto Parallax, cambiar tamaños de elementos, etc.

Animaciones progresivas

Entre las opciones que tenemos disponibles:

  • AnimateProgressInt
  • AnimateProgressColor
  • AnimateProgressCornerRadius
  • AnimateProgressDouble
  • AnimateProgressThickness

Dependiendo del tipo de de la propiedad, usaremos un Trigger u otro. El uso es sencillo. Indicamos:

  • TargetPropperty: La propiedad a animar.
  • Progress: La propiedad más importante. Es la responsable de controlar la animación.
  • Minimum: Cuando el valor de esta propiedad es igual al de Progress, comienza la animación.
  • Maximum: Cuando el valor de esta propiedad es igual al de Progress, la animación se detiene.
  • From: Valor inicial de la propiedad a animar.
  • To: El valor final de la propiedad que queremos animar.

Veamos un ejemplo para entenderlo mejor:

<xamanimation:AnimateProgressColor
     TargetProperty="VisualElement.BackgroundColor"
     Progress="{Binding ScrollY, Source={x:Reference ScrollBehavior}}" 
     Minimum="0"
     Maximum="200"
     From="Black"
     To="Red"/>

En este caso vamos a animar el color de fondo de un BoxView. El valor ira cambiando poco a poco segun el valor de ScrollY de un ScrollViewer pasando de negro a rojo segun el valor del scroll va aumentando hasta 200.

Transiciones

Otra opción muy solicitada va relacionada con el uso de transiciones. Se han añadido transiciones de entrada.

Este tipo de transición añade una transición en los controles cuando aparecen por primera vez. Puede usar esto en elementos individuales o en Layouts. En el último caso, los elementos hijos se animarán de forma secuencial en lugar de todos al mismo tiempo.

Transiciones de entrada

El uso es sencillo. Utilizamos EntranceTransition pudiendo controlar la duración.

Otros cambios

Además de los cambios anteriores:

  • Se ha actualizado todo a Xamarin.Forms 4.1.
  • El ejemplo hace uso de Shell y Visual ahora.
  • Se han añadido otros Triggers como por ejemplo, para detener animaciones.
  • Correciones menores.

¿Y en siguientes versiones?

Tengo algunas pruebas interesantes realizadas con la navegación. Una de las mejoras en la próxima versión estará relacionada con transiciones entre páginas además de añadir más tipos de transiciones.

Más información

Resumen de #XamarinUIJuly

¿Qué es el #XamarinUIJuly?

Durante todo el mes de Julio hemos ido viendo artículos de Xamarin relacionados con la UI por parte de la comunidad. Siguiendo la idea de Luis Matos, Steven Thewissen organizo la idea de tener un artículo relacionado con la UI por cada día del mes ed Julio. Esto ha sido conocido como #XamarinUIJuly.

Gracias a la iniciativa, día tras día, hemos ido viendo artículos relacionados con animaciones, validaciones de datos, replicar diseños, crear controles, etc. increíble!.

El resumen de #XamarinUIJuly

Para cerrar esta iniciativa, he participado junto a Steven Theweissen y Gerald Versluis en un directo en Twitch para hacer un repaso de cada artículo presentado.

Fue una sesión en directo muy divertida, repasando muchos conceptos y con mucha interacción!.

Gracias

Para cerrar el artículo, y como parte principal del mensaje a trasmitir en el resumen, gracias. Gracias a todos los miembros de la comunidad por participar con artículos, ejemplos, feedback o comentarios. La iniciativa ha sido un éxito, divertida y ha dejado gran variedad de ejemplos y artículos.

Gracias!

¿Y lo próximo?

En el directo en Twitch ya salio esta misma pregunta, ¿que será lo próximo?. Desde ya estoy dandole vueltas a ideas y sin duda habrá mas iniciativas como esta pero, ¿tienes alguna idea o sugerencia?. Si te gustaría un mes dedicado a arquitectura, uso de plugins, creación de custom renderers o algun tema concreto, añade un comentario a esta entrada!.

[Xamarin UI Challenge] Art Plant Mall (English Version)

The challenge

I am going for a new user interface challenge with Xamarin.Forms. In this article, we are going to take as reference a Dribbble design (by JIANGGM), and we will try to replicate it using Xamarin.Forms step by step.

Art Plant Mall

The challenges of the sample

Despite being a nice design, there are no very complex challenges, but we have many small details:

  • List of plants: The CollectionView is not only an important improvement in performance when working with collections, also with different layouts (horizontal listings, GridViews, etc.). In this case we need two columns, something simple now using the CollectionView.
  • Sliding menu from the bottom: The sliding menu is one of the most important parts of the sample. We can achieve this result in a simple way with plugins like SlideOverKit. However, in this sample we are going to create something simple from scratch using a ContentView and animations.
  • Content of the sliding menu: The content of the menu changes depending on two states, if the menu is open or closed. To manage two visual states and the change between them we will use VisualStateManager. When having the menu in the closed or collapsed state we show a horizontal list with the photos (circular images) of the plants that we have in the cart. We can get this easily using Bindable Layouts or CollectionView. Expanding the sliding menu we see a list with the details of the cart. We can use again the CollectionView and even a ListView with a DataTemplateSelector to differentiate between the plants added to the cart and other elements such as delivery costs. The buttons with rounded edges can be obtained with a combination of using Layouts (where the Frame with its CornerRadius acquires the weight) or using a community plugin such as PancakeView that facilitates the management of Layouts with rounded edges, etc.
  • Details of a plant: The details page is easy. The complexity of this view is on the necessary controls that we do not have by default in Xamarin.Forms such as the NumericUpDown or the ToggleButton.
  • Parallax: We can create a Parallax effect in a simple way using a ScrollView and the scroll event with animations (mainly translation).
  • NumericUpDown: We have several options to get the NumericUpDown control. We have the option to use a Custom Renderer or create it using SkiaSharp. We have another option that is to create a custom control using a composition of Xamarin.Forms elements. In this case, we will use a ContentView with a Layout and three Labels. The control will have several BindableProperties to set the value, the minimum value, the maximum value, etc.
  • ToggleButton: We will do something similar to the previous case, we will use a ContentView. Again, we will have BindableProperties to set the status, etc. To get the same visual result, we will use images to define each visual state of the control (Checked or UnChecked).

Plants list

Let’s start with the plants list. For the Layout required (two columns) we will use the CollectionView.

The CollectionView is still marked with experimental flag and can only be used by adding the following line of code in the AppDelegate in the case of iOS, as well as in the MainActivity in Android, before calling Forms.Init:

Forms.SetFlags("CollectionView_Experimental");

The use of the CollectionView will be simple:

<CollectionView
     ItemsSource="{Binding Plants}" />
CollectionView has the ItemsLayout property, which allows to specify the Layout to be used.
<CollectionView.ItemsLayout>
     <GridItemsLayout 
          Orientation="Vertical"
          Span="2" />
</CollectionView.ItemsLayout>

We have used GridItemsLayout specifying the orientation and the number of columns using the Span property.

The result:

El listado de plantas

Sliding menu

Our sliding menu is going to be something very simple… a ContentView. We will place it on the main page, but how do we position it?.

We must have in mind that will expand or collpase the menu using a translation animation on the Y axis.

The initial position of the sliding menu (closed or collapsed state) must be at the bottom and only showing the “header” of the menu.

CartPopup.TranslationY = pageHeight - CartPopup.HeaderHeight;

Simple. We move the menu on the Y axis to the bottom of the page plus the height of the header!.

Once the initial position is established, we need to manage two actions:

  • Expand: We will make a translation on the Y axis from the initial position, to the top edge of the screen less the top of the menu.
  • Collapse: We will return to the starting position. That is, height of the page less the menu header height.

Let’s see the logic:

private void OnExpand()
{
     CartPopupFade.IsVisible = true;
     CartPopupFade.FadeTo(1, ExpandAnimationSpeed, Easing.SinInOut);

     var height = pageHeight - CartPopup.HeaderHeight;
     CartPopup.TranslateTo(0, Height - height, ExpandAnimationSpeed, Easing.SinInOut);
}

private void OnCollapse()
{
     CartPopupFade.FadeTo(0, CollapseAnimationSpeed, Easing.SinInOut);
     CartPopupFade.IsVisible = false;
     CartPopup.TranslateTo(0, pageHeight - CartPopup.HeaderHeight, CollapseAnimationSpeed, Easing.SinInOut);
}

In addition to the translation of the sliding menu, we have added a Grid with a translucent dark background to enhance the opening and closing effect of the menu.

But … and what happens with the menu states?. Right. There are elements visible when the menu is collapsed and others visible when expanding it.

How do we manage this?.

We talk about manage different visual states. There is no case where the use of VisualStateManager fits better!.

We will define visual states in certain elements of the sliding menu (buttons to open and close the menu, lists, etc.):

<VisualStateManager.VisualStateGroups>
     <VisualStateGroup x:Name="CommonStates">
          <VisualState x:Name="Expanded">
               <VisualState.Setters>
                    <Setter Property="IsVisible" Value="False" />
               </VisualState.Setters>
          </VisualState>
          <VisualState x:Name="Collapsed">
               <VisualState.Setters>
                    <Setter Property="IsVisible" Value="True" />
               </VisualState.Setters>
          </VisualState> 
     </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

We have created the two necessary visual states and will manage the change of the state using the GoToState method. For example:

VisualStateManager.GoToState(CollapseButton, "Expanded");

Let’s see the result:

El menu deslizante

Content of the sliding menu

The most interesting part of the sliding menu content is the detailed list of items included in the cart. If we look at the design, in the list we have different elements. We can clearly differentiate between plants and other expenses such as the delivery costs.

To achieve this result, we will use a ListView:

<ListView 
     ItemsSource="{Binding Basket}"
     ItemTemplate="{StaticResource BasketItemDataTemplateSelector}" />

Using a DataTemplateSelector:

public class BasketItemDataTemplateSelector : DataTemplateSelector
{
     public DataTemplate PlantTemplate { get; set; }
     public DataTemplate DeliveryTemplate { get; set; }

     protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
     {
          return ((BasketItem)item).BasketItemType == BasketItemType.Plant ? PlantTemplate : DeliveryTemplate;
     }
}

A template selector is implemented creating a class that inherits from DataTemplateSelector. Next, we use the OnSelectTemplate method to return an element of type DataTemplate.

The result:

El contenido del menu

Plant details

The detail view is based on a composition of Layouts in combination with an Image and several Labels.

Detalles de una planta

Parallax

Despite being a design trend (actually has been used for years on the web and also in mobility development) is quite simple to get a Parallax effect. The effect behaves as a “narrative” complement. We play with the movement and depth to highlight something. In our case, scrolling to the bottom, we will highlight the information of the plant downplaying the header image.

Let’s simplify everything by creating a custom control. It is a class that inherits from ScrollView:

public class ParallaxControl : ScrollView
{
     const float ParallaxSpeed = 2.25f;

     double _height;

     public ParallaxControl()
     {
          Scrolled += (sender, e) => Parallax();
     }

     public static readonly BindableProperty ParallaxViewProperty =
          BindableProperty.Create(nameof(ParallaxControl), typeof(CachedImage), typeof(ParallaxControl), null);

     public View ParallaxView
     {
          get { return (View)GetValue(ParallaxViewProperty); }
          set { SetValue(ParallaxViewProperty, value); }
     }

     public void Parallax()
     {
          if (ParallaxView == null)
               return;

          if (_height <= 0)
               _height = ParallaxView.Height;

          var y = -(int)((float)ScrollY / ParallaxSpeed);

          if (y < 0)
               ParallaxView.TranslationY = y;
          else
               ParallaxView.TranslationY = 0;
     }
}

We are going to move the header image at a lower speed than the rest of the content. To do this, we create a BindableProperty to specify which visual element is the header. In the Scrolled event of the ScrollView, we are going to translate (TranslationY) the header.

The result:

Parallax effect

NOTE: We can improve the result!. In addition to translating the header (the image), we can apply other animations such as increasing the scale or applying Blur effect to the image for example.

NumericUpDown

And we only have to create the two custom controls to complete the UI Challenge!. Let’s start with the NumericUpDown. We can create custom controls using other Xamarin.Forms elements and relying on Bindable Properties to get quite surprising results.

In the case of the NumericUpDown, we will create the control with a Layout and three Labels:

<pancakeview:PancakeView
     HeightRequest="24"
     CornerRadius="24"
     BorderThickness="1"
     BackgroundColor="White"
     BorderColor="Gray">
     <Grid>
          <Grid.ColumnDefinitions>
          <ColumnDefinition Width="Auto" />
          <ColumnDefinition Width="*" />
          <ColumnDefinition Width="Auto" />
          </Grid.ColumnDefinitions>
          <Label 
               x:Name="MinusButton"
               Grid.Column="0"
               Text="-"
               Margin="12, 0, 0, 0">
               <Label.GestureRecognizers>
                    <TapGestureRecognizer 
                         Tapped="MinusTapped" />
               </Label.GestureRecognizers>
          </Label>
          <Label 
               x:Name="ValueText"
               Grid.Column="1" />
          <Label 
               x:Name="PlusButton"
               Grid.Column="2"
               Text="+"
               Margin="0, 0, 12, 0">
               <Label.GestureRecognizers>
                    <TapGestureRecognizer 
                         Tapped="PlusTapped" />
               </Label.GestureRecognizers>
          </Label>
     </Grid>
</pancakeview:PancakeView>

In the code-behind of the control, we will have some Bindable Properties like:

  • Minimum
  • Maximum
  • Value
  • Step
public static readonly BindableProperty ValueProperty =
     BindableProperty.Create(nameof(Value), typeof(double), typeof(NumericUpDown), 1.0,
     propertyChanged: (bindable, oldValue, newValue) =>
     ((NumericUpDown)bindable).Value = (double)newValue,
     defaultBindingMode: BindingMode.TwoWay
);

public double Value
{
     get { return (double)GetValue(ValueProperty); }
     set { SetValue(ValueProperty, value); }
}

...

And we would use it in the following way:

<controls:NumericUpDown
     Minimum="1"
     Maximum="10"/>

NOTE: To have a reusable control we should go further. We would include properties to customize the control (Animate, NumericBackgroundColor, NumericBorderColor, NumericTextColor, NumericBorderThickness, NumericCornerRadius) as well as events (ValueChanged) and/or commands. In this case we have only added the basic properties necessary to replicate the UI.

Do you want to see the result?.

NumericUpDown

ToggleButton

The case of the ToggleButton will be extremely similar to the previous control. We will use a ContentView with some BindableProperties:

public static readonly BindableProperty CheckedImageProperty =
     BindableProperty.Create("CheckedImage", typeof(ImageSource), typeof(ToggleButton), null);

public ImageSource CheckedImage
{
     get { return (ImageSource)GetValue(CheckedImageProperty); }
     set { SetValue(CheckedImageProperty, value); }
}

public static readonly BindableProperty UnCheckedImageProperty =
     BindableProperty.Create("UnCheckedImage", typeof(ImageSource), typeof(ToggleButton), null);

public ImageSource UnCheckedImage
{
     get { return (ImageSource)GetValue(UnCheckedImageProperty); }
     set { SetValue(UnCheckedImageProperty, value); }
}

In this case, we do not use XAML since the visual appearance will be defined in the images of the CheckedImage and UnCheckedImage properties.

We manage the visual state and other properties when changing the value of the Checked property:

private static async void OnCheckedChanged(BindableObject bindable, object oldValue, object newValue)
{

}

And we would use it in the following way:

<controls:ToggleButton
     Checked="False"
     Animate="True"
     CheckedImage="fav.png"
     UnCheckedImage="nofav.png"/>

The result:

El resultado final

The sample is available on GitHub:

Ver GitHub

We have a UI Challenge not excessively complex but with a great variety of details where we have used:

  • Animations
  • Creating custom controls
  • Navigation
  • Styles
  • Parallax
  • PancakeView (shadows, corner radius, etc.)
  • VisualStateManager
  • Etc.

This article has been part of the #XamarinUIJuly initiative:

 

#XamarinUIJuly

It is fantastic to be able to contribute to the #XamarinUIJuly. Ooh, and if this article has been interesting for you, we are only on July 17, do not miss the rest of the incredible articles that will come out during the rest of the month!.

Más información

[Xamarin UI Challenge] Art Plant Mall

El reto

Volvemos a por un reto de interfaz de usuario con Xamarin.Forms. En este artículo, vamos a tomar como referencia un diseño de Dribbble (por JIANGGM), que intentaremos replicar con paso a paso.

Art Plant Mall

Vamos a intentar replicar la UI del diseño paso a paso en Xamarin.Forms.

Los retos del ejemplo

A pesar de ser un diseño atractivo, no hay retos muy complejos en el diseño, si tenemos muchos pequeños detalles:

  • Listado de plantas: La llegada de CollectionView es no solo una mejora en el rendimiento a la hora de trabajar con colecciones, también con diferentes Layouts (listados horizontales, GridViews, etc.). En este caso necesitamos dos columnas, algo sencillo gracias al CollectionView.
  • Menu deslizante desde la parte inferior: El menu desplizante es una de las claves del ejemplo. Podemos conseguir este resultado de forma sencilla con plugins como SlideOverKit. Sin embargo, en este ejemplo vamos a crear algo sencillo desde cero utilizando una ContentView y animaciones.
  • Contenido del menu deslizante: El contenido del menu varia dependiendo de dos estados, si el menu esta abierto o cerrado. De entrada, para gestionar dos estados y el cambio usaremos VisualStateManager. Al tener el menu en el estado cerrado o colapsado mostramos un listado horizontal con las fotos (circulares) de las plantas que tenemos en el carrito. Esto lo podemos conseguir facilmente usando Bindable Layouts o CollectionView. Al expandir el menu deslizante vemos un listado con los detalles del carrito. Podemos usar de nuevo el CollectionView e incluso un ListView con un DataTemplateSelector para diferenciar entre las plantas añadidas al carrito y otros elementos como los gastos de envio. Los botones con bordes redondeados los podemos conseguir con una combinación de uso de Layouts (donde el Frame con su CornerRadius adquiere el peso) o bien, usar algun plugin de comunidad como PancakeView que nos facilite la gestión de Layouts con bordes redondeados, etc.
  • Detalles de una planta: La página de detalles se puede conseguir fácilmente. La complejidad de esta vista recae en los controles necesarios que no tenemos por defecto en Xamarin.Forms como son el NumericUpDown o el ToggleButton.
  • Parallax: A pesar que desde la imagen estática del diseño no podemos saber si la experiencia diseñada usada el efecto Parallax…personalmente al ver el diseño me encajaba perfectamente!. Podemos crear un efecto Parallax de forma sencilla usando un ScrollView, su evento al realizar desplazamiento del scroll y animaciones (princpalmente de translación).
  • NumericUpDown: Tenemos varias opciones para conseguir el control NumericUpDown. Tenemos la opción de usar Custom Renderer o crearlo usando SkiaSharp. Tenemos otra opción que es crear un control personalizado utilizando una composición de elementos de Xamarin.Forms. En este caso, vamos a usar un ContentView combinando un Layout con tres Labels. El control contará con varias BindableProperties para establecer el valor, el valor mínimo, máximo, etc.
  • ToggleButton: Vamos a realizar algo similar al caso anterior, usaremos una ContentView. Contaremos con BindableProperties para establecer el estado, etc. Para conseguir el mismo resultado, la clave será usar imágenes para definir cada estado visual del control (Checked o UnChecked).

Listado de plantas

Comenzamos con el listado principal con las plantas en ventas en la tienda. Por el tipo de Layout requerido (dos columnas) vamos a utilizar el CollectionView.

El CollectionView sigue marcado con estado experimental y solo se puede usar agregando la siguiente línea de código en el AppDelegate en el caso de iOS, así como en la MainActivity en Android, antes de llamar a Forms.Init:

Forms.SetFlags("CollectionView_Experimental");

El uso del CollectionView será sencillo:

<CollectionView
     ItemsSource="{Binding Plants}" />

CollectionView cuenta con  la propiedad ItemsLayout, del tipo IItemsLayout , que permite especificar el Layout que se usará.

<CollectionView.ItemsLayout>
     <GridItemsLayout 
          Orientation="Vertical"
          Span="2" />
</CollectionView.ItemsLayout>

Hemos usado GridItemsLayout especificando la orientación y el número de columnas gracias a la propiedad Span.

El resultado:

El listado de plantas

Menu dezlizante desde la parte inferior

Nuestro menu deslizante va a ser algo muy sencillo…una ContentView. La situaremos en la página principal aunque, ¿cómo la posicionamos?.

Hay que tener en cuenta que el efecto de expandir y contraer lo vamos a conseguir utilizando animaciones de translación en el eje Y.

La posición inicial del menu deslizante (cerrado o colapsado) debe ser en la parte inferior solo mostrando la “cabecera” del menu.

CartPopup.TranslationY = pageHeight - CartPopup.HeaderHeight;

Sencillo. Transladamos el menu en el eje Y el alto de la página menos el alto de la cabecera!.

Una vez establecida la posición inicial necesitamos controlar dos acciones:

  • Expandir: Haremos una translación en el eje Y desde la posición inicial, al borde superior de la pantalla menos el alto del menu.
  • Contraer: Volveremos a la posición inicial. Es decir, alto de la página menos alto de la cabecera.

Veamos la lógica:

private void OnExpand()
{
     CartPopupFade.IsVisible = true;
     CartPopupFade.FadeTo(1, ExpandAnimationSpeed, Easing.SinInOut);

     var height = pageHeight - CartPopup.HeaderHeight;
     CartPopup.TranslateTo(0, Height - height, ExpandAnimationSpeed, Easing.SinInOut);
}

private void OnCollapse()
{
     CartPopupFade.FadeTo(0, CollapseAnimationSpeed, Easing.SinInOut);
     CartPopupFade.IsVisible = false;
     CartPopup.TranslateTo(0, pageHeight - CartPopup.HeaderHeight, CollapseAnimationSpeed, Easing.SinInOut);
}

Además de la translación del menu deslizante, hemos añadido un Grid con el fondo oscuro translúcido para potenciar el efecto de apertura y cierre del menu.

Pero…¿y qué ocurre con los estados del menu?. Cierto. Hay elementos visibles al estar el menu colapsado y otros visibles al expandir el mismo.

¿Cómo gestionamos esto?.

Hablamos de gestionar diferentes estados visuales. No hay caso donde encaje mejor el uso de VisualStateManager.

Vamos a definir estados visuales en ciertos elementos del menu deslizante (botones de abrir y cerrar el menu, listados, etc.):

<VisualStateManager.VisualStateGroups>
     <VisualStateGroup x:Name="CommonStates">
          <VisualState x:Name="Expanded">
               <VisualState.Setters>
                    <Setter Property="IsVisible" Value="False" />
               </VisualState.Setters>
          </VisualState>
          <VisualState x:Name="Collapsed">
               <VisualState.Setters>
                    <Setter Property="IsVisible" Value="True" />
               </VisualState.Setters>
          </VisualState> 
     </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

Hemos creado los dos estados visuales que necesitamos. La gestión del cambio de estado la realizaremos utilizando el método GoToState. Por ejemplo:

VisualStateManager.GoToState(CollapseButton, "Expanded");

Veamos el resultado:

El menu deslizante

Contenido del menu deslizante

De todo el contenido del menu deslizante, la parte más interesante es el listado detallado de elementos incluidos en el carrito. Si nos fijamos en el diseño, en el listado tenemos elementos diferentes. Podemos diferenciar claramente entre plantas y otros gastos como el envio.

Para conseguir este resultado, vamos a utilizar un ListView:

<ListView 
     ItemsSource="{Binding Basket}"
     ItemTemplate="{StaticResource BasketItemDataTemplateSelector}" />

Junto a un DataTemplateSelector:

public class BasketItemDataTemplateSelector : DataTemplateSelector
{
     public DataTemplate PlantTemplate { get; set; }
     public DataTemplate DeliveryTemplate { get; set; }

     protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
     {
          return ((BasketItem)item).BasketItemType == BasketItemType.Plant ? PlantTemplate : DeliveryTemplate;
     }
}

Un selector de plantillas se implementa mediante la creación de una clase que hereda de DataTemplateSelector. Después, utilizamos el método OnSelectTemplate para devolver un elemento de tipo DataTemplate.

El resultado:

El contenido del menu

Detalles de una planta

La vista de detalles (a falta de crear los controles NumericUpDown y ToggleButton) se basa en una composición de Layouts en combinación con una Image y varios Labels.

El resultado:

Detalles de una planta

Parallax

A pesar de ser una tendencia (en realidad lleva ya usandose hace años en web y también en movilidad) es bastante sencillo de conseguir un efecto de tipo Parallax. El efecto se comporta con un complemento “narrativo”. Jugamos con el movimiento y la profundidad para resaltar algo. En nuestro caso, segun se haga scroll hacia la parte inferior, vamos a resaltar la información de la planta quitando peso a la imagen de la misma.

Vamos a simplificarlo todo creando un control personalizado. Se trata de una clase que hereda de ScrollView:

public class ParallaxControl : ScrollView
{
     const float ParallaxSpeed = 2.25f;

     double _height;

     public ParallaxControl()
     {
          Scrolled += (sender, e) => Parallax();
     }

     public static readonly BindableProperty ParallaxViewProperty =
          BindableProperty.Create(nameof(ParallaxControl), typeof(CachedImage), typeof(ParallaxControl), null);

     public View ParallaxView
     {
          get { return (View)GetValue(ParallaxViewProperty); }
          set { SetValue(ParallaxViewProperty, value); }
     }

     public void Parallax()
     {
          if (ParallaxView == null)
               return;

          if (_height <= 0)
               _height = ParallaxView.Height;

          var y = -(int)((float)ScrollY / ParallaxSpeed);

          if (y < 0)
               ParallaxView.TranslationY = y;
          else
               ParallaxView.TranslationY = 0;
     }
}

Vamos a desplazar la imagen de la cabecera a una velocidad inferior al resto del contenido. Para ello, creamos una BindableProperty para especificar que elemento visual es la cabecera. En el evento Scrolled del ScrollView, vamos a transladar (TranslationY) la cabecera.

El resultado:

Parallax effect

NOTA: Podemos mejorar el resultado!. Además de transladar la cabecera (la imagen de la cabecera), podemos aplicar otras animaciones como aumentar la escala o aplicar efecto tipo Blur a la imagen por ejemplo.

NumericUpDown

Y solo nos queda por completar los dos controles personalizados. Vamos a comenzar por el NumericUpDown. Podemos crear controles compuestos utilizando otros elementos de Xamarin.Forms y apoyándonos en Bindable Properties para conseguir resultados bastante sorprendentes.

En el caso del NumericUpDown, vamos a crear el control con un Layout y tres Labels:

<pancakeview:PancakeView
     HeightRequest="24"
     CornerRadius="24"
     BorderThickness="1"
     BackgroundColor="White"
     BorderColor="Gray">
     <Grid>
          <Grid.ColumnDefinitions>
          <ColumnDefinition Width="Auto" />
          <ColumnDefinition Width="*" />
          <ColumnDefinition Width="Auto" />
          </Grid.ColumnDefinitions>
          <Label 
               x:Name="MinusButton"
               Grid.Column="0"
               Text="-"
               Margin="12, 0, 0, 0">
               <Label.GestureRecognizers>
                    <TapGestureRecognizer 
                         Tapped="MinusTapped" />
               </Label.GestureRecognizers>
          </Label>
          <Label 
               x:Name="ValueText"
               Grid.Column="1" />
          <Label 
               x:Name="PlusButton"
               Grid.Column="2"
               Text="+"
               Margin="0, 0, 12, 0">
               <Label.GestureRecognizers>
                    <TapGestureRecognizer 
                         Tapped="PlusTapped" />
               </Label.GestureRecognizers>
          </Label>
     </Grid>
</pancakeview:PancakeView>

En el code-behind del control, tendremos Bindable Properties para definir:

  • Minimum
  • Maximum
  • Value
  • Step

Por ejemplo:

public static readonly BindableProperty ValueProperty =
     BindableProperty.Create(nameof(Value), typeof(double), typeof(NumericUpDown), 1.0,
     propertyChanged: (bindable, oldValue, newValue) =>
     ((NumericUpDown)bindable).Value = (double)newValue,
     defaultBindingMode: BindingMode.TwoWay
);

public double Value
{
     get { return (double)GetValue(ValueProperty); }
     set { SetValue(ValueProperty, value); }
}

...

Su uso sería muy sencillo:

<controls:NumericUpDown
     Minimum="1"
     Maximum="10"/>

NOTA: Para tener un control reutilizable deberíamos llegar más lejos. Nos faltarían propiedades para personalizar el control (Animate, NumericBackgroundColor, NumericBorderColor, NumericTextColor, NumericBorderThickness, NumericCornerRadius) así como enventos (ValueChanged) y/o comandos. En este caso tan solo hemos añadido las propiedades básicas necesarias para replicar la UI.

¿Quieres ver el resultado?.

NumericUpDown

ToggleButton

El caso del ToggleButton va a ser sumamente similar al anterior control. Usaremos una ContentView con un conjunto de BindableProperties:

public static readonly BindableProperty CheckedImageProperty =
     BindableProperty.Create("CheckedImage", typeof(ImageSource), typeof(ToggleButton), null);

public ImageSource CheckedImage
{
     get { return (ImageSource)GetValue(CheckedImageProperty); }
     set { SetValue(CheckedImageProperty, value); }
}

public static readonly BindableProperty UnCheckedImageProperty =
     BindableProperty.Create("UnCheckedImage", typeof(ImageSource), typeof(ToggleButton), null);

public ImageSource UnCheckedImage
{
     get { return (ImageSource)GetValue(UnCheckedImageProperty); }
     set { SetValue(UnCheckedImageProperty, value); }
}

En este caso, no hacemos uso de XAML ya que la apariencia visual será lo que se defina en las imágenes de las propiedades CheckedImage y UnCheckedImage.

La gestión de estado, cambio de imagen y otras acciones las realizamos al cambiar el valor de la propiedad Checked:

private static async void OnCheckedChanged(BindableObject bindable, object oldValue, object newValue)
{

}

De modo que, el uso del control es simple:

<controls:ToggleButton
     Checked="False"
     Animate="True"
     CheckedImage="fav.png"
     UnCheckedImage="nofav.png"/>

El resultado:

El resultado final

¿Qué te parece?.

En cuanto al ejemplo realizado, esta disponible en GitHub:

Ver GitHub

Llegamos hasta aquí. Estamos ante un UI Challenge no excesivamente complejo pero con una gran variedad de detalles donde hemos utilizado:

  • Animaciones
  • Creación de controles personalizados
  • Navegación
  • Estilos
  • Parallax
  • VisualStateManager
  • Etc.

Este artículo ha sido parte de la iniciativa XamarinUIJuly:

#XamarinUIJuly

Un verdadero placer poder contribuir en una iniciativa tan interesante. Ah, si te ha resultado interesante este artículo, estamos a sólo 17 de Julio no te pierdas el resto de increíbles artículos que saldrán en el resto del mes!.

Más información

Probando Xamarin.Forms XAML Hot Reload

Iterar en UI

Si, conozco esa sensación. Hablo de los segundos esperando a desplegar la App de nuevo en un emulador o dispositivo para probar los últimos cambios en UI trabajando con Xamarin.Forms. Realizando la misma tarea varias veces se vuelve en algo que podría/debería ser más eficiente.

En el pasado Xamarin Developer Summit se ha anunciado Xamarin.Forms XAML Hot Reload.

Xamarin.Forms XAML Hot Reload

Llega Xamarin.Forms XAML Hot Reload

El objetivo principal de Hot Reload es permitir ver cambios de XAML al vuelo sin necesidad de compilar y desplegar constantemente. Las características fundamentales son:

  • Permite ver los cambios de XAML al vuelo una vez desplegada la App en modo depuración.
  • Funciona en Visual Studio 2019 tanto en Windows como macOS.
  • Funciona tanto para iOS como Android.
  • Soporta emuladores como dispositivos físicos.
  • Soporta plugins, librerías de terceros, SkiaSharp, etc.

¿Cómo funciona?

Actualmente al trabajar iterando interfaces en Xamarin.Forms el ciclo consiste en hacer un cambio en XAML, compilar, desplegar y depurar. Ante cualquier otro cambio, volver a repetir todo el proceso.

XAML Hot Reload llega para ayudar reducir drásticamente los pasos y tiempos necesarios para iterar en UIs. No hace falta hacer nada especial para usar XAML Hot Reload. Sencillamente, compila y despliega tu App como lo harías normalmente y a partir de ese momento solo tienes que hacer cualquier cambio en XAML. Es decir, tampoco es necesario instalar ningun paquete NuGet adicional en la App o realizar cualquier lógica de incialización en la misma.

Esto es así porque XAML Hot Reload usa el depurador y no una conexión de red, asi que funciona con o sin conectividad. Edita XAML y guarda los cambios. Al volver a cargar, se mantendrá el estado de navegación. Si usas el patrón MVVM, el estado de la interfaz de usuario vinculado a la ViewModel permanecerá intacto.

Suena genial, ¿verdad?. Veamoslo en funcionamiento. Se soportan cambios en cualquier tipo de archivo XAML, es decir, se soportan páginas, ContentView’s, App, recursos o Shell.

Para las pruebas he usado el ejemplo RottenUI de Kym Phillpotts. Es un ejemplo visualmente atractivo pero además:

  • Hace uso de estilos a nivel de App y de página.
  • Hace uso de ContentView definido en otro archivo.
  • Usa plugins de comunidad como PancakeView.
  • Usa SkiaSharp.

Comenzamos haciendo cambios en la página principal:

Cambios a nivel de página

Continuamos haciendo cambios en una ContentView usada en la página pero definida en otro archivo:

ContentView usada en la página

Y también a nivel de App:

Cambios a nivel de App

Y todo…en modo avión sin conexión a internet, etc.

Sin internet!

Preview privada

Si quieres probar XAML Hot Reload, desde ya, puedes registrarte para obtener una Preview. Puede encontrar toda la información  aquí: https://devblogs.microsoft.com/xamarin/xaml-hot-reload.

Se estan añadiendo a grupos por lotes, así que no te preocupes si no puedes obtenerlo de forma inmediato. Y recuerda; cualquier feedback es importante para continuar mejorando.

Estoy seguro de que Hot Reload hará que tu vida desarrollando Xamarin.Forms mucho mejor.

Happy reloading!

Más información

[Material DotNet2019] Optimizar Apps con Xamarin.Forms

El evento

El pasado 19 de Junio tenía lugar la DotNet2019. Un gran día repleto de sesiones técnicas cubriendo todo el espectro de tecnologías .NET, desde Xamarin, Azure, IoT, VR, etc.

Fue un día genial de grandes sesiones pero también de encontrarte con  excompañeros, grandes amigos, desvirtualizar a nuevos amigos, networking, etc. Quisiera agradecer a todos los ponentes y asistentes por hacer posible el evento. Y por supuesto, mi enhorabuena a toda la organización por hacer que algo tan grande y complejo fluyese de forma tan natural y sencilla.

El Material

He tenido en placer de poder participar en el evento con una sesión acerca de Xamarin.Forms y formas de obtener el mejor rendimiento posible.

En esta sesión vimos siempre con forma de gráficas, diferentes medidas, etc.

  • Uso de XAMLC.
  • Uso de Fast Renderers y comparativa de rendimiento.
  • Rendimiento de CollectionView en comparación al ListView.
  • Compresión de Layouts.
  • Consejos al utilizar Layouts.
  • Consejos al utilizar imágenes.
  • Consejos al crear Custom Renderers.
  • Revisión del rendimiento usando Visual o Shell.
  • Etc.

Puedes encontrar todos los ejemplos, gráficas y comparativas en GitHub:

Ver GitHub

Hasta el año próximo!

Más información

Xamarin UI Julio

Introducción

Recientemente hablé con Steven Thewissen que quería volver a promover la idea de Snppts. También hablamos de que sería genial tener más y más comunidad haciendo ejemplos con interfaces atractivas con Xamarin.Forms. De esta forma, y basandose en idea de Luis Matos que en Febrero hizo el mes de Xamarin, de la mano de Steven nace el Xamarin UI de Julio.

Xamarin UI de Julio

El objetivo es lograr publicar durante todo el mes de julio un artículo de parte de la comunidad de Xamarin. Cada uno de los  colaboradores lo publicará en su propio blog, y luego estará disponible en un calendario común. De esa manera, entre todos crearemos una publicación sorpresa  diaria. Las publicaciones del blog se escribirán en inglés y tratarán sobre un tema concreto.
Suena bien, pero ¿cuál es el tema?.

La creación de interfaces de usuario es una parte esencial de la creación de una aplicación. En actualizaciones recientes, Xamarin.Forms ha agregado más y más características relacionadas como Visual y Shell, y controles adicionales como CollectionView para permitir a los desarrolladores crear aplicaciones con mejor aspecto.  Así que el tema fundamental será crear interfaces de usuario con Xamarin.Forms.

¿No sabes de que escribir?. No te preocupes, sabemos que es un tema bastante amplio. Si tienes dificultades para encontrar inspiración, a continuación algunas ideas:

  • Animaciones.
  • Empezando con Shell.
  • Utilizar y/o personalizar Visual.
  • Interfaz avanzada con CollectionView,
  • Herramientas a usar (generación de iconos, LiveXAML, MFractor, etc.)
  • Recreando una interfaz de usuario existente con Xamarin.
  • Construyendo un control personalizado.
  • Etc.

¿Cuáles son los pasos a seguir para participar?

  1. Reserva un slot en Twitter (usando los hashtags #Xamarin y #XamarinUIJuly).
  2. Escribe tu blog, en inglés (animo a toda la comunidad, si necesitas ayuda con ello, puedo ayudar).
  3. Publica tu artículo en tu blog en la fecha especificada (según la fecha elegida en el calendario).
  4. Publica el enlace a tu blog en Twitter con los hashtags #Xamarin #XamarinUIJuly.

Sencillo, ¿verdad?.

Más información

[Xamarin.Forms] Mejorar el rendimiento en el tiempo de arranque

Introducción

Además de cuidar detalles como la funcionalidad o la apariencia visual de nuestra aplicación, nuestra aplicación debe funcionar correctamente bajo todas las condiciones en todos los dispositivos para la que sea lanzada.

Un punto importante a revisar siempre suele ser, el tiempo de arranque de la aplicación.

¿Conoces conceptos como AOT?, ¿fast renderers?. En este artículo vamos a repasar algunos conceptos que podemos aplicar para tener un impacto positivo en los tiempos de arranque de la aplicación.

XAMLC

Si defines la interfaz de usuario de la aplicación Xamarin.Forms con XAML tienes la opción de utilizar XamlCompilationOptions.

Cuenta con dos valores:

Compile.

  • Acelera la carga de elementos visuales.
  • Reduce el tamaño del paquete.
  • La compilación (AOT) es más larga.

Skip.

  • Valor por defecto para mantener retocompatibilidad con versiones antiguas de Xamarin.Forms.
  • No hay validación en tiempo de ejecución de XAML.

Ejemplo de uso:

[assembly: XamlCompilation(XamlCompilationOptions.Compile)]

La diferencia:

Rendimiento con XAMLC

NOTA: Resultado obtenido tras 5 mediciones utilizando Android.

Fast Renderers

Hablamos de cambios realizados en Xamarin.Forms con el objetivo de reducir a mínimos el número de operaciones y cálculos a realizar para renderizar el control y gestionar su tamaño y posición.

Hasta ahora, la mayoría de los renderers en Android se componían de dos vistas:

  • Control nativo, como Button.
  • Un contenedor ViewGroup que se encargaba de controlar aspectos como los gestos, etc.

Sin embargo, este enfoque tiene una implicación directa en el rendimiento al crear dos elementos por cada control, lo que resulta en un árbol visual más complejo, requiere más memoria y más capacidad de procesamiento para representar en la pantalla.

Los Fast Renderers reducen la inflación y los tiempos para el renderizado de un control de Xamarin.Forms.

Veamos un ejemplo de jerarquía de elementos sin utilizar Fast Renderers:

Jerarquía sin utilizar Fast Renderers

Y utilizando Fast Renderers:

Jerarquía utilizando Fast Renderers

La diferencia:

Rendimiento con Fast Renderers

Los Fast Renderers están disponibles para los siguientes controles de Xamarin.Forms en Android:

  • Button
  • Image
  • Label
  • Frame

Desde Xamarin.Forms 4.0 se utilizan por defecto los Fast Renderers. Si quieres no utilizarlos, puedes hacerlo:

Forms.SetFlags("UseLegacyRenderers");

Configuración de la App

La opción compilación AOT habilita la compilación Ahead Of Time de los ensamblados. Cuando esta opción está habilitada, la sobrecarga de inicio Just-In-Time (JIT) se minimiza al precompilar ensamblados antes del tiempo de ejecución. El código nativo resultante se incluye en el paquete (APK) junto con los ensamblados sin compilar. Esto da como resultado un tiempo de inicio de la aplicación más reducido, pero a costa de tamaños APK más grandes.

A costa de tiempos de compilación más lentos, el compilador de optimización de LLVM debe crear un código compilado más pequeño y rápido (en las pruebas, efectivamente conseguimos un paquete más ligero aunque no tenemos reducción en lso tiempos de arranque de la aplicación).

A continuación, una comparativa del tiempo de arranque de la aplicación en milisegundos entre el uso de JIT, AOT y AOT junto con la opciónde optimización de LLVM:

Rendimiento con diferentes configuraciones

Como se puede observar, la reducción de tiempo usando AOT es notoria pero…¿cómo afecta al tamaño del paquete?.

Comparativa del peso del paquete

Puedes encontrar los ejemplos utilizados así como archivos Excel con las medidas en GitHub:

Ver GitHub

Más información

[Xamarin.Forms] Mejorar el rendimiento al trabajar con imágenes

Introducción

El uso de imágenes en aplicaciones móviles es habitual. Es un elemento clave a la hora de conseguir buenos resultados visuales. Sin embargo, también suelen ser uno de los puntos clave que impactan en el rendimiento de la aplicación. Problemas como:

  • Alto consumo de memoria.
  • Bloqueo de la UI (carga de imágenes en el hilo de UI, etc).
  • Etc.

En este artículo, vamos a realizar comparaciones en el rendimiento del control Image de Xamarin.Forms entre diferentes versiones, así como una comparativa con FFImageLoading y GlideX.

Para realizar las comaparativas vamos a necesitar un ejemplo y algo de código para obtener información. Nuestro ejemplo va a ser una versión modificada del gran ejemplo realizado por Jonathan Peppers en Glidex.

El ejemplo

A la hora de medir, vamos a utilizar:

System.Diagnostics.Process.GetCurrentProcess().WorkingSet64

Para obtener la cantidad de bytes que se estan consumiendo.

Imágenes en Xamarin.Forms

El control Image de Xamarin.Forms permite la carga de imágenes desde diferentes fuentes (URL, recurso incrustrado, etc.). El rendimiento general es correcto aunque con mejoras (mejoras en la cache, reutilizar recursos, etc.).

Desde la versión 4.0 de Xamarin.Forms se han comenzado a realizar mejoras en la gestión de imágenes. Pero…¿realmente tenemos mejora?.

Para contestar de forma correcta, vamos a cargar 100 imágenes en un Grid (evitamos usar listados y reutilización de celdas para conseguir tener un volumen elevado de imágenes en el árbol visual y en memoria).

Lanzamos la aplicación tanto utilizando la versión 3.6 como la versión 4.0. Tras lanzar la aplicación 5 veces y hacer la media de la memoria consumida:

Mejoras en la gestión de imágenes en Xamarin.Forms 4.0

Xamarin.Forms 4.0 consume una media de un 15% menos de memoria con respecto a la versión 3.6. Una buena mejora en la gestión de imágenes pero…¿podemos conseguir mejorar más al gestionar las imágenes?.

FFImageLoading

FFImageLoading es una de las librerías de la comunidad más usadas y más recomendadas. Esta librería tiene como objetivo cargar imágenes de la forma más eficiente posible. Entre las características principales (y que tienen impacto en el rendimiento):

  • Cache de imágenes en memoria y disco.
  • Múltiples imágenes usando el mismo origen (url, ruta, recurso) usarán solo un mapa de bits que se almacena en caché en memoria (menos uso de memoria).
  • Placeholders de carga y error.
  • Las imágenes se pueden ajustar automáticamente a un tamaño especificado (menos uso de memoria).
  • Etc.

Tras realizar benchmarking entre FFImageLoading y el control Image de Xamarin.Forms, gracias a las opciones de cache, ajuste de tamaño, etc., obtenemos mejor rendimiento con FFImageLoading.

GlideX

GlideX.Forms nos permite utilizar Glide (Una librería de carga y almacenamiento de imágenes para Android enfocada en el rendimiento) en Xamarin.Forms Android.

Al igual que anteriormente, hemos realizado benchmarking en Android entre el control Image de Xamarin.Forms, FFImageLoading y GlideX. GlideX es la opción más óptima en Android superando levemente a FFImageLoading y con una diferencia más considerable con respecto al control Image.

IImageSourceHandler

Así que, GlideX es más eficiente en Android pero…¿que usamos en iOS?. Podríamos utilizar FFImageLoading directamente en ambas plataformas, es una buena opción. Sin embargo, podríamos usar FFImageLoading en iOS y GlideX en Android.

Desde Xamarin.Forms 2.3.5, tenemos la interfaz IImageSourceHandler. Permite implementar ImageSource en la plataforma.

Ejemplo básico (en iOS):

public class ImageSourceHandler : IImageSourceHandler
{
     public Task<UIImage> LoadImageAsync(
          ImageSource imageSource,
          CancellationToken cancellationToken = new CancellationToken(),
          float scale = 1)
     {
          ...
     }
}

Podemos gestionar la fuente de imágenes de forma sencilla, utilizando en cada plataforma la opción que necesitemos. Jean-Marie Alfonsi ha creado Xamarin.Forms.ImageSourceHandlers con esta misma idea. Su uso es sencillo.

En Android, vamos a utilizar GlideX. Necesitaremos la inicialización de la librería.

Android.Glide.Forms.Init();

Mientras que en iOS, vamos a usar FFImageLoading:

FFImageLoading.FormsHandler.Init();

Benchmarking

Llegados a este punto, tenemos todo lo necesario para obtener datos y sacar algunas conclusiones. La gestión de imágenes es un punto importante a la hora de crear aplicaciones móviles. En Xamarin.Forms se continua mejorando y lo podemos ver en los datos obtenidos comparando la versión 3.6 y la versión 4.0.

A continuación, una comparativa en Android de la misma aplicación cargando 100 imágenes utilizando el control Image de Xamarin.Forms, FFImageLoading y GlideX:

La comparativa

NOTA: Los datos salen de la media de 5 medidas.

Puedes encontrar los ejemplos utilizados así como archivos Excel con las medidas en GitHub:

Ver GitHub

Otras recomendaciones

Podemos aplicar otra serie de acciones para mejorar considerablemente el rendimiento a la hora de trabajar con imágenes:

  • Evitar la necesidad de reducir el tamaño de imágenes (memoria consumida) en el dispositivo. Si tenemos control sobre el servidor proveedor de las imágenes, gestionar las imágenes en el mismo.
  • Además del punto anterior, reducir las imágenes locales utilizadas. Podemos conseguirlo de varias formas. Controlando el tamaño (o formato) de la imagen,  o bien, se pueden utilizar servicios como tinypng.

Más información