First things first — did you know that you can use UIImage to show frame-by-frame animations? It’s actually quite straight forward, all you need to do is:

  1. Prepare your frames in png format, name them using an ordering convention (for example “Frame1.png”, “Frame2.png”, …)
  2. Add these images to your Xamarin.iOS project, let’s say to Resources/Animation folder
  3. Make sure all of your images are referenced as BundleResource in your csproj:
<ItemGroup>
<BundleResource Include="Resources\Animation\*.png" />
</ItemGroup>

4. Load your images as UIImage and pass them to the UIImageView using AnimationImages property:

UIImage[] animationFrames = Enumerable.Range(1, 126)
   .Select(i => $"{NSBundle.MainBundle.ResourcePath}/Animation/Frame{i}")
   .Select(UIImage.FromBundle).ToArray();

var animatedImageView = new UIImageView
{
   AnimationImages = animationFrames,
   AnimationRepeatCount = 0, // repeats animation indefinitely
   AnimationDuration = 2     // whole animation will take 2 secs
};

Add animatedImageView to your controller and here’s what you will see:

via GIPHY

Great, it’s alive! 🧟‍♂️ What if you’d like to pause the animation on tap and then resume it after another tap? Sadly, there’s no API for this in the UIImageView, but there is a way to accomplish it using UIImageView’s Layer.

I’ve found it described in one of the Technical Q&As on developer.apple.com. This Objective C code may not be so easy to rewrite to C#, especially if you’re just getting started, so below is a working solution in Xamarin.iOS. You may want to polish the code a bit (extracting StopAnimation/StartAnimation extension methods may be a good start).


var animatedImageView = new UIImageView
{
    AnimationImages = animationFrames,
    AnimationRepeatCount = 0,
    AnimationDuration = 2,
    UserInteractionEnabled = true
};

animatedImageView.AddGestureRecognizer(new UITapGestureRecognizer(() =>
{
    if (animatedImageView.Layer.Speed > 0.5)
    {
        var pausedTime = animatedImageView.Layer.ConvertTimeFromLayer(CAAnimation.CurrentMediaTime(), null);
        animatedImageView.Layer.Speed = 0;
        animatedImageView.Layer.TimeOffset = pausedTime;
    }
    else
    {
        var pausedTime = animatedImageView.Layer.TimeOffset;
        animatedImageView.Layer.Speed = 1;
        animatedImageView.Layer.TimeOffset = 0.0;
        animatedImageView.Layer.BeginTime = 0.0;
        animatedImageView.Layer.BeginTime = animatedImageView.Layer.ConvertTimeFromLayer(CAAnimation.CurrentMediaTime(), null) - pausedTime;
    }
}));

Now you should be able to pause/resume the animation with taps:

gif source: cliply.co/clip/rocket-icon

FAQ:

Do I need to add my frames as iOS Assets? It’s kind of tedious with so many files! 😱
No, you can just drag and drop the whole folder to Resources, remember to reference files as BundleResource.

The animation is not pausing when I tap it, what’s wrong? 🤔
You might have forgotten to set UserInteractionEnabled to true on the UIImageView.


I hope that this quick tutorial was helpful to you. Let me know if something is not clear ✌🏻

Write A Comment