diff --git a/samples/ImageLoading.Forms.Sample/Shared/FFImageLoading.Forms.Sample.csproj b/samples/ImageLoading.Forms.Sample/Shared/FFImageLoading.Forms.Sample.csproj
index 5b0a59149..e7ae43fd3 100644
--- a/samples/ImageLoading.Forms.Sample/Shared/FFImageLoading.Forms.Sample.csproj
+++ b/samples/ImageLoading.Forms.Sample/Shared/FFImageLoading.Forms.Sample.csproj
@@ -147,6 +147,10 @@
StreamListPageCell.xaml
+
+
+ SimpleGifPage.xaml
+
@@ -278,6 +282,9 @@
MSBuild:UpdateDesignTimeXaml
+
+ MSBuild:UpdateDesignTimeXaml
+
diff --git a/samples/ImageLoading.Forms.Sample/Shared/Pages/MenuPageModel.cs b/samples/ImageLoading.Forms.Sample/Shared/Pages/MenuPageModel.cs
index 1c511f1df..eaa6236cf 100644
--- a/samples/ImageLoading.Forms.Sample/Shared/Pages/MenuPageModel.cs
+++ b/samples/ImageLoading.Forms.Sample/Shared/Pages/MenuPageModel.cs
@@ -30,6 +30,15 @@ public MenuPageModel()
})
},
+ new MenuItem() {
+ Section = "Basic",
+ Title = "Simple Gif",
+ Command = new BaseCommand(async (param) =>
+ {
+ await this.PushPageFromCacheAsync(pm => pm.Reload());
+ })
+ },
+
new MenuItem() {
Section = "Basic",
Title = "Placeholders examples",
diff --git a/samples/ImageLoading.Forms.Sample/Shared/Pages/SimpleGifPage.xaml b/samples/ImageLoading.Forms.Sample/Shared/Pages/SimpleGifPage.xaml
new file mode 100644
index 000000000..887fe3ce1
--- /dev/null
+++ b/samples/ImageLoading.Forms.Sample/Shared/Pages/SimpleGifPage.xaml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/samples/ImageLoading.Forms.Sample/Shared/Pages/SimpleGifPage.xaml.cs b/samples/ImageLoading.Forms.Sample/Shared/Pages/SimpleGifPage.xaml.cs
new file mode 100644
index 000000000..f33abd8ea
--- /dev/null
+++ b/samples/ImageLoading.Forms.Sample/Shared/Pages/SimpleGifPage.xaml.cs
@@ -0,0 +1,13 @@
+using Xamarin.Forms;
+using Xamvvm;
+
+namespace FFImageLoading.Forms.Sample
+{
+ public partial class SimpleGifPage : ContentPage, IBasePage
+ {
+ public SimpleGifPage()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/ImageLoading.Forms.Sample/Shared/Pages/SimpleGifPageModel.cs b/samples/ImageLoading.Forms.Sample/Shared/Pages/SimpleGifPageModel.cs
new file mode 100644
index 000000000..c8254c0ac
--- /dev/null
+++ b/samples/ImageLoading.Forms.Sample/Shared/Pages/SimpleGifPageModel.cs
@@ -0,0 +1,15 @@
+using Xamvvm;
+
+namespace FFImageLoading.Forms.Sample
+{
+ [PropertyChanged.ImplementPropertyChanged]
+ public class SimpleGifPageModel : BasePageModel
+ {
+ public void Reload()
+ {
+ ImageUrl = "https://media.giphy.com/media/l0Hlyi4ZMJI9MpFUQ/giphy.gif";
+ }
+
+ public string ImageUrl { get; set; }
+ }
+}
diff --git a/source/FFImageLoading.Touch/FFImageLoading.Touch.csproj b/source/FFImageLoading.Touch/FFImageLoading.Touch.csproj
index 5914fb13a..43768e546 100644
--- a/source/FFImageLoading.Touch/FFImageLoading.Touch.csproj
+++ b/source/FFImageLoading.Touch/FFImageLoading.Touch.csproj
@@ -89,6 +89,7 @@
+
diff --git a/source/FFImageLoading.Touch/Helpers/GifHelper.cs b/source/FFImageLoading.Touch/Helpers/GifHelper.cs
new file mode 100644
index 000000000..cbe2b072a
--- /dev/null
+++ b/source/FFImageLoading.Touch/Helpers/GifHelper.cs
@@ -0,0 +1,79 @@
+using System.Collections.Generic;
+using System.Linq;
+using Foundation;
+using ImageIO;
+using UIKit;
+
+namespace FFImageLoading.Helpers
+{
+ public static class GifHelper
+ {
+ public static UIImage AnimateGif(NSData data)
+ {
+ if (data?.Length == 0)
+ return null;
+
+ using (var source = CGImageSource.FromData(data))
+ return AnimateGifFromSource(source) ?? UIImage.LoadFromData(data);
+ }
+
+ private static UIImage AnimateGifFromSource(CGImageSource source)
+ {
+ var frameCount = source?.ImageCount;
+
+ // no need to animate
+ if (frameCount <= 1)
+ return null;
+
+ var frames = GetFrames(source);
+ var delays = GetDelays(source);
+ var totalDuration = delays.Sum();
+
+ // SUPER BASIC. Does not respect variable length frames. No memory optimizations.
+ return UIImage.CreateAnimatedImage(frames.ToArray(), totalDuration);
+ }
+
+ private static List GetFrames(CGImageSource source)
+ {
+ var retval = new List();
+
+ for (int i = 0; i < source?.ImageCount; i++)
+ {
+ using (var frameImage = source.CreateImage(i, null))
+ retval.Add(UIImage.FromImage(frameImage));
+ }
+
+ return retval;
+ }
+
+ private static List GetDelays(CGImageSource source)
+ {
+ var retval = new List();
+
+ for (int i = 0; i < source?.ImageCount; i++)
+ {
+ var properties = source.GetProperties(i, null);
+ using (var gifProperties = properties.Dictionary["{GIF}"])
+ {
+ using (var delayTime = gifProperties.ValueForKey(new NSString("DelayTime")))
+ {
+ var realDuration = double.Parse(delayTime.ToString());
+ retval.Add(realDuration);
+ }
+ }
+ }
+
+ return retval;
+ }
+
+ private static int GetLoopCount(CGImageSource source)
+ {
+ var var = source.GetProperties(null);
+ using (var gifProperties = var.Dictionary["{GIF}"])
+ {
+ var loopCount = gifProperties.ValueForKey(new NSString("LoopCount"));
+ return int.Parse(loopCount.ToString());
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/FFImageLoading.Touch/Work/PlatformImageLoaderTask.cs b/source/FFImageLoading.Touch/Work/PlatformImageLoaderTask.cs
index ed62ca9ba..280d79ab5 100644
--- a/source/FFImageLoading.Touch/Work/PlatformImageLoaderTask.cs
+++ b/source/FFImageLoading.Touch/Work/PlatformImageLoaderTask.cs
@@ -48,6 +48,12 @@ protected async override Task GenerateImageAsync(string path, ImageSour
{
imageIn = new WebP.Touch.WebPCodec().Decode(imageData);
}
+ // Special case to handle gif animations on iOS
+ else if (path.ToLowerInvariant().EndsWith(".gif", StringComparison.InvariantCulture))
+ {
+ using (var nsdata = NSData.FromStream(imageData))
+ imageIn = GifHelper.AnimateGif(nsdata);
+ }
else
{
var nsdata = NSData.FromStream(imageData);