Xamarin.forms: [F100] Translucent Nav Bar

Created on 26 Jan 2018  Â·  15Comments  Â·  Source: xamarin/Xamarin.Forms

Rationale

Currently when setting the navigation bar to Translucent requires a custom renderer. In addition the Xamarin Forms content pages don't account for the extra space now created by the translucent bar. Having a property on the NavigationBar to allow it to be transparent and then having the content flow correctly would be useful

Implementation

public static readonly BindableProperty IsTranslucentProperty =
     BindableProperty.Create(propertyName: nameof(IsTranslucent),
         returnType: typeof(bool), declaringType: typeof(NavigationPage), defaultValue: false);

Navigation Renderer will set the Nav Bar to be translucent and then the contained pages will fill all available space

Expected Result

Android

  • NavigationPage.IsTranslucent = true: Should cause the navigation bar to become translucent. Content Pages should shifted and measured as if the Navigation Bar doesn't exist

iOS

See Android

UWP

See Android

Implications for CSS

Not sure if this translates to the same concept as making the navbar translucent as "translucents" also has content flow implications. Could possibly map opacity >= 0.5 to setting the IsTranslucent Property to true

navigationpage {opacity: 0.5; } 

Backward Compatibility

Third party renderers may need to be updated to ensure that this functionality is supported through the new official mechanism. Further we will need to be careful to code the changes to the renderers in a careful manner to ensure that if someone is already using an effect support this feature that the effect is as best as possible not broken by our changes.

Difficulty : Medium

https://bugzilla.xamarin.com/show_bug.cgi?id=24500

F100 community-sprint inactive high impact proposal-accepted roadmap enhancement âž•

Most helpful comment

Guys,
Any update on this ?

All 15 comments

@rmarinho It would appear that is only for iOS? This may be because on Android a translucent status bar is ordinarily done through themes which will be challenging to achieve with Xamarin Forms....

androiddraw
I added an attachableprop to NavigationPage (bool) and adjusted the layout depending on it in NavigationPageRenderer, it works just like the HasNavigationBar property.
But this is for android only, would you even accept a PR that just covers one platform or is it better if i make is as a platformspecific property?

What you see is 2 pages, one with DrawBehind = true and one false.
I change the color from blue to transparent when navigating

I was searching for an Android implementation of a translucent NavigationPage toolbar and found this PR: #1287 It seems to handle the Android side of things. Looking forward to having this in Xamarin.Forms 3.0 :-)

@bentmar I would like to implement the same effect on android as you showed in your post, do you have any source code for this? (if you are willing to share ofcourse)

@bentmar
Nevermind, already found it myself :)

@jelle-vdst do you mind to share? I haven't found it yet.

@5yunus2efendi

I've made a custom navigationPage adding a Translucent (bool) property and a translucent change event

then i've made a custom navigation renderer and overridden the OnLayout like this:

`protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
base.OnLayout(changed, l, t, r, b);

        if (!((NavigationExPage)Element).TranslucentToolbar)
            return;

            AToolbar bar = _toolbar;

        int containerHeight = b - t;

        int barHeight = _actionbarHeight;

        var pageController = Element;
        pageController.ContainerArea = new Rectangle(0, 0, Context.FromPixels(r - l), Context.FromPixels(containerHeight));


        for (var i = 0; i < ChildCount; i++)
        {
            AView child = GetChildAt(i);

            if (child is AToolbar)
                return;

            bar.Layout(0, -1000, r, barHeight - 1000);
            child.Layout(0, 0, r, b);
        }

    }`

I've also added this:

