Tuesday, February 17, 2015

Dipping your feet into Xamarin Animation Extensions

I must admit that I've kind of missed this whole animation thingy that Xamarin put into Xamarin Forms. I've used the Animation classes to animate single properties, but haven't really put any time into exploring the extension methods for the View class. (poorly documented here).

The animation extension methods lets you move, rotate and scale stuff with different duration and easing functions. Duration and easing are optional parameters that you can pass into each animation function. An easing is a function of the rate of change during an animation. For example the BounceIn easing will overshoot its target and "bounce back" to the value required. Hence, bounce...

Anyway, let's move through each function!

The sandbox

So I started out with a single button in a single XAML view. Centered in all directions and just perfect. I've totally ignored all MVVM stuff and directly referenced the button control by name (theButton).

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="AnimateThis.MainView">

    <ContentPage.Content>
        <Button Text="Click me" x:Name="theButton" 
                VerticalOptions="Center" 
                HorizontalOptions="Center" />
    </ContentPage.Content>
</ContentPage>



Step one - Let's move it!

I added code in the code behind to handle the Click event (don't worry, we'll do it MVVM style later on in another post). This simply moves the button itself to a absolute position. You could of course move any other object that inherits from VisualElement. An important thing to think about is that it will move the object to the bounding box's upper left corner. Also, you must supply enough space yourself to make sure that the contents of the view has enough room to display itself.

    public partial class MainView : ContentPage
    {
        public MainView()
        {
            InitializeComponent();
            theButton.Clicked += async (object sender, EventArgs e) => 
                {
                   await theButton.LayoutTo(new Rectangle(100, 100, 300, 50));
                };
        }
    }

Step two - Translate this!

Another way to move the button without having to care about the width and height is to use the TranslateTo extension method. The important thing here is that the movement is relative. So translating by {0,0} doesn't do a thing. The example below moves the button left and up by 50.

    await theButton.TranslateTo(-50, -50);

In order to use the TranslateTo extension method you need to keep track of where you start from.

Step three - Scaling and ContinueWith!

You can string animations together since all calls are using async/await. The first way of doing this is to simply declare the method async and add await before each call. (gotta love C#).

    await theButton.ScaleTo(3);
    await theButton.ScaleTo(1);

So then this should work...

await theButton.ScaleTo(3).ContinueWith((a) => theButton.ScaleTo(1) );  

It does not... It works the first time you click the button, but the second time it gets stuck after the initial upscaling.

Step four - RelScale does what?

All the Rel* functions simply add to what ever value the control has before. The code below adds 4 to the scale each time you click it. It becomes really large after a while.

    await theButton.RelScaleTo(4);

This also goes for RelRotate and is pretty much what TranslateTo also does.

Step five - Fade to black

The fade function let's you control the opacity to what ever value you'd like. Nothing fancy, but very useful. To make the button disappear, just fade to 0. The upper limit is one (1) and that means that it's totally visible.

    await theButton.FadeTo(0);

Step six - Rotation

The last of the animation extension methods. Rotates the control to 40 absolute degrees. The RelRotate adds the given value to the current rotation.

   await theButton.RotateTo(40);

Another cool rotate function the RotateXTo and RotateYTo that gives you a 3D effect. Combine it with a FadeTo(0) to make your buttons fold and disappear! :)

Combining them

Can you combine different animations? Well, yes you can! Simply omit the await keyword to continue execution.

   theButton.TranslateTo(-10, -150);
   await theButton.FadeTo(0);

You might wonder why I don't use a fancier way to do the await, like in the example below? It simply doesn't work. The animation "locks up" half way through. Another issue for Bugzilla.

   var t1 = theButton.TranslateTo(-10, -150);
   var t2 = theButton.FadeTo(0);

   Task.WaitAll(t1, t2);

Summary

The animation extension methods are cool. Use them. Now!

Monday, February 16, 2015

Animate the Height of RowDefinition

When I first was faced with the requirement to create a kind of accordion-like area that open and closes by the click of a button, I thought to my self that that would be easy...

The solution was pretty easy, but accepting that the most simple solution didn't work was not.

The requirement was based on the image below. When you tap the surface titled "Tap me!" the red surface was going to animate it's height and disappear. I know I'm not going to earn any design awards from this! :)

Grids to the rescue!

I defined a simple three-row grid that gives the layout displayed in the image above.

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="AnimatedGrid.MainPage">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="200" />
            <RowDefinition Height="*" />
            <RowDefinition Height="100" />
        </Grid.RowDefinitions>

        <BoxView BackgroundColor="Red" Grid.Row="0" />
        <BoxView BackgroundColor="Blue" Grid.Row="1">
            <BoxView.GestureRecognizers>
                <TapGestureRecognizer Command="{Binding Animate}" />
            </BoxView.GestureRecognizers>
        </BoxView>
        <BoxView BackgroundColor="Yellow" Grid.Row="2" />

        <Button Grid.Row="1" Text="Tap me!" TextColor="White" 
            HorizontalOptions="Center" 
            VerticalOptions="Center" Command="{Binding Animate}" />
        <Label Grid.Row="0" Text="I'm going to animate" 
            HorizontalOptions="Center" VerticalOptions="Center" />

    </Grid>
