xamarin / XamarinCommunityToolkit

The Xamarin Community Toolkit is a collection of Animations, Behaviors, Converters, and Effects for mobile development with Xamarin.Forms. It simplifies and demonstrates common developer tasks building iOS, Android, and UWP apps with Xamarin.Forms.
MIT License
1.59k stars 471 forks source link

[Spec] Badge support for Shell #517

Closed Dresel closed 1 year ago

Dresel commented 5 years ago

Summary

There are requests for badge support for classic tabs and there are also some libraries out there which kinda support that. With shell it's not possible to use this libraries without touching / replacing too much of the shell internals so it would be great if shell supports this scenario out of the box. I couldn't find anything related in shell spec, that's why I'm proposing it here.

API Changes

Badges should be supported on all shell navigation patterns, which are

I propose the following properties for first iteration of badge support:

Primary properties

BaseShellItem (bindable properties)

Future implementations / considerations

Bottom tabs use the concept of the More tab if there are more than 5 items. All items with index >= 5 will be "hidden" by this more tab and handled by a platform specific context menu.

One use case would be the transformation of the underlying items hidden by this more tab. For example Item 5 (Messages) has "1" new item and Item 6 (Notifications) has "2" new items, so you could show "3" in the more tab, but the user would not how how this is split between the aggregated items.

We could think of modifying the text (e.g. for the example above "Messages (1)" and "Notifications (6)"). I'm proposing this because adding a badge to the underlying platform specific context menu could be too much work / impossible for a specific platform. Modifying the title value should definitely work though and we would see at least something.

Internal implementation

Android

Material components will support badges out of the box, which when I understood it correctly can be used when AndroidX is supported by Xamarin. In the meantime we have to supply a custom implementation like this for badges.

Flyout

Flyout menu is created by the ShellFlyoutTemplatedContentRenderer in combination with ShellFlyoutRecyclerAdapter. The data template is based on forms controls, so we just might use a Frame here for the default template. For custom templates the user has to provide the necessary controls and bind color, text, etc..

Bottom tabs

ShellItemRenderer uses BottomNavigationView, which can be used to retrieve the underlying BottomNavigationItemView items and inject the badge view.

The more tab uses a BottomSheetDialog.

Top tabs

ShellSectionRenderer uses TabLayout, which can be used to retrieve the underlaying TabView items and inject the badge view.

iOS

Flyout

Flyout menu is created by the ShellTableViewController in combination with ShellTableViewSource. The data template is based on forms controls, so we just might use a Frame here for the default template. For custom templates the user has to provide the necessary controls and bind color, text, etc..

Bottom tabs

ShellItemRenderer uses UITabBarController which can use the UITabBarItem to specify native Badge properties.

The more tab uses the default implementation of the MoreNavigationController of UITabBarController.

Top tabs

ShellSectionRootRenderer uses a UICollectionView, so you would have to modify ShellSectionHeaderCell and add a custom badge view (something like this).

Screenshots

TODO: Add them here

Feedback welcome.

samhouts commented 5 years ago

Sounds great to me! @davidortinau @pauldipietro Anything to add?

Dresel commented 5 years ago

I have started implementation for bottom tabs support (see fork), because this will cover most needs and is the easiest to implement.

How should I proceed? Should I go for a PR? Do you want to refine specification or implementation details?

Android

iOS

PureWeen commented 5 years ago

@Dresel can you create a draft PR against your fork and then we can check it out?

Dresel commented 5 years ago

Feel free to test.

Dresel commented 5 years ago

Little dot mode might be looking something like this (Android):

TheBaileyBrew commented 5 years ago

Badge support shouldn't be limited to only Tabs (top or bottom) - it should also be supported in the FlyoutMenu itself so the badges can be seen when the flyout opens and users don't need to navigate into each page to see the badges.

Dresel commented 5 years ago

Badge support shouldn't be limited to only Tabs (top or bottom) - it should also be supported in the FlyoutMenu itself so the badges can be seen when the flyout opens and users don't need to navigate into each page to see the badges.

