Skip to content

Adding Active Inactive Tint Color #42

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

Merged

Conversation

Stringsaeed
Copy link
Contributor

@Stringsaeed Stringsaeed commented Oct 14, 2024

PR Description:

This pull request introduces a new feature that adds support for active/inactive tint colors on tabs across both Android and iOS components.

Notes:

  • Color Handling:
    The colors are sourced from the react-navigation theme by default. This follows the same approach used by @react-navigation/bottom-tabs. As a result, I added dependencies for color and @types/color to handle this functionality.

  • Android Implementation:
    My Kotlin code might feel more Java-like, as I'm more familiar with Java. So, you’ll notice some usage of as and if (x != null) for type narrowing.

  • iOS Implementation:
    As you may know, SwiftUI Tabs rely on the appearance of the UIKit tab bar. However, SwiftUI doesn't provide a built-in way to customize the inactive (unselected) tint color. To work around this, I had to leverage the UIKit appearance settings.

@Stringsaeed Stringsaeed marked this pull request as ready for review October 14, 2024 05:10
@Stringsaeed Stringsaeed mentioned this pull request Oct 14, 2024
6 tasks
@okwasniewski okwasniewski self-requested a review October 14, 2024 07:12
@okwasniewski
Copy link
Collaborator

Thanks for working on this!

I'll try to review this PR in the upcoming days 🙌

@okwasniewski
Copy link
Collaborator

okwasniewski commented Oct 15, 2024

For some reason examples other than "Tint Colors" don't work for me. Can you do a rebase and check if that's the case for you?

@Stringsaeed Stringsaeed force-pushed the feat/active-inactive-tint-color branch from b9f0508 to 7aa0423 Compare October 15, 2024 07:15
@Stringsaeed
Copy link
Contributor Author

@okwasniewski fixed ✅

@okwasniewski
Copy link
Collaborator

okwasniewski commented Oct 16, 2024

Thanks for bringing in the fixes!

I looked into JS Bottom Tabs and it looks like they have tabBarActiveTintColor and tabBarInactiveTintColor as options that you can pass to the screen. So you can technically have different tints for each item. I would prefer to match the JS bottom tabs API as closely as possible. Do you think we can change the way it works to be per tab?

@Stringsaeed Stringsaeed force-pushed the feat/active-inactive-tint-color branch from 16ccec8 to 2e9df96 Compare October 16, 2024 21:24
@Stringsaeed
Copy link
Contributor Author

Yes we can add the same logic per item on select event but I don't know if it will be the best implementation for the performance

I will experiment it and push the code for it

@@ -9,7 +9,7 @@ const Tab = createNativeBottomTabNavigator();

