Skip to content

Commit

Permalink
Fix Switch Off Track Color - Fixes #10099 (#10758)
Browse files Browse the repository at this point in the history
* Add a default color to the track in the off position

* remove unneeded using statement

* Use UIColor to handle dark mode

* remove noise

* Add tests and add null checks for getting subviews

* add new lines

* Move the tests into the iOS Test

* remove UIKit on the shared file

* - fix checkbox test

* fix style

* removing the few unneeded changes

* move the code from the mapper to the extensions

* move from extension to mapper and use FirstOrDefault

* Auto-format source code

* change test name and make color a static var

* use Manuels NoLink alternative

* move to arrayExtensions and use different implementation

* allow param to be null

---------

Co-authored-by: TJ Lambert <[email protected]>
Co-authored-by: Shane Neuville <[email protected]>
Co-authored-by: GitHub Actions Autoformatter <[email protected]>
  • Loading branch information
4 people authored Apr 3, 2023
1 parent e2448f1 commit 9e72fbc
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 19 deletions.
2 changes: 2 additions & 0 deletions src/Core/src/Extensions/ArrayExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,7 @@ public static T[] RemoveAt<T>(this T[] self, int index)
/// <param name="self">The array to retrieve the last item from.</param>
/// <returns>An object of type <typeparamref name="T"/> that is the last item in the collection.</returns>
public static T Last<T>(this T[] self) => self[self.Length - 1];

internal static T? FirstOrDefaultNoLinq<T>(this T[]? items) => items is null || items.Length == 0 ? default : items[0];
}
}
6 changes: 6 additions & 0 deletions src/Core/src/Handlers/Switch/SwitchHandler.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ protected override void DisconnectHandler(UISwitch platformView)

public static void MapIsOn(ISwitchHandler handler, ISwitch view)
{
UpdateIsOn(handler);
handler.PlatformView?.UpdateIsOn(view);
}

Expand All @@ -55,5 +56,10 @@ void OnControlValueChanged(object? sender, EventArgs e)

VirtualView.IsOn = PlatformView.On;
}

static void UpdateIsOn(ISwitchHandler handler)
{
handler.UpdateValue(nameof(ISwitch.TrackColor));
}
}
}
39 changes: 21 additions & 18 deletions src/Core/src/Platform/iOS/SwitchExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
using System;
using System.Linq;
using ObjCRuntime;
using UIKit;