` public NavigationPageExRenderercs(Context context) : base(context)
{
_color = Color.ParseColor("#2196F3");
_standardTextColor = Xamarin.Forms.Color.White;
}

    protected override void OnElementChanged(ElementChangedEventArgs<NavigationPage> e)
    {
        base.OnElementChanged(e);
        if (e.NewElement != null)
        {
            ((NavigationExPage) e.NewElement).TranslucentChange += ColorSwitch;
        }
        if (e.OldElement != null)
        {
            ((NavigationExPage)e.OldElement).TranslucentChange -= ColorSwitch;
        }
    }

    private void ColorSwitch(object sender, EventArgs e)
    {
        for (var i = 0; i < ChildCount; i++)
        {
            AView toolbar = GetChildAt(i);

            if (toolbar is AToolbar)
            {
                if (((NavigationExPage)Element).TranslucentToolbar)
                {
                    Element.BarTextColor = Xamarin.Forms.Color.Transparent;
                    toolbar.SetBackgroundColor(Color.Transparent);
                }
                else
                {
                    Element.BarTextColor = _standardTextColor;
                    toolbar.SetBackgroundColor(_color);
                }

            }
        }
    }


    public override void OnViewAdded(View child)
    {
        base.OnViewAdded(child);

        if (((NavigationExPage)Element).TranslucentToolbar)
        {
            if (child is AToolbar)
            {
                var toolbar = (AToolbar)child;
                toolbar.SetBackgroundColor(Color.Transparent);
                GetToolbar();
                _actionbarHeight = ActionBarHeight();
            }
        }
    }



    private void GetToolbar()
    {
        Context context = Context;
        var activity = (FormsAppCompatActivity)context;

        AToolbar bar;
        if (FormsAppCompatActivity.ToolbarResource != 0)
            bar = activity.LayoutInflater.Inflate(FormsAppCompatActivity.ToolbarResource, null).JavaCast<AToolbar>();
        else
            bar = new AToolbar(context);

        _toolbar = bar;
    }

    private int ActionBarHeight()
    {
        int attr = Resource.Attribute.actionBarSize;

        int actionBarHeight;
        using (var tv = new TypedValue())
        {
            actionBarHeight = 0;
            if (Context.Theme.ResolveAttribute(attr, tv, true))
                actionBarHeight = TypedValue.ComplexToDimensionPixelSize(tv.Data, Resources.DisplayMetrics);
        }

        if (actionBarHeight <= 0)
            return Device.Info.CurrentOrientation.IsPortrait() ? (int)Context.ToPixels(56) : (int)Context.ToPixels(48);

        if (((Activity)Context).Window.Attributes.Flags.HasFlag(WindowManagerFlags.TranslucentStatus) || ((Activity)Context).Window.Attributes.Flags.HasFlag(WindowManagerFlags.TranslucentNavigation))
        {
            if (_toolbar.PaddingTop == 0)
                _toolbar.SetPadding(0, GetStatusBarHeight(), 0, 0);

            return actionBarHeight + GetStatusBarHeight();
        }

        return actionBarHeight;
    }

    int GetStatusBarHeight()
    {
        if (_statusbarHeight > 0)
            return _statusbarHeight;

        int resourceId = Resources.GetIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0)
            _statusbarHeight = Resources.GetDimensionPixelSize(resourceId);

        return _statusbarHeight;
    }`

The ActionBarHeight and GetToolBar method i've copied out of the xamarin forms renderer (based all my code on what happens in the standard renderer). The code still needs to be optimized (for example switching from a page with translucent to a page without is not smooth yet, it's still a work in progress)

@jelle-vdst It works!, thanks.

@jelle-vdst I wouldn't write such custom code to create a translucent navigation bar as it is likely that it will be interfering with how XF is laying out controls (now or in the future). Xamarin should support this functionality out of the box.

@bentmar With regards to DrawBehind, this works nicely if the page has already appeared and you can toggle this property on and off which most people most likely wouldn't.

A more complicated scenario should be supported by XF. Suppose one page set DrawBehind to false while the one to be pushed on the stack set it to true. What will switching from false to true look like if the page transition animation was custom on Android? Suppose it was more like iOS where pages enter in from the right and exit to the right. How will DrawBehind work with this transition?

@rmarinho If I'm not mistaken, iOS transcluency does not get rid of the navbar as in the case of @bentmar's DrawBehind gif even if BarBackgroundColor is transparent, so I believe this enhancement proposal is going to be similar to how iOS is working now.

@adrianknight89 I agree, but I need the functionality right now, the moment it's supported, I'll kick out my custom renderer. But right now I need it, I only need the transparency on one page and it works smootly for the moment.

@jelle-vdst Can you share me your navigationbar renderer make for this ?

@jelle-vdst I thought that the translucent navbar was already available with XF3, as we can see this in the ConferenceVision Xamarin sample. But they used a similar approach to yours. Do we know when this feature will be officially supported? And would you have a Github project that describes your implementation?

I've implemented the solution found in the ConferenceVision sample, but I meet a strange behavior that I describe there: the rendering is not the same between iOS 10.1 and iOS 11.3. If you have any suggestion, you're welcome!

Guys,
Any update on this ?

Was this page helpful?
0 / 5 - 0 ratings