maui icon indicating copy to clipboard operation
maui copied to clipboard

Fixed Switch Thumb and On Colors

Open kubaflo opened this issue 1 year ago • 11 comments

Description of Change

The initial respect for the ThumbColor and OnColor properties was overridden by values set within visual states each time the switcher changed its state. I recommend verifying whether these values have been overwritten, and if so, exclusively utilizing the new properties.

Issues Fixed

Fixes https://github.com/dotnet/maui/issues/19883

Fixes https://github.com/dotnet/maui/issues/19380

Before
After

kubaflo avatar Feb 03 '24 23:02 kubaflo

Hey there @kubaflo! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

ghost avatar Feb 03 '24 23:02 ghost

/azp run

jsuarezruiz avatar Feb 05 '24 07:02 jsuarezruiz

Azure Pipelines successfully started running 3 pipeline(s).

azure-pipelines[bot] avatar Feb 05 '24 07:02 azure-pipelines[bot]

Any reason this has been sitting unmerged for a month? Seems like a pretty basic and critical bug to be sitting unfixed for months now

BlueRaja avatar Mar 05 '24 11:03 BlueRaja

/azp run MAUI-UITests-public

rmarinho avatar Mar 05 '24 11:03 rmarinho

Azure Pipelines successfully started running 1 pipeline(s).

azure-pipelines[bot] avatar Mar 05 '24 11:03 azure-pipelines[bot]

@jsuarezruiz @rmarinho Just wanted to ask why this has not been merged to main for such a long time now? Is there anything missing? Still waiting for this to be fixed. And this fix was provided long time ago. Any reason for that?

MAUIoxo avatar Mar 19 '24 17:03 MAUIoxo

@MAUIoxo we are not sure this is correct fix to work with VSM. we were discussing internally.

rmarinho avatar Mar 20 '24 12:03 rmarinho

Thanks for the update on this! So, at least we know why it is parked 😊

MAUIoxo avatar Mar 20 '24 15:03 MAUIoxo

@MAUIoxo we are not sure this is correct fix to work with VSM. we were discussing internally.

Hi, was the discussion effective ? This is an unbearable suspense.

elparasite avatar Apr 12 '24 07:04 elparasite

@MAUIoxo we are not sure this is correct fix to work with VSM. we were discussing internally.

Hi, was the discussion effective ? This is an unbearable suspense.

Yes it was - I found out that this issue is going to take forever to be fixed and probably no one is going to deal with a solution anytime soon, so I guess I'll just have to come up with my own solution 😅

Here is an example of a CustomSwitch with a CustomSwitchHandler that allows me to set the OnColor and ThumbColor as in a normal Switch in XAML and map the states of IsToggled, OnColor and ThumbColor to the corresponding properties of the newly created iOS UISwitch. This is just a possible solution for iOS. I've tested it with scrolling also as re-drawing View elements also has to be updated correspondingly. At least it works for me and tested with <MauiVersion>8.0.40-nightly.10485</MauiVersion>.


CustomSwitch:

namespace OptimizerApp.Pages.Views.Controls.CustomSwitch
{
    public class CustomSwitch : Switch
    {
        
    }
}

Integration in XAML Code:

...
<constrols:CustomSwitch ... IsToggled="{Binding IsSelected, Mode=TwoWay}" ThumbColor="{StaticResource Gray100}" OnColor="{StaticResource DarkOrange1}" />
...

CustomSwitchHandler:

#if IOS

using OptimizerApp.Pages.Views.Controls.CustomSwitch;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
using UIKit;

namespace OptimizerApp.Platforms.iOS.ControlHandlers
{
    public partial class CustomSwitchHandler : ViewHandler<CustomSwitch, UISwitch>
    {
        /// <summary>
        /// Configures property mappings for the CustomSwitch and binds UI properties to platform-specific handlers.
        /// This mapper directs changes in MAUI's CustomSwitch properties to appropriate methods that
        /// update the native iOS UISwitch, ensuring UI consistency and responsiveness:
        /// - 'IsToggled' updates the switch's active state.
        /// - 'OnColor' adjusts the color when the switch is on.
        /// - 'ThumbColor' changes the color of the switch thumb.
        /// These mappings ensure that the visual state of the UISwitch is synchronized with the CustomSwitch.
        /// </summary>
        public static PropertyMapper<CustomSwitch, CustomSwitchHandler> CustomSwitchMapper = new PropertyMapper<CustomSwitch, CustomSwitchHandler>
        {
            [nameof(CustomSwitch.IsToggled)] = MapIsToggled,
            [nameof(CustomSwitch.OnColor)] = MapOnColor,
            [nameof(CustomSwitch.ThumbColor)] = MapThumbColor
        };

        
        public CustomSwitchHandler() : base(CustomSwitchMapper)
        {
        }

        /// <summary>
        /// Creates the native UISwitch view
        /// </summary>
        /// <returns>The newly created UISwitch instance</returns>
        protected override UISwitch CreatePlatformView()
        {
            return new UISwitch();
        }

        /// <summary>
        /// Connects this handler to the UISwitch, setting up event handlers and initial state
        /// </summary>
        /// <param name="platformView">The UISwitch to connect</param>
        protected override void ConnectHandler(UISwitch platformView)
        {
            base.ConnectHandler(platformView);

            platformView.ValueChanged += OnSwitchValueChanged;
            platformView.On = VirtualView.IsToggled;    // Synchronize the UISwitch's state with the IsToggled property of the MAUI CustomSwitch

            SetSwitchColors(platformView, VirtualView);
        }