namespace Microsoft.Maui.Platform
{
public static class SwitchExtensions
{
static UIColor DefaultBackgroundColor = UIColor.FromRGBA(120, 120, 128, 40);

public static void UpdateIsOn(this UISwitch uiSwitch, ISwitch view)
{
uiSwitch.SetState(view.IsOn, true);
Expand All @@ -17,17 +17,23 @@ public static void UpdateTrackColor(this UISwitch uiSwitch, ISwitch view)
if (view == null)
return;

if (view.TrackColor != null)
uiSwitch.OnTintColor = view.TrackColor.ToPlatform();
var uIView = GetTrackSubview(uiSwitch);

UIView uIView;
if (OperatingSystem.IsIOSVersionAtLeast(13) || OperatingSystem.IsTvOSVersionAtLeast(13))
uIView = uiSwitch.Subviews[0].Subviews[0];
else
uIView = uiSwitch.Subviews[0].Subviews[0].Subviews[0];
if (uIView is null)
return;

if (!view.IsOn)
{
// iOS 13+ uses the UIColor.SecondarySystemFill to support Light and Dark mode
// else, use the RGBA equivalent of UIColor.SecondarySystemFill in Light mode
uIView.BackgroundColor = OperatingSystem.IsIOSVersionAtLeast(13) ? UIColor.SecondarySystemFill : DefaultBackgroundColor;
}

if (view.TrackColor != null)
else if (view.TrackColor is not null)
{
uiSwitch.OnTintColor = view.TrackColor.ToPlatform();
uIView.BackgroundColor = uiSwitch.OnTintColor;
}
}

public static void UpdateThumbColor(this UISwitch uiSwitch, ISwitch view)
Expand All @@ -40,20 +46,17 @@ public static void UpdateThumbColor(this UISwitch uiSwitch, ISwitch view)
uiSwitch.ThumbTintColor = thumbColor?.ToPlatform();
}

internal static UIView GetTrackSubview(this UISwitch uISwitch)
internal static UIView? GetTrackSubview(this UISwitch uISwitch)
{
UIView uIView;
if (OperatingSystem.IsIOSVersionAtLeast(13) || OperatingSystem.IsTvOSVersionAtLeast(13))
uIView = uISwitch.Subviews[0].Subviews[0];
return uISwitch.Subviews?.FirstOrDefaultNoLinq()?.Subviews?.FirstOrDefaultNoLinq();
else
uIView = uISwitch.Subviews[0].Subviews[0].Subviews[0];

return uIView;
return uISwitch.Subviews?.FirstOrDefaultNoLinq()?.Subviews?.FirstOrDefaultNoLinq()?.Subviews?.FirstOrDefaultNoLinq();
}

internal static UIColor? GetOffTrackColor(this UISwitch uISwitch)
internal static UIColor? GetTrackColor(this UISwitch uISwitch)
{
return uISwitch.GetTrackSubview().BackgroundColor;
return uISwitch.GetTrackSubview()?.BackgroundColor;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,38 @@ async Task ValidateThumbColor(ISwitch switchStub, Color color, Action action = n
Assert.Equal(expected, color);
}

async Task ValidateVisualTrackColor(ISwitch switchStub, UIColor color)
{
var actualBackgroundColor = await GetValueAsync(switchStub, handler =>
{
var uISwitch = GetNativeSwitch(handler);
var uIView = uISwitch.GetTrackSubview();
return uIView?.BackgroundColor;
});

Assert.NotNull(actualBackgroundColor);

// Compare the actual RGBA values since UIColor can be picky
actualBackgroundColor.GetRGBA(out var actualRed, out var actualGreen, out var actualBlue, out var actualAlpha);
color.GetRGBA(out var colorRed, out var colorGreen, out var colorBlue, out var colorAlpha);

Assert.True(actualRed == colorRed);
Assert.True(actualGreen == colorGreen);
Assert.True(actualBlue == colorBlue);
Assert.True(actualAlpha == colorAlpha);
}

async Task ValidateTrackSubViewExists(ISwitch switchStub)
{
var uIView = await GetValueAsync(switchStub, handler =>
{
var uISwitch = GetNativeSwitch(handler);
return uISwitch.GetTrackSubview();
});

Assert.NotNull(uIView);
}

/// <summary>
/// If a UISwitch grows beyond 101 pixels it's no longer
/// clickable via Voice Over
Expand All @@ -72,5 +104,61 @@ public async Task EnsureUISwitchStaysBelow101Width()

Assert.True(width < 100, $"UISwitch width is too much {width}");
}

[Fact(DisplayName = "Track Color's view is set when toggled on")]
public async Task OnTrackColorSetVisually()
{
var switchStub = new SwitchStub()
{
IsOn = false,
TrackColor = Colors.Red,
};

await InvokeOnMainThreadAsync(async () =>
{
await ValidatePropertyUpdatesValue(
view: switchStub,
property: nameof(ISwitch.IsOn),
GetPlatformValue: (handler) => handler.PlatformView.On,
expectedSetValue: true,
expectedUnsetValue: false);

await ValidateVisualTrackColor(switchStub, UIColor.Red);
});
}

[Fact(DisplayName = "Track Color's view is default color when toggled off")]
public async Task OffTrackColorSetToDefaultColor()
{
var switchStub = new SwitchStub()
{
IsOn = true,
};

await InvokeOnMainThreadAsync(async () =>
{
await ValidatePropertyUpdatesValue(
view: switchStub,
property: nameof(ISwitch.IsOn),
GetPlatformValue: (handler) => handler.PlatformView.On,
expectedSetValue: false,
expectedUnsetValue: true);

var color = OperatingSystem.IsIOSVersionAtLeast(13) ? UIColor.SecondarySystemFill : UIColor.FromRGBA(120, 120, 128, 40);

await ValidateVisualTrackColor(switchStub, color);
});
}

[Fact(DisplayName = "Apple has not changed UITrack Subviews")]
public async Task UIViewSubviewExists()
{
var switchStub = new SwitchStub();

await InvokeOnMainThreadAsync(async () =>
{
await ValidateTrackSubViewExists(switchStub);
});
}
}
}
}

0 comments on commit 9e72fbc

Please sign in to comment.