SkiaSharp.Extended
SkiaSharp.Extended copied to clipboard
Programmatically Create SKLottieView in MAUI
All of the examples I have seen assign the source in XAML; I have tried to do it in code without success:
var source = new SKFileLottieImageSource()
{
File = String.Format("{0}.json", Name)
};
var view = new SKLottieView()
{
Source = source,
WidthRequest = 400,
HeightRequest = 400,
BackgroundColor = Colors.Red,
RepeatMode = SKLottieRepeatMode.Restart,
RepeatCount = Loop ? -1 : 0,
//Scale = Scale,
StyleId = ID
};
The view renders to the screen but the animation does not appear.
I see in the source that the file is ultimately loaded using FileSystem.OpenAppPackageFileAsync and so I tested that with the file path I used in SKFileLottieImageSource, and it worked.
Also, I know the Lottie file I am using works, as it did fine in the Xamarin Forms app that we are attempting to migrate to MAUI.
Similar to this but slightly different. I cannot play animation on demand from code behind. In Xamarin.Forms there was a method available PlayAnimation() but it it is missing in MAUI version. Can somebody provide some work around on how to play an animation from code behind for the LottieView created in XAML?
Yes we need an example or guidance to set image source using C#
I was trying to get this working with Maui.Markup.
The AnimationLoaded
event fires, but the animation is never displayed. 😢
Animations work perfectly fine when I assign them in Xaml. However, when I try to setup a derived control, that would set some predefined animations, the animations won't show up.
using System.Diagnostics;
using SkiaSharp.Extended.UI.Controls;
namespace MSOISales.Maui.Legacy.Controls;
public enum LoadingIndicatorType
{
Circular,
Horizontal
}
public class LoadingIndicator : SKLottieView
{
public LoadingIndicator()
{
AnimationLoaded += OnAnimationLoaded;
AnimationFailed += OnAnimationFailed;
UpdateIndicatorType();
UpdateIndicatorState();
}
public static readonly BindableProperty IndicatorTypeProperty = BindableProperty.Create(nameof(IndicatorType),
typeof(LoadingIndicatorType),
typeof(LoadingIndicator),
defaultValue:LoadingIndicatorType.Circular,
propertyChanged:OnIndicatorTypeChanged);
public LoadingIndicatorType IndicatorType
{
get => (LoadingIndicatorType)GetValue(IndicatorTypeProperty);
set => SetValue(IndicatorTypeProperty, value);
}
private static void OnIndicatorTypeChanged(BindableObject bindable, object oldValue, object newValue)
{
var loadingIndicator = (LoadingIndicator)bindable;
loadingIndicator.UpdateIndicatorType();
}
public static readonly BindableProperty IsRunningProperty = BindableProperty.Create(nameof(IsRunning),
typeof(bool),
typeof(LoadingIndicator),
defaultValue:false,
propertyChanged:OnIsRunningChanged);
public bool IsRunning
{
get => (bool)GetValue(IsRunningProperty);
set => SetValue(IsRunningProperty, value);
}
private static void OnIsRunningChanged(BindableObject bindable, object oldValue, object newValue)
{
var loadingIndicator = (LoadingIndicator)bindable;
loadingIndicator.UpdateIndicatorState();
}
private void UpdateIndicatorType()
{
Source = IndicatorType switch
{
LoadingIndicatorType.Circular => new SKFileLottieImageSource { File = "animation_circular_loading.json" },
LoadingIndicatorType.Horizontal => new SKFileLottieImageSource { File = "animation_horizontal_loading.json" },
_ => throw new ArgumentOutOfRangeException()
};
}
private void UpdateIndicatorState()
{
IsAnimationEnabled = IsRunning;
IsVisible = IsRunning;
}
private void OnAnimationLoaded(object sender, EventArgs e)
{
Debug.WriteLine($"Animation {IndicatorType} loaded");
}
private void OnAnimationFailed(object sender, EventArgs e)
{
Debug.WriteLine($"Animation {IndicatorType} failed");
}
}
Later, I use it in Xaml like this
<Style
x:Key="DefaultLoadingIndicatorStyle"
TargetType="controls:LoadingIndicator">
<Setter Property="WidthRequest" Value="{StaticResource LoadingIndicatorWidth}" />
<Setter Property="HeightRequest" Value="{StaticResource LoadingIndicatorHeight}" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="BackgroundColor" Value="Red" />
</Style>
<Style
BasedOn="{StaticResource DefaultLoadingIndicatorStyle}"
TargetType="controls:LoadingIndicator" />
<controls:LoadingIndicator
IsRunning="{Binding SyncCommand.IsBusy}"
IndicatorType="Horizontal" />
The AnimationLoaded
is fired, and I never receive an error on the AnimationFailed
; however, nothing happens, I see a blank view.
I think I found a workaround for this. We create a library where we have a Content Page that will animate using SKLottieView when consumers set a property for the page IsFinishedSaving
.
The SKLottieView is created programatically, and we also noticed that the first time the app loads and the first time we display the animation, it does not appear. The second time we try to display the animation it does appear. I went through this repository, and found this line in the constructor of SKLottieView:
ResourceLoader<SKLottieViewResources>.EnsureRegistered((VisualElement) this);
From my reading, the ResourcesLoader basically makes sure that the SKLottieViewResources
is added to the apps resources. I somehow figured out that this has to be some kind of race condition where it might not have been loaded successfully when we try to display the animation the first time you try to create it. The second time you display it its already merged and it works as expected.
The workaround is to merge this resource to your resources manually in your App.xaml:
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:themes="clr-namespace:SkiaSharp.Extended.UI.Controls.Themes;assembly=SkiaSharp.Extended.UI"
...>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<themes:SKLottieViewResources/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Let me know if this did not work, we did not inherit directly from SKLottieView in our component. So there might be differences in how we programatically use SKLottieView if it does not work for you.
Hello @haavamoa I have the same problem. Unfortunately, your workaround doesn't work for me. If I load the animation for the second time, it works as expected.
If I add the following to the constructor of the page containing the animation, it works for me:
Application.Current?.Resources?.MergedDictionaries.Add(new SKLottieViewResources());
Looks like binding the source does not work either. When using binding, nothing displays within the SKLottieView. Not even after loading a second or third time. Only when hardcoding the name of the JSON file within the source attribute, the animation displays.
I think I found a workaround for this. We create a library where we have a Content Page that will animate using SKLottieView when consumers set a property for the page
IsFinishedSaving
.The SKLottieView is created programatically, and we also noticed that the first time the app loads and the first time we display the animation, it does not appear. The second time we try to display the animation it does appear. I went through this repository, and found this line in the constructor of SKLottieView:
ResourceLoader<SKLottieViewResources>.EnsureRegistered((VisualElement) this);
From my reading, the ResourcesLoader basically makes sure that the
SKLottieViewResources
is added to the apps resources. I somehow figured out that this has to be some kind of race condition where it might not have been loaded successfully when we try to display the animation the first time you try to create it. The second time you display it its already merged and it works as expected.The workaround is to merge this resource to your resources manually in your App.xaml:
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:themes="clr-namespace:SkiaSharp.Extended.UI.Controls.Themes;assembly=SkiaSharp.Extended.UI" ...> <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <themes:SKLottieViewResources/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> </Application>
Let me know if this did not work, we did not inherit directly from SKLottieView in our component. So there might be differences in how we programatically use SKLottieView if it does not work for you.
Thanks, the workaround worked for me :)
I think this should be easy to fix in library though since this clearly shows where the issue is :)
I think I found a workaround for this. We create a library where we have a Content Page that will animate using SKLottieView when consumers set a property for the page
IsFinishedSaving
.The SKLottieView is created programatically, and we also noticed that the first time the app loads and the first time we display the animation, it does not appear. The second time we try to display the animation it does appear. I went through this repository, and found this line in the constructor of SKLottieView:
ResourceLoader<SKLottieViewResources>.EnsureRegistered((VisualElement) this);
From my reading, the ResourcesLoader basically makes sure that the
SKLottieViewResources
is added to the apps resources. I somehow figured out that this has to be some kind of race condition where it might not have been loaded successfully when we try to display the animation the first time you try to create it. The second time you display it its already merged and it works as expected.The workaround is to merge this resource to your resources manually in your App.xaml:
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:themes="clr-namespace:SkiaSharp.Extended.UI.Controls.Themes;assembly=SkiaSharp.Extended.UI" ...> <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <themes:SKLottieViewResources/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> </Application>
Let me know if this did not work, we did not inherit directly from SKLottieView in our component. So there might be differences in how we programatically use SKLottieView if it does not work for you.
Great workaround, thanks so much.
In our case, we were programmatically creating the SKLottieView before the Window was set, causing that EnsureRegistered to fail.
Thanks again!