Yes, it's on my (extended) list, might look similar to

Since Flyout Items are rendered with forms controls and can use custom templates, it's (from an implementations perspective) different though. I will update the spec according to that soon.

andreinitescu commented 5 years ago

Can we please have this for non Shell too? I am thinking that it should be first implemented in the non Shell (classic) and then surfaced in Shell.

If we start having having feature discrepancies between Shell and non-Shell that doesn’t sound good to me. I am referring to only features like this one, obviously not everything can or should be in non Shell, that’s the reason why Shell exists.

Dresel commented 5 years ago

Can we please have this for non Shell too? I am thinking that it should be first implemented in the non Shell (classic) and then surfaced in Shell.

If we start having having feature discrepancies between Shell and non-Shell that doesn’t sound good to me. I am referring to only features like this one, obviously not everything can or should be in non Shell, that’s the reason why Shell exists.

What's wrong with packages like xamarin-forms-tab-badge? Problem with shell is that it's not possible to use this libraries without touching / replacing too much of the shell internals, that's why I was proposing this in the first place.

andreinitescu commented 5 years ago

My suggestion is not related to that package, it might be a great package. I was only suggesting a way I think it's best to handle this, which is to give this new feature to non-Shell apps too. I just wanted to share my feedback, your proposal is great.

Dresel commented 5 years ago

Spec updated.

motoko89 commented 5 years ago

This is great! Hopefully it's available in pre-release soon

TraceFerrier commented 5 years ago

Yep, for our app to be able to continue to use the new Shell, this is a must! Thanks, and waiting anxiously!

codrinamerigo commented 5 years ago

Yep, for our app to be able to continue to use the new Shell, this is a must! Thanks, and waiting anxiously!

Also for our app! This is highly requested from the business side

alex-relov commented 5 years ago

when will the badge for shell appear in xamarin forms? Can you show an example of a renderer for Tab (ShellSection) with badge?

Dresel commented 5 years ago

I'm currently waiting for review / feedback from the XF team, I cannot tell you when this will be ready.

Can you show an example of a renderer for Tab (ShellSection) with badge?

If you want you can build the XF repo and play with the Control Gallery sample or check the PR sources.

imranaz commented 4 years ago

@Dresel do you have an ETA when badge support within toolbar and flyout menu will be available? We're released a Xamarin based app and user engagement is being impacted by the lack of badges. In-app badges are essential for all modern apps and I'm surprised that this feature is not getting prioritized on the Xamarin team. Thanks and looking forward to your feedback.

Dresel commented 4 years ago

@Dresel do you have an ETA when badge support within toolbar and flyout menu will be available? We're released a Xamarin based app and user engagement is being impacted by the lack of badges. In-app badges are essential for all modern apps and I'm surprised that this feature is not getting prioritized on the Xamarin team. Thanks and looking forward to your feedback.

PR is ready for review - hopefully soon 👍

jennablackwell commented 4 years ago

When is this going to be released?

Dresel commented 4 years ago

When is this going to be released?

I'm currently waiting for review. I don't know how work items are prioritized, maybe @samhouts can tell you more.

RodSanford commented 4 years ago

I'm pretty sure you have a team of testers ready to check it out, myself included.

jennablackwell commented 4 years ago

When is this going to be released?

I'm currently waiting for review. I don't know how work items are prioritized, maybe @samhouts can tell you more.

@samhouts will this be included anytime soon? Been waiting on this for awhile since switching to AppShell! It's pretty much expected in apps to have a red badge on a tab icon for notifications....

samhouts commented 4 years ago

We're still reviewing the PR. We hope to have it ready soon! Thanks for your patience!

mohamadayash commented 4 years ago

Hello @samhouts really this is an urgent in our software , I hope it will be applied so soon Thank you

SnowPowerCore commented 4 years ago