</ContentPage>

Attempt one - binding to the height property

I then created a view model with a TopRowHeight property that I bound to the first RowDefinition.

 <RowDefinition Height="{Binding TopRowHeight}" />

This did not work at all. Even though the documentation states that the HeightProperty is bindable it simply does not call the getter. I tried returning an int, float, double and GridLength and any binding mode possible.

The solution - xaml

So what I ended up doing was to give the RowDefinition a name instead. For this example, it's important to keep the Height="200" since it decides what height to open up to again.

        <Grid.RowDefinitions>
            <RowDefinition 
Height="200x:Name="topRow" />
            <RowDefinition Height="*" />
            <RowDefinition Height="100" />
        </Grid.RowDefinitions>

The solution - ViewModel

And then a ViewModel that looks like the snippet below. Two things to note first before you bash my code.
  • The INotifyPropertyChanged-implementation is omitted. Use a base class or implement it in any real world example.
  • We take an Action in the constructor, this might not be so good in the world of IoC, but it's there to make for a simpler example.

    public class MainPageViewModel
    {
        private Action _rowAnimation;

        public MainPageViewModel(Action rowAnimation)
        {
            _rowAnimation = rowAnimation;
        }

        public ICommand Animate
        {
            get
            {
                return new Command((o) =>
                {
                    _rowAnimation.Invoke();
                });
            }
        }
    }

The solution - The View

And then the actual implementation of the View (the page). What we do here is to create the MainPageViewModel and pass a reference of an Action that takes care of the animation of the grid row.

public partial class MainPage : ContentPage
    {    
        private Animation _animation;
        private double _initialHeight;

        public MainPage ()
        {
            InitializeComponent ();

            // Since we can't use binding to animate the row height directly we
            // need to give the ViewModel a method to call when it wants to 
            // animate the row height.
            var model = new MainPageViewModel(AnimateRow);
            BindingContext = model;

            // Store the inital value so we know what to what height to restore to
            _initialHeight = topRow.Height.Value;
        }

        private void AnimateRow()
        {
            if(topRow.Height.Value < _initialHeight)
            {
                // Move back to original height
                _animation = new Animation(
                    (d) => topRow.Height = new GridLength(Clamp(d, 0, double.MaxValue)),
                    topRow.Height.Value, _initialHeight, Easing.SpringIn, () => _animation = null);
            }
            else
            {
                // Hide the row
                _animation = new Animation(
                    (d) => topRow.Height = new GridLength(Clamp(d, 0, double.MaxValue)),
                    _initialHeight, 0, Easing.SpringIn, () => _animation = null);
            }

            _animation.Commit(this, "the animation");
        }

        // Make sure we don't go below zero
        private double Clamp(double value, double minValue, double maxValue)
        {
            if (value < minValue)
            {
                return minValue;
            }

            if (value > maxValue)
            {
                return maxValue;
            }

            return value;
        }
    }

Resources

You can download the example from here!

Friday, February 13, 2015

Manage the Windows Phone Theme

Wouldn't it be great if you could control the theme of your Windows Phone 8 (silverlight) app? Well, just install the PhoneThemeManager package from nuget written by Jeff Wilcox and add a single line of code to your app.

    ThemeManager.ToDarkTheme();

Read more about it at http://www.jeff.wilcox.name/2012/01/phonethememanager/. Oh, and it seems like the reference isn't added sometimes so just add a reference and drill down into the packages folder until you find the correct dll. In this case JeffWilcox.PhoneThemeManager.dll in the windowsphone8 folder.

Windows Phone 8.1 solves this by letting you request a theme by writing something like

    this.RequestedTheme = ApplicationTheme.Light;

in the constructor (or any other place) of your app.

Check out this article on Stack Overflow you more info!

Monday, February 2, 2015

Byte array is empty when fetching using Xamarin Forms 1.3 and XLabs 2.0


I stumbled upon a small "bug/feature" after updating a Xamarin.Forms app to 1.3 and XLabs 2.0. The small snippet of code below is called after a pictures is selected or taken.

Photo is a ImageSource that belongs to the ViewModel for the View. I also needed the raw bytes later on so I simply used the Read method of the stream returned.

After the update the bytes-array was simply set to 0 for each byte in the array until I reset the position before the read. No exception, simply just empty.

    Photo = ImageSource.FromStream(() => mediaFile.Source);
    var bytes = new byte[mediaFile.Source.Length];
    mediaFile.Source.Position = 0;

    mediaFile.Source.Read(bytes, 0, (int)mediaFile.Source.Length);

Have a nice day!