Kristoffer Berge
Kristoffer Berge

Reputation: 1076

Get height of Xamarin.Forms ContentView before animating in

I'm trying to create a notification that animates in from the top of a page in Xamarin.Forms. I'm pretty close, but I can't get the height of the ContentView before I animate it in on the page. I've made a xamarin workbook to demonstrate my issue, that can be run on an android-simulator. (guess it would work on ios/wpf as well with some modifications)

The workbook is available here

The issue is in ToastNotificationView.AnimateIn() where I first set the visibility of the entire contentview to visible (red background). The green notification is immediately rendered in it's final position despite the different attempts to set it's position outside the screen before animating it in. This only happens the first time the notification is animated in on a page, since it's height is available after it is animated out of the screen and made hidden again.

I can solve this by hard-coding the height offset to say 50, but to achieve a consistent animation for variable heights, this isn't going to cut it. I could also create a method that calculates the number of lines and then dynamically set the height based on that, but I hope there is a way to actually render this, and get the actual height of the component somehow without making it visible for the user until I start the animation from where I want it.

public class ToastNotificationView: ContentView {
    // The notification item to be animated in/out
    private StackLayout MainStackLayout;
    public ToastNotificationView() {
        InitializeComponent(); // Let's pretend workbooks supports xaml

        //Subscribing to notification events
        NotificationService.NotificationAppearing += AnimateIn;
        // TODO: Find out how to unsubscribe in contentview without access to life cycle hooks
    }
    private async void OnDismissClicked(object sender, EventArgs e) {
        await AnimateOut();
    }
    private async Task AnimateOut() {
        await MainStackLayout.TranslateTo(0, -MainStackLayout.Height, 1000);
        IsVisible = false;
    }
    private async void AnimateIn(object sender, EventArgs e) {
        // Make the entire view visible
        IsVisible = true;

        // First time, height is always zero, so we can't set it like this
        MainStackLayout.TranslationY = -MainStackLayout.Height;

        // Attempting to await a 1ms animation to get a height does not result in correct height either
        await MainStackLayout.TranslateTo(0, 0, 1);
        await MainStackLayout.TranslateTo(0, -MainStackLayout.Height, 1);

        //Still does not animate in on initial animation
        await MainStackLayout.TranslateTo(0, 0, 1000);
    }
}

The layout is created in the InitializeComponent method to support xamarin workbooks. This could be anything for the purpose of this issue.

// layout
private void InitializeComponent(){
    // Make contentview background red for debugging purposes
    this.BackgroundColor = Color.Red;
    this.VerticalOptions = LayoutOptions.Start;

    //The actual notification should be green
    MainStackLayout = new StackLayout{
        Orientation = StackOrientation.Horizontal,
        VerticalOptions = LayoutOptions.Start,
        BackgroundColor = Color.Green
    };
    var title = new Label{
        Text = "Hello There"
    };
    var msg = new Label{
        Text = "Importaint stuff you should be aware of"
    };

    var btn = new Button{
        Text = "X",
        VerticalOptions = LayoutOptions.Start,
        HorizontalOptions = LayoutOptions.EndAndExpand,
        BorderColor = Color.Transparent,
        BackgroundColor = Color.Transparent,
        HeightRequest = 40,
        WidthRequest = 40,
        Margin = 3
    };
    btn.Clicked += OnDismissClicked;
    var textStack = new StackLayout();
    textStack.Children.Add(title);
    textStack.Children.Add(msg);
    MainStackLayout.Children.Add(textStack);
    MainStackLayout.Children.Add(btn);
    Content = MainStackLayout;

    // Default visibility is hidden
    IsVisible = false;

}

Upvotes: 3

Views: 1719

Answers (1)

Daniel P
Daniel P

Reputation: 3374

You can use Measure to get the height:

private async void AnimateIn(object sender, EventArgs e)
{
    var parent = (VisualElement)Parent;
    SizeRequest sizeRequest = MainStackLayout.Measure(parent.Width, parent.Height);

    MainStackLayout.TranslationY = -sizeRequest.Request.Height;

    IsVisible = true;

    await MainStackLayout.TranslateTo(0, 0, 1000);
}

Upvotes: 5

Related Questions