function NativeBottomTabs() {
return (
<Tab.Navigator>
<Tab.Navigator inactiveTintColor="blue">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For JS Bottom tabs we have 2 props: tabBarActiveTintColor, tabBarInActiveTintColor to provide this functionality, which are part of screenOptions.

To make API close to JS one don't you think we should include this inside screenOptions and match names as well?

@Stringsaeed
Copy link
Contributor Author

Stringsaeed commented Oct 17, 2024

@okwasniewski

The thing is in SwiftUI we can't have different color for each tab, we will have to make it only different tint when selected only but we can't have different inactive color as there's no a modifier to say that color for inactive color

So when we define inactive color in each screen we will have to use that for our current all other tab active color that's the current implementation draft I have:

func updateUITabBarAppearance() {
  let appearance = UITabBarAppearance()

  if let inactiveTintColor = getInactiveTintColor()  {
    appearance.stackedLayoutAppearance.normal.titleTextAttributes = [.foregroundColor: inactiveTintColor]
    appearance.stackedLayoutAppearance.normal.iconColor = inactiveTintColor
  }

  if let activeTintColor = getActiveTintColor() {
    appearance.stackedLayoutAppearance.selected.titleTextAttributes = [.foregroundColor: activeTintColor]
  }
    
  if #available(iOS 15.0, *) {
    // This causes issues with lazy loading making the TabView background blink.
    UITabBar.appearance().scrollEdgeAppearance = appearance
  }

  UITabBar.appearance().standardAppearance = appearance
}

and this will result in different effect than JS one for SwiftUI it's a limitation to just follow Apple Design Guide

What can do based on configuration in UITabBar appearance

  • Active/Inactive Colors for icons and labels
  • We can use the image icon as it's with .renderingMode(.template) but the user must add the colored image based on focusability of the screen something like this:
     <Tab.Screen
         name="Article"
         component={Article}
         options={{
           tabBarBadge: '10',
           tabBarIcon: ({ focused }) =>
             focused
               ? require('../../assets/icons/focused_person_dark.png')
               : require('../../assets/icons/unfocused_person_dark.png'),
         }}
     />
    And this will not let us to color the image whatsoever

cc: @shubhamguptadream11

@okwasniewski
Copy link
Collaborator

@Stringsaeed

So activeTintColor can be handled using SwiftUI only, there is no need to additional UIKit code as we can use .tintColor()

We can also easily achieve unselectedTintColor but for all tabs:

UITabBar.appearance().unselectedItemTintColor = inactiveTintColor

Maybe we can make activeTintColor per tab item and inactive for the whole tab bar? Im not sure if there is any better way as it looks like a limitation of UITabBar

@Stringsaeed
Copy link
Contributor Author

@okwasniewski

Unfortunately we can't use this

UITabBar.appearance().unselectedItemTintColor = inactiveTintColor

As we define our own appearance

Here is an example of:

no override for appearance override for appearance
Screenshot 2024-10-17 at 11 09 38 PM Screenshot 2024-10-17 at 11 09 27 PM

So we have to stick with our method for inactive tint colors

@Stringsaeed Stringsaeed force-pushed the feat/active-inactive-tint-color branch from 2e9df96 to 3aa9bbf Compare October 17, 2024 19:31
@okwasniewski
Copy link
Collaborator

So as I mentioned before, we can do:

function NativeBottomTabs() {
  return (
    <Tabs.Navigator tabBarInactiveTintColor="orange">
      <Tabs.Screen
        name="index"
        options={{
          title: "Home",
          tabBarIcon: () => ({ sfSymbol: "house" }),
          tabBarActiveTintColor: "red"
        }}
      />
      <Tabs.Screen
        name="explore"
        options={{
          title: "Explore",
          tabBarIcon: () => ({ sfSymbol: "person" }),
          tabBarActiveTintColor: "orange"
        }}
      />
    </Tabs.Navigator>
  );
}

Separate tint color for each tab, but one inactive color. This is a tradeoff but we have to stick to what native components allow us to achieve. (Let's describe this decision in docs)

@okwasniewski
Copy link
Collaborator

Hey @Stringsaeed,

I don't want to rush you in any way but this is a crucial feature for the library and users are asking me for it.

Let me know if you currently don't have the time to work on this so someone else can pick it up where you left off. (cc: @shubhamguptadream11 @sarthak-d11)

@Stringsaeed
Copy link
Contributor Author

@okwasniewski
Actually I was going to finish it today you can except an update in 10 hours around 9:00 PM (Poland time)

@okwasniewski
Copy link
Collaborator

@okwasniewski Actually I was going to finish it today you can except an update in 10 hours around 9:00 PM (Poland time)

Awesome, thank you @Stringsaeed 🎉

@Stringsaeed Stringsaeed force-pushed the feat/active-inactive-tint-color branch from 11b832c to d08b48c Compare October 22, 2024 19:11
@okwasniewski
Copy link
Collaborator

Thanks for working on this!

I've updated the PR with docs changes and fixes for both platforms.

Now we get support for:

  • activeTintColor (navigator level)
  • inactiveTintColor (navigator level)
  • activeTintColor (per tab)
CleanShot.2024-10-23.at.09.45.00.mp4

Copy link
Collaborator

@okwasniewski okwasniewski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@okwasniewski okwasniewski merged commit d499559 into callstackincubator:main Oct 23, 2024
5 checks passed
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

Successfully merging this pull request may close these issues.

3 participants