Hello. +1 assigning to the priority of this feature. It's a "must-have" thing for Xamarin.Forms development with the help of Shell.

sgreifeneder commented 4 years ago

What does "ready soon" mean? I don't know why this takes so long, it's open for more than a year now and a very basic feature. I hope it will be really ready soon ...

saiyamshah143 commented 4 years ago

+1

chadzhao commented 4 years ago

vNext+1 means 4.8.0? have it done in 4.8.0, please.

galadril commented 4 years ago

Im really waiting for this feature! Thanks @Dresel

Adlorem commented 4 years ago

I'm also waiting for this feature.

Remmo commented 4 years ago

June 2020, 13 months later, but Xamarin still doesn't supports badge on Shell tabs.... I know it's not your fault, but please @samhouts update us about this release, all my customers needs this feature. Thanks.

angelru commented 4 years ago

when can it be used?

galadril commented 4 years ago

FYI as a workaround, I've managed to get the badges working on iOS and Android on tabs / shells via renderers. Maybe you can use some of this code for yourself..

Android

[assembly: ExportRenderer(typeof(Shell), typeof(CustomShellRenderer))]
namespace Xamarin.Droid.Renderers
{
   /// <summary>
   /// The CustomShellRenderer is necessary in order to replace the ShellItemRenderer with your own.
   /// </summary>
   class CustomShellRenderer : ShellRenderer
   {
      public CustomShellRenderer(Context context) : base(context)
      { }

      protected override IShellBottomNavViewAppearanceTracker CreateBottomNavViewAppearanceTracker(ShellItem shellItem)
      {
         return new CustomShellBottomAppearance(this);
      }
   }
}

Then the CustomShellBottomAppearance.cs:


namespace Xamarin.Droid.Renderers
{
   internal class CustomShellBottomAppearance : IShellBottomNavViewAppearanceTracker
   {
      public CustomShellBottomAppearance(CustomShellRenderer shellRenderer)
      { }

      public void Dispose()
      { }

      public void ResetAppearance(BottomNavigationView bottomView)
      { }

      public async void SetAppearance(BottomNavigationView bottomView, IShellAppearanceElement appearance)
      {
         bottomView.LabelVisibilityMode = LabelVisibilityMode.LabelVisibilitySelected;
         var _bottomNavigationMenuView = (BottomNavigationMenuView)bottomView.GetChildAt(0);

         var badge = await MainViewModel.CheckNewEventsAsync();
         CreatePageBadge(2, badge > 0, badge, _bottomNavigationMenuView);
      }

      private void CreatePageBadge(int index, bool ShowBadge, int BadgeCount, BottomNavigationMenuView _bottomNavigationMenuView)
      {
         var itemView = (BottomNavigationItemView)_bottomNavigationMenuView.GetChildAt(index);
         if (ShowBadge && BadgeCount > 0)
         {
            var mtxtnotificationsbadge = itemView.FindViewById<TextView>(Resource.Id.txtbadge);
            if (mtxtnotificationsbadge == null)
            {
               var vBadge = LayoutInflater.From(CrossCurrentActivity.Current.Activity).Inflate(Resource.Layout.notification_badge, _bottomNavigationMenuView, false);
               itemView.AddView(vBadge);
               mtxtnotificationsbadge = itemView.FindViewById<TextView>(Resource.Id.txtbadge);
            }
            mtxtnotificationsbadge.Text = BadgeCount.ToString();
            if (mtxtnotificationsbadge.Text.Length == 1)
               mtxtnotificationsbadge.SetBackgroundResource(Resource.Drawable.custom_circle_shape);
            else
               mtxtnotificationsbadge.SetBackgroundResource(Resource.Drawable.custom_rectangle_shape);
         }
         else
         {
            var badgeLayout = itemView.FindViewById<FrameLayout>(Resource.Id.badge);
            if (badgeLayout != null)
               itemView.RemoveView(badgeLayout);
         }
      }
   }
}

then under resource/layout (notification_badge.axml):

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:id="@+id/badge"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/txtbadge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="top|center_horizontal"
        android:layout_marginLeft="10dp"
        android:layout_marginStart="10dp"
        android:gravity="center"
        android:padding="3dp"
        android:textColor="@color/white"
        android:textSize="11sp" />
</FrameLayout>

custom_rectangle_shape.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
   <solid
   android:color="#f00"/>
   <corners
   android:bottomRightRadius="60dp"
   android:bottomLeftRadius="60dp"
   android:topLeftRadius="60dp"
   android:topRightRadius="60dp" />
</shape>

custom_circle_shape.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
   <solid
   android:color="#f00"/>
   <corners
   android:bottomRightRadius="60dp"
   android:bottomLeftRadius="60dp"
   android:topLeftRadius="60dp"
   android:topRightRadius="60dp" />
</shape>

iOS

[assembly: ExportRenderer(typeof(Shell), typeof(CustomShellRenderer))]
namespace Xamarin.iOS.Renderers
{
   /// <summary>
   /// The CustomShellRenderer is necessary in order to replace the ShellItemRenderer with your own.
   /// </summary>
   class CustomShellRenderer : ShellRenderer
   {
      public CustomShellRenderer() : base()
      { }

      protected override IShellTabBarAppearanceTracker CreateTabBarAppearanceTracker()
      {
         return new CustomShellBottomAppearance();
      }
   }
}

And then the CustomShellBottomAppearance.cs:

namespace Xamarin.iOS.Renderers
{
   public class CustomShellBottomAppearance : IShellTabBarAppearanceTracker
   {
      public void Dispose()
      { }

      public void ResetAppearance(UITabBarController controller)
      { }

      public async void SetAppearance(UITabBarController controller, ShellAppearance appearance)
      {
         if (controller?.TabBar?.Items != null && controller.TabBar.Items.Any())
         {
            var badge = await MainViewModel.CheckNewEventsAsync();
            if (badge > 0)
               controller.TabBar.Items[2].BadgeValue = badge.ToString();
         }
      }
      public void UpdateLayout(UITabBarController controller)
      { }
   }
}

result on iOS: image

dan5602 commented 4 years ago

Thanks for the work around @galadril! Do you have the code for Resource.Drawable.custom_circle_shape and Resource.Drawable.custom_rectangle_shape?

galadril commented 4 years ago

Thanks for the work around @galadril! Do you have the code for Resource.Drawable.custom_circle_shape and Resource.Drawable.custom_rectangle_shape?

Ive added those drawable xml files to the comment as well.. sry

angelru commented 4 years ago

@galadril awesome!! many thanks!!
I imagine MainViewModel.CheckNewEventsAsync ()checks for new notifications?

galadril commented 4 years ago

@galadril awesome!! many thanks!! I imagine MainViewModel.CheckNewEventsAsync ()checks for new notifications?

yeah thats the async method that gets the new event count, to show the badge

pnda489 commented 4 years ago

Any Updates or ETA on this?

kklauder commented 4 years ago

In iOS you could set up a Subscription and call it from anywhere in your code. Building on @galadril example. In the renderer...

public async void SetAppearance(UITabBarController controller, ShellAppearance appearance) { if (controller?.TabBar?.Items != null && controller.TabBar.Items.Any()) {

            MessagingCenter.Subscribe<Shell, string>(
            this,
            "BadgeTab1",
            (shell, arg) =>
            {

                Device.BeginInvokeOnMainThread(() => controller.TabBar.Items[0].BadgeValue = arg);

            });

            MessagingCenter.Subscribe<Shell, string>(
            this,
            "BadgeTab2",
            (shell, arg) =>
            {

                Device.BeginInvokeOnMainThread(() => controller.TabBar.Items[1].BadgeValue = arg);

            });

        }
    }

Then you can call from inside your tab.

MessagingCenter.Send(Shell.Current, "BadgeTab2", "1");

chadzhao commented 4 years ago

I have another solution.

Advantage

Disadvantage

1. Hide tab bar in AppShell page

<Shell Shell.TabBarIsVisible="False">
  Your layout
</Shell>

It will keep hiding all time without manually hide in other page.

2. Create your own tab bar view by creating a custom Grid view

<Grid x:Class="MyLib.Views.MyTab" BackgroundColor="#2196F3" ColumnSpacing="0">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>

    # Tabbar layout
    <StackLayout Grid.Column="0" Padding="0,5,0,0" Orientation="Vertical">
        <Image Source="course.png" WidthRequest="30" HeightRequest="30"></Image>
        <Label Text="MyCourse" TextColor="White" HorizontalOptions="Center"></Label>
    </StackLayout>

    # Handle tapped, show badge, show selected mask
    <AbsoluteLayout Grid.Column="0" x:Name="lay0">
        <AbsoluteLayout.GestureRecognizers>
            <TapGestureRecognizer Tapped="OnSelected" CommandParameter="0"></TapGestureRecognizer>
        </AbsoluteLayout.GestureRecognizers>

        <Grid AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,1,1,1" IsVisible="true" BackgroundColor="White" Opacity="0.2"></Grid>
        <Frame AbsoluteLayout.LayoutFlags="PositionProportional" AbsoluteLayout.LayoutBounds=".9,0.1,12,12" CornerRadius="6" BackgroundColor="#cf1322" Padding="0"></Frame>
    </AbsoluteLayout>

     Other Tabs Here
</Grid>

TIPS: MyLib.Views.MyTab needs to inherit from Grid.

3. Add switch tab logic in MyTab.xaml.cs

private void OnSelected(object sender, EventArgs e)
{
    if (e is TappedEventArgs tap)
    {
        var newIndex = Convert.ToInt32(tap.Parameter);
        var shell = Shell.Current;
        shell.CurrentItem.CurrentItem = shell.CurrentItem.Items[newIndex];
    }
}

int selectedIndex = -1;
public int SelectedIndex
{
    get { return selectedIndex; }
    set
    {
        if (selectedIndex != value)
        {
            selectedIndex = value;

            switch (value)
            {
                case 0: this.lay0.Children[0].IsVisible = false; break;
                case 1: this.lay1.Children[0].IsVisible = false; break;
                case 2: this.lay2.Children[0].IsVisible = false; break;
                case 3: this.lay3.Children[0].IsVisible = false; break;
            }
        }
    }
}

4. Add MyTab to all tab pages which shown in AppShell page

<Grid Margin="0" RowSpacing="0">
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition Height="60" />
    </Grid.RowDefinitions>

    <local:MyTab Grid.Row="1" SelectedIndex="0"></local:MyTab>

     Your original page layout here
</Grid>

Result on Android screenshot

thuyetpv commented 4 years ago

@galadril Sorry, I have a question. How to reset BadgeCount value on android?

tawreyhap commented 4 years ago

@galadril Thank you for your work on providing a workaround example for shell tabbar badges. Is there any chance you could please share the XAML referenced in CustomShellBottomAppearance.cs's SetAppearance method for the iOS platform?

galadril commented 4 years ago

@galadril How to reset BadgeCount value on android?

You can use MessagingCenter for this?

MessagingCenter.Send(this, App.MessageNameRemoveBadge);

Then in the SetAppearance : MessagingCenter.Subscribe<MainPage>(this, App.MessageNameRemoveBadge, async app => await ClearBadgeAsync());

galadril commented 4 years ago

@galadril Thank you for your work on providing a workaround example for shell tabbar badges. Is there any chance you could please share the XAML referenced in CustomShellBottomAppearance.cs's SetAppearance method for the iOS platform?

There is not XAML reference in the iOS CustomShellBottomAppearance.cs's SetAppearance ...?

kamranmasud commented 4 years ago

FYI as a workaround, I've managed to get the badges working on iOS and Android on tabs / shells via renderers. Maybe you can use some of this code for yourself..

Android

[assembly: ExportRenderer(typeof(Shell), typeof(CustomShellRenderer))]
namespace Xamarin.Droid.Renderers
{
   /// <summary>
   /// The CustomShellRenderer is necessary in order to replace the ShellItemRenderer with your own.
   /// </summary>
   class CustomShellRenderer : ShellRenderer
   {
      public CustomShellRenderer(Context context) : base(context)
      { }

      protected override IShellBottomNavViewAppearanceTracker CreateBottomNavViewAppearanceTracker(ShellItem shellItem)
      {
         return new CustomShellBottomAppearance(this);
      }
   }
}

Then the CustomShellBottomAppearance.cs:


namespace Xamarin.Droid.Renderers
{
   internal class CustomShellBottomAppearance : IShellBottomNavViewAppearanceTracker
   {
      public CustomShellBottomAppearance(CustomShellRenderer shellRenderer)
      { }

      public void Dispose()
      { }

      public void ResetAppearance(BottomNavigationView bottomView)
      { }

      public async void SetAppearance(BottomNavigationView bottomView, IShellAppearanceElement appearance)
      {
         bottomView.LabelVisibilityMode = LabelVisibilityMode.LabelVisibilitySelected;
         var _bottomNavigationMenuView = (BottomNavigationMenuView)bottomView.GetChildAt(0);

         var badge = await MainViewModel.CheckNewEventsAsync();
         CreatePageBadge(2, badge > 0, badge, _bottomNavigationMenuView);
      }

      private void CreatePageBadge(int index, bool ShowBadge, int BadgeCount, BottomNavigationMenuView _bottomNavigationMenuView)
      {
         var itemView = (BottomNavigationItemView)_bottomNavigationMenuView.GetChildAt(index);
         if (ShowBadge && BadgeCount > 0)
         {
            var mtxtnotificationsbadge = itemView.FindViewById<TextView>(Resource.Id.txtbadge);
            if (mtxtnotificationsbadge == null)
            {
               var vBadge = LayoutInflater.From(CrossCurrentActivity.Current.Activity).Inflate(Resource.Layout.notification_badge, _bottomNavigationMenuView, false);
               itemView.AddView(vBadge);
               mtxtnotificationsbadge = itemView.FindViewById<TextView>(Resource.Id.txtbadge);
            }
            mtxtnotificationsbadge.Text = BadgeCount.ToString();
            if (mtxtnotificationsbadge.Text.Length == 1)
               mtxtnotificationsbadge.SetBackgroundResource(Resource.Drawable.custom_circle_shape);
            else
               mtxtnotificationsbadge.SetBackgroundResource(Resource.Drawable.custom_rectangle_shape);
         }
         else
         {
            var badgeLayout = itemView.FindViewById<FrameLayout>(Resource.Id.badge);
            if (badgeLayout != null)
               itemView.RemoveView(badgeLayout);
         }
      }
   }
}

then under resource/layout (notification_badge.axml):

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:id="@+id/badge"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/txtbadge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="top|center_horizontal"
        android:layout_marginLeft="10dp"
        android:layout_marginStart="10dp"
        android:gravity="center"
        android:padding="3dp"
        android:textColor="@color/white"
        android:textSize="11sp" />
</FrameLayout>

custom_rectangle_shape.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
   <solid
   android:color="#f00"/>
   <corners
   android:bottomRightRadius="60dp"
   android:bottomLeftRadius="60dp"
   android:topLeftRadius="60dp"
   android:topRightRadius="60dp" />
</shape>

custom_circle_shape.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
   <solid
   android:color="#f00"/>
   <corners
   android:bottomRightRadius="60dp"
   android:bottomLeftRadius="60dp"
   android:topLeftRadius="60dp"
   android:topRightRadius="60dp" />
</shape>

iOS

