Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Programmatically Create SKLottieView in MAUI #157

Open
heiser opened this issue Dec 9, 2022 · 10 comments
Open

Programmatically Create SKLottieView in MAUI #157

heiser opened this issue Dec 9, 2022 · 10 comments

Comments

@heiser
Copy link

heiser commented Dec 9, 2022

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.

@dpjha84
Copy link

dpjha84 commented Jan 17, 2023

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?

@Strypper
Copy link

Yes we need an example or guidance to set image source using C#

@mrlacey
Copy link

mrlacey commented May 29, 2023

I was trying to get this working with Maui.Markup.
The AnimationLoaded event fires, but the animation is never displayed. 😢

@kyurkchyan
Copy link

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.

@haavamoa
Copy link

haavamoa commented Sep 19, 2023

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.

@naaeef
Copy link

naaeef commented Nov 23, 2023

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.

@naaeef
Copy link

naaeef commented Nov 29, 2023

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());

@Elmigo
Copy link

Elmigo commented Feb 10, 2024

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.

@awasilik
Copy link

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 :)

@bradencohen
Copy link

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants