New to Telerik UI for Xamarin? Download free 30-day trial

Chart for Xamarin.iOS: Custom Animations

TKChart uses the Core Animation infrastructure available on iOS that you use to animate the visual points in series. In order to enable the animations, you should set AllowAnimations property to true. In that case the default animations are performed for each series. If you handle the TKChartDelegate protocol and implement the AnimationForSeries method, you can perform custom animations. With Core Animation, most of the work required to draw each frame of an animation is done for you. All you have to do is configure a few animation parameters (such as the start and end points).

You can use most of the Core Animation framework to customize the visual points animation. You can read more about Core Animation at Apple Developer website.

Configuration Prerequisites

You should handle the TKChartDelegate's method AnimationForSeries to create a custom animation. In addition, you should group the animation created for each point in CAAnimationGroup to apply animation sequentially. You can access old and new points collection by using the TKChartSeriesRenderState properties OldPoints and Points. It allows generation for value key path property for point at a specified index by calling the animationKeyPathForPointAtIndex method.

Animating Line Series

The code below animates the line series points by moving them from bottom to top.

public override CAAnimation AnimationForSeries (TKChart chart, TKChartSeries series, TKChartSeriesRenderState state, CGRect rect)
{
    double duration = 0;
    List<CAAnimation> animations = new List<CAAnimation> ();

    for (int i = 0; i<(int)state.Points.Count; i++) 
    {            
        TKChartVisualPoint point = (TKChartVisualPoint)state.Points.ObjectAtIndex ((uint)i);

        if (Grow) 
        {
            string keyPath = string.Format ("seriesRenderStates.{0}.points.{1}.x", series.Index, i);

            CABasicAnimation animation = (CABasicAnimation)CABasicAnimation.FromKeyPath(keyPath);
            animation.Duration = 0.1 *(i + 0.2);
            animation.From = new NSNumber(0);
            animation.To = new NSNumber(point.X);
            animation.TimingFunction = CAMediaTimingFunction.FromName (CAMediaTimingFunction.EaseOut);
            animations.Add(animation);

            duration = animation.Duration;
        }
        else 
        {
            string keyPath = string.Format ("seriesRenderStates.{0}.points.{1}.y", series.Index, i);
            nfloat oldY = rect.Height;

            if (i > 0) 
            {
                CAKeyFrameAnimation animation = (CAKeyFrameAnimation)CAKeyFrameAnimation.GetFromKeyPath(keyPath);
                animation.Duration = 0.1* (i + 1);
                animation.Values = new NSNumber[] { new NSNumber(oldY), new NSNumber(oldY), new NSNumber(point.Y) };
                animation.KeyTimes = new NSNumber[] { new NSNumber(0), new NSNumber(i/(i+1.0)), new NSNumber(1) };
                animation.TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseOut);
                animations.Add (animation);

                duration = animation.Duration;
            }
            else 
            {
                CABasicAnimation animation = (CABasicAnimation)CABasicAnimation.FromKeyPath(keyPath);
                animation.From = new NSNumber(oldY);
                animation.To = new NSNumber(point.Y);
                animation.Duration = 0.1f;
                animations.Add(animation);
            }
        }
    }

    CAAnimationGroup group = new CAAnimationGroup ();
    group.Duration = duration;
    group.Animations = animations.ToArray();

    return group;
}

Animating Pie Series

The following lines of code animate the pie's segments by moving them to the pie center together with changing their opacity.

public override CAAnimation AnimationForSeries (TKChart chart, TKChartSeries series, TKChartSeriesRenderState state, CGRect rect)
{
    double duration = 0;
    List<CAAnimation> animations = new List<CAAnimation>();
    for (int i = 0; i<(int)state.Points.Count; i++) {
        string pointKeyPath = state.AnimationKeyPathForPointAtIndex ((uint)i);

        string keyPath = string.Format("{0}.distanceFromCenter", pointKeyPath);
        CAKeyFrameAnimation a = CAKeyFrameAnimation.GetFromKeyPath(keyPath);
        a.Values = new NSNumber[] { new NSNumber(50), new NSNumber(50), new NSNumber(0) };
        a.KeyTimes = new NSNumber[] { new NSNumber(0), new NSNumber(i/(i+1.0)), new NSNumber(1) };
        a.Duration = 0.3 * (i+1.1);
        animations.Add(a);

        keyPath = string.Format("{0}.opacity", pointKeyPath);
        a = CAKeyFrameAnimation.GetFromKeyPath(keyPath);
        a.Values = new NSNumber[] { new NSNumber(0), new NSNumber(0), new NSNumber(1) };
        a.KeyTimes = new NSNumber[] { new NSNumber(0), new NSNumber(i/(i+1.0)), new NSNumber(1) };
        a.Duration = 0.3 * (i+1.1);
        animations.Add(a);

        duration = a.Duration;
    }
    CAAnimationGroup g = new CAAnimationGroup();
    g.Duration = duration;
    g.Animations = animations.ToArray();
    return g;
}
In this article