        /// <summary>
        /// Disconnects this handler from the UISwitch and clean up the event handlers
        /// </summary>
        /// <param name="platformView">The UISwitch to disconnect</param>
        protected override void DisconnectHandler(UISwitch platformView)
        {
            platformView.ValueChanged -= OnSwitchValueChanged;
            base.DisconnectHandler(platformView);

            // Reset UI properties
            ResetSwitchColors(platformView);
        }

        /// <summary>
        /// Sets the ThumbColor and OnTintColor of the UISwitch based on the properties of the CustomSwitch
        /// </summary>
        /// <param name="uiSwitch">The native UISwitch</param>
        /// <param name="customSwitch">The MAUI CustomSwitch</param>
        private static void SetSwitchColors(UISwitch uiSwitch, CustomSwitch customSwitch)
        {
            // Setting the null-values will set it to default values
            uiSwitch.ThumbTintColor = customSwitch.ThumbColor != default(Color) ? customSwitch.ThumbColor.ToPlatform() : null;
            uiSwitch.OnTintColor = customSwitch.OnColor != default(Color) ? customSwitch.OnColor.ToPlatform() : null;
        }

        /// <summary>
        /// Resets the ThumbColor and OnTintColor properties of the UISwitch to defaults
        /// </summary>
        /// <param name="uiSwitch">The UISwitch to reset</param>
        private void ResetSwitchColors(UISwitch uiSwitch)
        {
            uiSwitch.ThumbTintColor = null;
            uiSwitch.OnTintColor = null;
        }

        /// <summary>
        /// Map changes in the OnColor property to the UISwitch
        /// </summary>
        public static void MapOnColor(CustomSwitchHandler handler, CustomSwitch customSwitch)
        {
            if (handler.PlatformView != null)
            {
                handler.PlatformView.OnTintColor = customSwitch.OnColor.ToPlatform();
            }
        }

        /// <summary>
        /// Map changes in the ThumbColor property to the UISwitch
        /// </summary>
        public static void MapThumbColor(CustomSwitchHandler handler, CustomSwitch customSwitch)
        {
            if (handler.PlatformView != null)
            {
                handler.PlatformView.ThumbTintColor = customSwitch.ThumbColor.ToPlatform();
            }
        }

        /// <summary>
        /// Map changes in the IsToggled property to the UISwitch
        /// </summary>
        public static void MapIsToggled(CustomSwitchHandler handler, CustomSwitch customSwitch)
        {
            if (handler.PlatformView != null)
            {
                handler.PlatformView.On = customSwitch.IsToggled;                
                handler.UpdateSwitchColors(handler.PlatformView, customSwitch);     // Update colors when switch is toggled/untoggled
            }
        }

        private void OnSwitchValueChanged(object sender, EventArgs e)
        {
            var uiSwitch = (UISwitch)sender;
            VirtualView.IsToggled = uiSwitch.On;

            UpdateSwitchColors(uiSwitch, VirtualView);
        }

        /// <summary>
        /// Update the OnTintColor and ThumbColor with the colors defined in the customSwitch
        /// </summary>
        private void UpdateSwitchColors(UISwitch uiSwitch, CustomSwitch customSwitch)
        {
            if (uiSwitch.On)
            {
                if (customSwitch.OnColor != default(Color))
                {
                    uiSwitch.OnTintColor = customSwitch.OnColor.ToPlatform();
                }
            }

            if (customSwitch.ThumbColor != default(Color))
            {
                uiSwitch.ThumbTintColor = customSwitch.ThumbColor.ToPlatform();
            }
        }
    }
}

#endif

MauiProgram.cs:

...
#if IOS
using OptimizerApp.Platforms.iOS.ControlHandlers;
#endif
...

var builder = MauiApp.CreateBuilder();
builder
    .UseMauiApp<App>()
    ...
    .ConfigureMauiHandlers(handlers =>
    {
        // The handler will only be called if the target platform is iOS
#if IOS
        handlers.AddHandler<CustomSwitch, CustomSwitchHandler>();
#endif
    });
...

CustomSwitchHandler



BUT: It even does not have to be this full blown implementation. As I found out, it was sufficient to just use this simple CustomSwitch above which simply derives from Switch and use this in XAML with corresponding Color settings. See before and after on iOS and Android :


Simple Switch iOS:

SwitchiOS


CustomSwitch iOS:

CustomSwitchiOS



Simple Switch Android:

SwitchAndroid


CustomSwitch Android:

CustomSwitchHandlerAndroid


MAUIoxo avatar Apr 13 '24 11:04 MAUIoxo

/azp run

jsuarezruiz avatar Jun 13 '24 07:06 jsuarezruiz

Azure Pipelines successfully started running 3 pipeline(s).

azure-pipelines[bot] avatar Jun 13 '24 07:06 azure-pipelines[bot]

For me the simplest solution was to go into Resources/Styles/Styles.xaml, search for "Switch" and set colors you need both for dark and white theme.

AncientLust avatar Jul 29 '24 16:07 AncientLust

I have tried this, but its not working. ThumbColor not getting changed even if set using DataTrigger.

https://github.com/user-attachments/assets/9ccf4ef2-7b12-4d30-9c9d-e3666b2d44e9

divyesh008 avatar Aug 14 '24 13:08 divyesh008