[assembly: ExportRenderer(typeof(Shell), typeof(CustomShellRenderer))]
namespace Xamarin.iOS.Renderers
{
   /// <summary>
   /// The CustomShellRenderer is necessary in order to replace the ShellItemRenderer with your own.
   /// </summary>
   class CustomShellRenderer : ShellRenderer
   {
      public CustomShellRenderer() : base()
      { }

      protected override IShellTabBarAppearanceTracker CreateTabBarAppearanceTracker()
      {
         return new CustomShellBottomAppearance();
      }
   }
}

And then the CustomShellBottomAppearance.cs:

namespace Xamarin.iOS.Renderers
{
   public class CustomShellBottomAppearance : IShellTabBarAppearanceTracker
   {
      public void Dispose()
      { }

      public void ResetAppearance(UITabBarController controller)
      { }

      public async void SetAppearance(UITabBarController controller, ShellAppearance appearance)
      {
         if (controller?.TabBar?.Items != null && controller.TabBar.Items.Any())
         {
            var badge = await MainViewModel.CheckNewEventsAsync();
            if (badge > 0)
               controller.TabBar.Items[2].BadgeValue = badge.ToString();
         }
      }
      public void UpdateLayout(UITabBarController controller)
      { }
   }
}

result on iOS: image

Brother how can i set the badge in my class library main project? I have copied all this code..All my project backend code is in main library project..Now i want to set value of badge and show it on tab..How can i do this in mainpage.xaml file?

tawrey commented 4 years ago

@galadril Thank you for your work on providing a workaround example for shell tabbar badges. Is there any chance you could please share the XAML referenced in CustomShellBottomAppearance.cs's SetAppearance method for the iOS platform?

There is not XAML reference in the iOS CustomShellBottomAppearance.cs's SetAppearance ...?

Very true @galadril there is no XAML referenced in your iOS platform's CustomShellBottomAppearance.cs's SetAppearance method but where we assign the badge value:  "controller.TabBar.Items[2].BadgeValue = badge.ToString();". We are setting the value to a badge which I assume is either created on the client XAML side or in code for the iOS implementation of the Shell's tabbar. I am curious to see how you added the badges for iOS to the tabbar. Thank you in advance for your help.

galadril commented 4 years ago

At this moment I have a public static method that returns an int with the badge value: var badge = await MainViewModel.CheckNewEventsAsync();

But you can easily use MessagingCenter for this, to subscribe for a new badge value.. Its up to you.

angelru commented 4 years ago

I have noticed that SetAppearance is called twice.

I for example, use a static variable and then in the notifications tab, I do my logic.

    protected override async void OnNavigating(ShellNavigatingEventArgs args)
    {
        base.OnNavigating(args);

        if (Current?.CurrentItem == null) return;

        var s = args.Target.Location.ToString();

        if (s.Contains("notificationsTab"))
        {
           //do something
        }
    }
jmelendez025 commented 4 years ago

@galadril Thank you for your work on providing a workaround example for shell tabbar badges. Is there any chance you could please share the XAML referenced in CustomShellBottomAppearance.cs's SetAppearance method for the iOS platform?

There is not XAML reference in the iOS CustomShellBottomAppearance.cs's SetAppearance ...?

Very true @galadril there is no XAML referenced in your iOS platform's CustomShellBottomAppearance.cs's SetAppearance method but where we assign the badge value:  "controller.TabBar.Items[2].BadgeValue = badge.ToString();". We are setting the value to a badge which I assume is either created on the client XAML side or in code for the iOS implementation of the Shell's tabbar. I am curious to see how you added the badges for iOS to the tabbar. Thank you in advance for your help.

Hi @tawrey , I have the same question. Did you find how the XAML is?

jmelendez025 commented 4 years ago

At this moment I have a public static method that returns an int with the badge value: var badge = await MainViewModel.CheckNewEventsAsync();

But you can easily use MessagingCenter for this, to subscribe for a new badge value.. Its up to you.

@galadril Do you have a repository in witch i can see your complete solution?

I would like to see how do you create your shell xaml.