Customizing Your Xamarin.Forms Apps with Animations
Animations are an important part of mobile UIs, and they're a great way to make a mobile app both more functional and visually interesting. They have always been a part of ComponentOne Studio for Xamarin, but there are tools for adding your own animations into your apps as well. Xamarin.Forms allows you to very easily provide animations in your applications using ViewExtensions. In this article, we’ll look at how you can use the ViewExtensions to customize your apps with animations.
Animations in Xamarin
Xamarin provides its own API infrastructure for creating animations that can create both simple and complex animations across platforms. Generally the animations will progressively change a property from one value to another over a given duration, and Xamarin has several animation topics in their documentation that are an excellent place to start. Basic animations give you the option to scale, rotate, fade, and translate (move along x or y) VisualElements in Xamarin.Forms. This lets you easily add animations to almost everything.
Animations are all asynchronous, cancellable, and Task based, and you can use the await operator to determine if an animation completes since they’ll return a Task<bool>
(which is false if the animation complete or true otherwise). Using the await operator also allows you to create sequential animations where each animation waits on the execution of the proceeding. Of course, you can also use animations without the await operator if you prefer that they execute in the background.
Using these animations is quite easy. To perform a scale animation for a Label for instance, all one needs to do is use the ScaleTo method:
MyLabel.ScaleTo(2, 100);
The first argument in this case is the scale (which will double here from 1), and the duration of the animation (100 milliseconds). Scaling the image back to the defaults size would simply be another call:
MyLabel.ScaleTo(1, 100);
Since you most likely would want to see both animations play out, you'd likely want to use await for both of these calls if they're back to back.
await MyLabel.ScaleTo(2, 100);
await MyLabel.ScaleTo(1, 100);
You can also optionally specify an Easing function for your animations. Easing functions dictate how the animation will play out over the duration, and they control when and how the animation may speed up or slow down at different points. For example, a CubicInOut animation accelerates the animation at the beginning and decelerates at the end. Xamarin has many functions predefined in the Easing class, and you can use them very easily with your animations. For example, we can add the CubicInOut Animation to our Label from earlier:
await MyLabel.ScaleTo(2, 100, Easing.CubicInOut);
await MyLabel.ScaleTo(1, 100, Easing.CubicInOut);
You can also create your own custom easing functions if you prefer, though the built in Xamarin functions cover the most common scenarios. Animations can also be combined to create composite animations. For example, we can combine the scaling animation above with a rotation of the label. In the code bellow we can play these animations together on a button click:
private async void Button_Clicked(object sender, EventArgs e)
{
MyLabel.RotateTo(360, 200, Easing.CubicInOut);
await MyLabel.ScaleTo(2, 100, Easing.CubicInOut);
await MyLabel.ScaleTo(1, 100, Easing.CubicInOut);
MyLabel.Rotation = 0;
}
As you can see in the above code, the scaling animations are awaited so that they play sequentially, whereas the rotation plays in the background and is not awaited.
Shake Animation for AutoComplete
We can take some of these animation principals and apply them to a more real-world use case such as input validation. It's quite common to provide some type of animated feedback to a user when they enter some type of disallowed or otherwise incorrect input. The shaking animation is a very common way of indicating these to your users. The C1AutoComplete control in C1Input provides us a perfect opportunity to try to implement this.
For this example, we'll add a very simple layout with a Label and AutoComplete control:
<StackLayout Padding="20" x:Name="MyStack" Spacing="100">
<Label x:Name="MyLabel" FontSize="Large" Text="Enter an approved Country" HorizontalOptions="CenterAndExpand" Margin="0,0,0,20"/>
<c1:C1AutoComplete x:Name="MyAC" TextChanged="MyAC_TextChanged" AutoCompleteMode="StartsWith" DropDownMode="BelowOrAbove" DropDownBehavior="ButtonTap" DisplayMemberPath="Name"/>
</StackLayout>
You'll notice that there's an event added into the XAML called MyAC_TextChanged. This is where we'll implement both the validation and shake animation for the control. First, let's look at the event and input validation:
private async void MyAC_TextChanged(object sender, C1.Xamarin.Forms.Input.TextChangedEventArgs e)
{
foreach (Country c in MyAC.ItemsSource)
{
//ToLower removes any check for case
if (c.Name.ToLower().StartsWith(MyAC.Text.ToLower()))
_flag = false;
}
if (_flag)
{
//Shake animation goes here
}
_flag = true;
}
All this code really does is check that there's a potential match within the AutoComplete ItemsSource. As long as there is a potential match it will keep the flag from being raised. If there isn't that possibility though, the flag is raised with the shake animation.
The shake animation is easily implemented as a series of TranslateTo() calls:
uint timeout = 50;
ViewExtensions.CancelAnimation(MyAC);
await MyAC.TranslateTo(-15, 0, timeout);
await MyAC.TranslateTo(15, 0, timeout);
await MyAC.TranslateTo(-9, 0, timeout);
await MyAC.TranslateTo(9, 0, timeout);
await MyAC.TranslateTo(-5, 0, timeout);
await MyAC.TranslateTo(5, 0, timeout);
await MyAC.TranslateTo(-2, 0, timeout);
await MyAC.TranslateTo(2, 0, timeout);
MyAC.TranslationX = 0;
This moves the AutoComplete from side to side over a fixed interval, and you'll also notice that the values move closer to 0 to give it the impression that it's coming to rest. There is also a call to ViewExtensions.CancelAnimation() which cancels whatever previous animation hasn't finished executing. This prevents the animations from continuing to execute long after a user has stopped inputting characters. Notice that we're also explicitly making sure TranslationX has been set back to 0 at the animation's conclusion.
At this point many developers will be satisfied, but for the sake of exploring a more complicated case we'll push this example a little further. What if we wanted to add more visual feedback to the app with deeper customizations? Xamarin also allows you to create your own custom animations using ViewExtensions. Xamarin once again has great documentation about how to create your own animations, and for this example we'll use the ColorTo example shown in their docs. Basically, you can create your own ViewExtensions class to implement your own animations. Here's the example that they provide for the ColorTo method which will fade a color property from one color to another:
public static class ViewExtensions
{
public static Task<bool> ColorTo(this VisualElement self, Color fromColor, Color toColor, Action<Color> callback, uint length = 250, Easing easing = null)
{
Func<double, Color> transform = (t) =>
Color.FromRgba(fromColor.R + t * (toColor.R - fromColor.R),
fromColor.G + t * (toColor.G - fromColor.G),
fromColor.B + t * (toColor.B - fromColor.B),
fromColor.A + t * (toColor.A - fromColor.A));
return ColorAnimation(self, "ColorTo", transform, callback, length, easing);
}
public static void CancelAnimation(this VisualElement self)
{
self.AbortAnimation("ColorTo");
}
static Task<bool> ColorAnimation(VisualElement element, string name, Func<double, Color> transform, Action<Color> callback, uint length, Easing easing)
{
easing = easing ?? Easing.Linear;
var taskCompletionSource = new TaskCompletionSource<bool>();
element.Animate<Color>(name, transform, callback, 16, length, easing, (v, c) => taskCompletionSource.SetResult(c));
return taskCompletionSource.Task;
}
}
We can then apply these to our AutoComplete sample to have the screen flash red when the user types something incorrect. The updated code for the event is below:
private async void MyAC_TextChanged(object sender, C1.Xamarin.Forms.Input.TextChangedEventArgs e)
{
foreach (Country c in MyAC.ItemsSource)
{
//ToLower removes any check for case
if (c.Name.ToLower().StartsWith(MyAC.Text.ToLower()))
_flag = false;
}
if (_flag)
{
uint timeout = 50;
ViewExtensions.CancelAnimation(MyAC);
ViewExtensions.CancelAnimation(MyLabel);
ViewExtensions.CancelAnimation(MyStack);
MyLabel.ScaleTo(1.15, 100, Easing.CubicInOut);
MyStack.ColorTo(Color.Red, Color.White, c => MyStack.BackgroundColor = c, 500);
await MyAC.TranslateTo(-15, 0, timeout);
await MyAC.TranslateTo(15, 0, timeout);
await MyAC.TranslateTo(-9, 0, timeout);
await MyAC.TranslateTo(9, 0, timeout);
MyLabel.ScaleTo(1, 100, Easing.CubicInOut);
await MyAC.TranslateTo(-5, 0, timeout);
await MyAC.TranslateTo(5, 0, timeout);
await MyAC.TranslateTo(-2, 0, timeout);
await MyAC.TranslateTo(2, 0, timeout);
MyAC.TranslationX = 0;
}
_flag = true;
}
You'll notice for the ColorTo() method, we're making the StackLayout (MyStack) turn red and fading it back to white over a 500 millisecond duration to produce a flashing effect. Just for fun we're also adjusting the scale of the label too to emphasize that it must be an approved country on the list.
When you run your project you should see something similar to this:
In many cases animations like this will be overkill (there's a lot going on here), but it should give you an idea how you can combine them.
FlexGrid Animation Preview
We're always working to improve the look and feel of our controls, and for 2019v1 we're adding new animations for FlexGrid that provide much better visual feedback for different interactions related to dragging.
First, we're adding a new rip animation (where it's lifted out of the page) when a user long presses over a column. This is to indicate to the user that they've selected the column and can now manipulate and reposition it.
We've also greatly improved the animation of the FlexGrid when a user drags a column:
Notice the "flowing" repositioning animations for all of the columns as they move to make room for the column you're dragging.
Animations are one of the most important aspects of mobile controls, and we're excited about the upcoming changes and the improved user experience they provide. While this is just a preview of what's coming for 2019v1, we're happy to receive feedback on any of our controls or features, so feel free to let us know how you feel about these new animations. If you think we've missed any or are longing for some other new feature, please let us know in the comments below!
For additional customization tips, check out how to create a custom color picker for Xamarin.Forms, using a Native Custom Control with Xamarin.Forms via a Custom Renderer, and how to create a custom DropDown Control in Xamarin.Forms.