Skip to content

Commit

Permalink
[iOS] Fix wrong gray color using transparent in iOS gradients (#17696)
Browse files Browse the repository at this point in the history
* Fix wrong gray color using transparent in iOS gradients

* Added UI Test

* Updated test screenshot

* Added a new sample and test

* Updated test

* Fix build errors
  • Loading branch information
jsuarezruiz committed Dec 20, 2023
1 parent e737057 commit ba78d8a
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Issues.Issue17366">
<StackLayout
Padding="12">
<Label
AutomationId="WaitForStubControl"
Text="Background (Gradient using a transparent color)"/>
<Frame
HeightRequest="200">
<Frame.Background>
<LinearGradientBrush
StartPoint="0,0"
EndPoint="0,1">
<GradientStop
Color="Transparent"
Offset="0.0"/>
<GradientStop
Color="Red"
Offset="1.00"/>
</LinearGradientBrush>
</Frame.Background>
<Label
Text="Issue 17366" />
</Frame>
</StackLayout>
</ContentPage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Xaml;
using Microsoft.Maui.Platform;

namespace Maui.Controls.Sample.Issues
{
[XamlCompilation(XamlCompilationOptions.Compile)]
[Issue(IssueTracker.Github, 17366, "Wrong gray color using transparent in iOS gradients", PlatformAffected.iOS)]
public partial class Issue17366 : ContentPage
{
public Issue17366()
{
InitializeComponent();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,50 @@
<Label
Text="Background" />
</Frame>
<Label
Text="Background (Gradient using a transparent color)"
Style="{StaticResource Headline}"/>
<Frame>
<Frame.Background>
<LinearGradientBrush
StartPoint="0,0"
EndPoint="0,1">
<GradientStop
Color="Transparent"
Offset="0.0"/>
<GradientStop
Color="Red"
Offset="1.00"/>
</LinearGradientBrush>
</Frame.Background>
<Label
Text="Background" />
</Frame>
<Label
Text="Background (Gradient using multiple transparent colors)"
Style="{StaticResource Headline}"/>
<Frame>
<Frame.Background>
<LinearGradientBrush
StartPoint="0,0"
EndPoint="0,1">
<GradientStop
Color="Transparent"
Offset="0.0"/>
<GradientStop
Color="Red"
Offset="0.3"/>
<GradientStop
Color="Transparent"
Offset="0.6"/>
<GradientStop
Color="Red"
Offset="1.00"/>
</LinearGradientBrush>
</Frame.Background>
<Label
Text="Background" />
</Frame>
<Label
Text="BorderColor"
Style="{StaticResource Headline}"/>
Expand Down Expand Up @@ -134,8 +178,7 @@
Margin="10">
<!-- Empty on purpose -->
</Frame>
</Grid>

</Grid>
</VerticalStackLayout>
</ScrollView>
</views:BasePage.Content>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,11 @@ public virtual void SetupLayer()
if (backgroundLayer != null)
{
_actualView.Layer.BackgroundColor = UIColor.Clear.CGColor;
Layer.InsertBackgroundLayer(backgroundLayer, 0);

backgroundLayer.BackgroundColor = ColorExtensions.BackgroundColor.CGColor;
backgroundLayer.CornerRadius = cornerRadius;

Layer.InsertBackgroundLayer(backgroundLayer, 0);
}
}

Expand Down
29 changes: 27 additions & 2 deletions src/Controls/src/Core/Platform/iOS/Extensions/BrushExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public static CALayer GetBackgroundLayer(this UIView control, Brush brush)
if (linearGradientBrush.GradientStops != null && linearGradientBrush.GradientStops.Count > 0)
{
var orderedStops = linearGradientBrush.GradientStops.OrderBy(x => x.Offset).ToList();
linearGradientLayer.Colors = orderedStops.Select(x => x.Color.ToCGColor()).ToArray();
linearGradientLayer.Colors = GetCAGradientLayerColors(orderedStops);
linearGradientLayer.Locations = GetCAGradientLayerLocations(orderedStops);
}

Expand Down Expand Up @@ -100,7 +100,7 @@ public static CALayer GetBackgroundLayer(this UIView control, Brush brush)
if (radialGradientBrush.GradientStops != null && radialGradientBrush.GradientStops.Count > 0)
{
var orderedStops = radialGradientBrush.GradientStops.OrderBy(x => x.Offset).ToList();
radialGradientLayer.Colors = orderedStops.Select(x => x.Color.ToCGColor()).ToArray();
radialGradientLayer.Colors = GetCAGradientLayerColors(orderedStops);
radialGradientLayer.Locations = GetCAGradientLayerLocations(orderedStops);
}

Expand Down Expand Up @@ -251,6 +251,31 @@ static NSNumber[] GetCAGradientLayerLocations(List<GradientStop> gradientStops)
}
}

static CGColor[] GetCAGradientLayerColors(List<GradientStop> gradientStops)
{
if (gradientStops == null || gradientStops.Count == 0)
return new CGColor[0];

CGColor[] colors = new CGColor[gradientStops.Count];

int index = 0;
foreach (var gradientStop in gradientStops)
{
if (gradientStop.Color == Colors.Transparent)
{
var color = gradientStops[index == 0 ? index + 1 : index - 1].Color;
CGColor nativeColor = color.ToPlatform().ColorWithAlpha(0.0f).CGColor;
colors[index] = nativeColor;
}
else
colors[index] = gradientStop.Color.ToCGColor();

index++;
}

return colors;
}

static bool ShouldUseParentView(UIView view)
{
if (view is UILabel)
Expand Down
73 changes: 73 additions & 0 deletions src/Controls/tests/DeviceTests/Elements/Frame/FrameTests.iOS.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,62 @@
using System.Threading.Tasks;
using CoreAnimation;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform;
using UIKit;
using Xunit;

namespace Microsoft.Maui.DeviceTests
{
public partial class FrameTests
{
[Fact(DisplayName = "Frame with gradient Background Test")]
public async Task FrameWithGradientBackgroundTest()
{
SetupBuilder();

var frame = new Frame()
{
HasShadow = false,
HeightRequest = 200,
WidthRequest = 200,
Background = new LinearGradientBrush
{
StartPoint = new Point(0, 0),
EndPoint = new Point(0, 1),
GradientStops = new GradientStopCollection
{
new GradientStop { Color = Colors.Transparent, Offset = 0 },
new GradientStop { Color = Colors.Red, Offset = 0.3f },
new GradientStop { Color = Colors.Transparent, Offset = 0.6f },
new GradientStop { Color = Colors.Red, Offset = 1 },
}
},
Content = new Label()
{
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.Center,
Text = "Gradient Background"
}
};

await InvokeOnMainThreadAsync(() =>
frame.ToPlatform(MauiContext).AttachAndRun(() =>
{
var platformView = (Controls.Handlers.Compatibility.FrameRenderer)frame.ToPlatform(MauiContext);
Assert.NotNull(platformView);
var backgroundLayer = GetBackgroundLayer(platformView) as CAGradientLayer;
Assert.NotNull(backgroundLayer);
var backgroundLayerColors = backgroundLayer.Colors;
Assert.Equal(4, backgroundLayerColors.Length);
string transparentColor = "transparent";
Assert.Equal(transparentColor, backgroundLayerColors[0].AXName);
Assert.Equal(transparentColor, backgroundLayerColors[2].AXName);
}));
}

[Fact(DisplayName = "Frame HasShadow Test")]
public async Task FrameHasShadowTest()
{
Expand Down Expand Up @@ -82,5 +131,29 @@ public async Task FrameClipsCorrectly(bool? isClipped)
Assert.True(handler.PlatformView.ClipsToBounds);
}));
}

CALayer GetBackgroundLayer(UIView platformView)
{
string BackgroundLayer = "BackgroundLayer";

var layer = platformView.Layer;

if (layer is not null)
{
if (layer.Name == BackgroundLayer)
return layer;

if (layer.Sublayers == null || layer.Sublayers.Length == 0)
return null;

foreach (var subLayer in layer.Sublayers)
{
if (subLayer.Name == BackgroundLayer)
return subLayer;
}
}

return null;
}
}
}
25 changes: 25 additions & 0 deletions src/Controls/tests/UITests/Tests/Issues/Issue17366.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.AppiumTests.Issues
{
public class Issue17366 : _IssuesUITest
{
public Issue17366(TestDevice device) : base(device)
{
}

public override string Issue => "Wrong gray color using transparent in iOS gradients";

[Test]
public void Issue17366Test()
{
this.IgnoreIfPlatforms(new TestDevice[] { TestDevice.Android, TestDevice.Mac, TestDevice.Windows },
"The bug only happens on iOS; see https://github.com/dotnet/maui/pull/17789");

App.WaitForElement("WaitForStubControl");
VerifyScreenshot();
}
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 27 additions & 2 deletions src/Core/src/Graphics/PaintExtensions.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public static partial class PaintExtensions
if (linearGradientPaint.GradientStops != null && linearGradientPaint.GradientStops.Length > 0)
{
var orderedStops = linearGradientPaint.GradientStops.OrderBy(x => x.Offset).ToList();
linearGradientLayer.Colors = orderedStops.Select(x => x.Color.ToCGColor()).ToArray();
linearGradientLayer.Colors = GetCAGradientLayerColors(orderedStops);
linearGradientLayer.Locations = GetCAGradientLayerLocations(orderedStops);
}

Expand All @@ -96,7 +96,7 @@ public static partial class PaintExtensions
if (radialGradientPaint.GradientStops != null && radialGradientPaint.GradientStops.Length > 0)
{
var orderedStops = radialGradientPaint.GradientStops.OrderBy(x => x.Offset).ToList();
radialGradientLayer.Colors = orderedStops.Select(x => x.Color.ToCGColor()).ToArray();
radialGradientLayer.Colors = GetCAGradientLayerColors(orderedStops);
radialGradientLayer.Locations = GetCAGradientLayerLocations(orderedStops);
}

Expand Down Expand Up @@ -165,5 +165,30 @@ static NSNumber[] GetCAGradientLayerLocations(List<PaintGradientStop> gradientSt
return locations;
}
}

static CGColor[] GetCAGradientLayerColors(List<PaintGradientStop> gradientStops)
{
if (gradientStops == null || gradientStops.Count == 0)
return Array.Empty<CGColor>();

CGColor[] colors = new CGColor[gradientStops.Count];

int index = 0;
foreach (var gradientStop in gradientStops)
{
if (gradientStop.Color == Colors.Transparent)
{
var color = gradientStops[index == 0 ? index + 1 : index - 1].Color;
CGColor nativeColor = color.ToPlatform().ColorWithAlpha(0.0f).CGColor;
colors[index] = nativeColor;
}
else
colors[index] = gradientStop.Color.ToCGColor();

index++;
}

return colors;
}
}
}

0 comments on commit ba78d8a

Please sign in to comment.