Last active
November 30, 2023 12:29
-
-
Save jamesmontemagno/7619563 to your computer and use it in GitHub Desktop.
iOS Animated Image View. Display GIFs on iOS. Ported from: https://github.com/atnan/UIImageView-NDVAnimatedGIFSupport/blob/master/UIImageView%2BNDVAnimatedGIFSupport.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
using System.Drawing; | |
using MonoTouch.CoreAnimation; | |
using MonoTouch.CoreGraphics; | |
using MonoTouch.Foundation; | |
using MonoTouch.ImageIO; | |
using MonoTouch.UIKit; | |
namespace PuppyKittyOverflow.Touch | |
{ | |
public class AnimatedImageView | |
{ | |
public static UIImageView GetAnimatedImageView(string url, UIImageView imageView = null) | |
{ | |
var sourceRef = CGImageSource.FromUrl(NSUrl.FromString(url)); | |
return CreateAnimatedImageView (sourceRef, imageView); | |
} | |
public static UIImageView GetAnimatedImageView(NSData nsData, UIImageView imageView = null) | |
{ | |
var sourceRef = CGImageSource.FromData(nsData); | |
return CreateAnimatedImageView(sourceRef, imageView); | |
} | |
private static UIImageView CreateAnimatedImageView(CGImageSource imageSource, UIImageView imageView = null) | |
{ | |
var frameCount = imageSource.ImageCount; | |
var frameImages = new List<NSObject>(frameCount); | |
var frameCGImages = new List<CGImage>(frameCount); | |
var frameDurations = new List<double>(frameCount); | |
var totalFrameDuration = 0.0; | |
for (int i = 0; i < frameCount; i++) | |
{ | |
var frameImage = imageSource.CreateImage(i, null); | |
frameCGImages.Add(frameImage); | |
frameImages.Add(NSObject.FromObject(frameImage)); | |
var properties = imageSource.GetProperties(i, null); | |
var duration = properties.Dictionary["{GIF}"]; | |
var delayTime = duration.ValueForKey(new NSString("DelayTime")); | |
duration.Dispose (); | |
var realDuration = double.Parse(delayTime.ToString()); | |
frameDurations.Add(realDuration); | |
totalFrameDuration += realDuration; | |
frameImage.Dispose (); | |
} | |
var framePercentageDurations = new List<NSNumber>(frameCount); | |
var framePercentageDurationsDouble = new List<double>(frameCount); | |
NSNumber currentDurationPercentage = 0.0f; | |
double currentDurationDouble = 0.0f; | |
for (int i = 0; i < frameCount; i++) | |
{ | |
if (i != 0) | |
{ | |
var previousDuration = frameDurations[i - 1]; | |
var previousDurationPercentage = framePercentageDurationsDouble[i - 1]; | |
var number = previousDurationPercentage + (previousDuration/totalFrameDuration); | |
currentDurationDouble = number; | |
currentDurationPercentage = new NSNumber(number); | |
} | |
framePercentageDurationsDouble.Add(currentDurationDouble); | |
framePercentageDurations.Add(currentDurationPercentage); | |
} | |
var imageSourceProperties = imageSource.GetProperties(null); | |
var imageSourceGIFProperties = imageSourceProperties.Dictionary["{GIF}"]; | |
var loopCount = imageSourceGIFProperties.ValueForKey(new NSString("LoopCount")); | |
var imageSourceLoopCount = float.Parse(loopCount.ToString()); | |
var frameAnimation = new CAKeyFrameAnimation(); | |
frameAnimation.KeyPath = "contents"; | |
if (imageSourceLoopCount <= 0.0f) | |
{ | |
frameAnimation.RepeatCount = float.MaxValue; | |
} | |
else | |
{ | |
frameAnimation.RepeatCount = imageSourceLoopCount; | |
} | |
imageSourceGIFProperties.Dispose (); | |
frameAnimation.CalculationMode = CAAnimation.AnimationDescrete; | |
frameAnimation.Values = frameImages.ToArray(); | |
frameAnimation.Duration = totalFrameDuration; | |
frameAnimation.KeyTimes = framePercentageDurations.ToArray(); | |
frameAnimation.RemovedOnCompletion = false; | |
var firstFrame = frameCGImages[0]; | |
if(imageView == null) | |
imageView = new UIImageView(new RectangleF(0.0f, 0.0f, firstFrame.Width, firstFrame.Height)); | |
else | |
imageView.Layer.RemoveAllAnimations(); | |
imageView.Layer.AddAnimation(frameAnimation, "contents"); | |
frameAnimation.Dispose (); | |
return imageView; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var client = new HttpClient(); | |
var stream = await client.GetStreamAsync(image); | |
var data = await GetDataAsync(stream); | |
AnimatedImageView.GetAnimatedImageView(data, ImageViewAnimal); | |
private async Task<NSData> GetDataAsync(Stream stream) | |
{ | |
return await Task.Run (() => {return NSData.FromStream (stream);}); | |
} |
How can i use it for local files? Can someone put this in a git hub project?
Be aware that this class leads to very huge memory leaks.
CGImage is not memory managed and if you don't manually remove the animateView the memory continues to increase without any limit.
You can easily see this with the apple profiler
Hi, If you are looking same implementation in MAUI. Please visit below GitHub link
Be aware that this class leads to very huge memory leaks. CGImage is not memory managed and if you don't manually remove the animateView the memory continues to increase without any limit. You can easily see this with the apple profiler
I have jus learnt this after 3 days of troubleshooting !
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, first of all thanks for this gist!! I do have one question tough... is there any reasons you can think about why this is not working on a iPad 2? It works fine on a emulator and a iPad mini, but it does not work on a iPad 2 :(