Skip to content
This repository was archived by the owner on May 1, 2024. It is now read-only.

Commit df202a3

Browse files
authored
Workaround for iOS material entry application freeze (#12316)
* Repro Issue 12246; UI test for 12246 * Subtly adjust entry colors on iOS 14 for material; fixes #12246 * Fix typo * Forgot to add automation ids fixes #12246
1 parent 712849b commit df202a3

File tree

3 files changed

+123
-4
lines changed

3 files changed

+123
-4
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
using System.Collections.ObjectModel;
2+
using System.Threading.Tasks;
3+
using Xamarin.Forms.CustomAttributes;
4+
using Xamarin.Forms.Internals;
5+
6+
#if UITEST
7+
using Xamarin.Forms.Core.UITests;
8+
using Xamarin.UITest;
9+
using NUnit.Framework;
10+
#endif
11+
12+
namespace Xamarin.Forms.Controls.Issues
13+
{
14+
#if UITEST
15+
[Category(UITestCategories.Entry)]
16+
[Category(UITestCategories.Visual)]
17+
#endif
18+
[Preserve(AllMembers = true)]
19+
[Issue(IssueTracker.Github, 12246, "[Bug] iOS 14 App freezes when password is entered after email", PlatformAffected.iOS)]
20+
public class Issue12246 : TestContentPage
21+
{
22+
const string Entry = "Entry";
23+
const string Password = "Password";
24+
const string Success = "Success";
25+
26+
protected override void Init()
27+
{
28+
var layout = new StackLayout() { Margin = 40, Spacing = 10, VerticalOptions = LayoutOptions.Center };
29+
30+
var instructions = new Label { Text = $"Focus the 'Email' Entry. Type in some text. Then focus the 'Password' Entry." +
31+
$" Type in some text. Now focus the 'Email' Entry again. The '{Success}' Label should appear. " +
32+
$"If the '{Success}' Label does not appear or the application hangs, this test has failed." };
33+
34+
var result = new Label { Text = Success, IsVisible = false };
35+
36+
var entry = new Entry() { Visual = VisualMarker.Material, Keyboard = Keyboard.Email, Placeholder = "Email",
37+
TextColor = Color.Purple, AutomationId = Entry };
38+
39+
var password = new Entry { Visual = VisualMarker.Material, IsPassword = true, Placeholder = "Password",
40+
TextColor = Color.Purple, AutomationId = Password };
41+
42+
var passwordConfirmation = new Entry
43+
{
44+
Visual = VisualMarker.Material,
45+
IsPassword = true,
46+
Placeholder = "Confirm Password",
47+
TextColor = Color.Purple
48+
};
49+
50+
password.Unfocused += (sender, args) => {
51+
result.IsVisible = true;
52+
};
53+
54+
layout.Children.Add(instructions);
55+
layout.Children.Add(entry);
56+
layout.Children.Add(password);
57+
layout.Children.Add(passwordConfirmation);
58+
layout.Children.Add(result);
59+
60+
Content = layout;
61+
}
62+
63+
#if UITEST
64+
[Test]
65+
public void UnfocusingPasswordDoesNotHang()
66+
{
67+
RunningApp.WaitForElement(Entry);
68+
RunningApp.Tap(Entry);
69+
RunningApp.EnterText("test");
70+
71+
RunningApp.WaitForElement(Password);
72+
RunningApp.Tap(Password);
73+
RunningApp.EnterText("test");
74+
75+
RunningApp.Tap(Entry);
76+
RunningApp.WaitForElement(Success);
77+
}
78+
#endif
79+
}
80+
}

Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
<Compile Include="$(MSBuildThisFileDirectory)Issue10744.cs" />
3030
<Compile Include="$(MSBuildThisFileDirectory)Issue10909.cs" />
3131
<Compile Include="$(MSBuildThisFileDirectory)Issue11769.cs" />
32+
<Compile Include="$(MSBuildThisFileDirectory)Issue12246.cs" />
3233
<Compile Include="$(MSBuildThisFileDirectory)Issue8613.cs" />
3334
<Compile Include="$(MSBuildThisFileDirectory)Issue9137.cs" />
3435
<Compile Include="$(MSBuildThisFileDirectory)Issue8691.cs" />

Xamarin.Forms.Material.iOS/MaterialTextManager.cs

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ namespace Xamarin.Forms.Material.iOS
1010
{
1111
internal static class MaterialTextManager
1212
{
13+
static double AlphaAdjustment = 0.0;
14+
1315
public static void Init(IMaterialEntryRenderer element, IMaterialTextField textField, IFontElement fontElement)
1416
{
1517
var containerScheme = textField.ContainerScheme;
@@ -49,8 +51,10 @@ public static void ApplyTheme(IMaterialTextField textField, IMaterialEntryRender
4951
textField.ContainerScheme.ColorScheme = (SemanticColorScheme)CreateColorScheme();
5052
ApplyContainerTheme(textField);
5153

52-
var textColor = MaterialColors.GetEntryTextColor(element.TextColor);
53-
var placeHolderColors = MaterialColors.GetPlaceHolderColor(element.PlaceholderColor, element.TextColor);
54+
var adjustedTextColor = AdjustTextColor(element);
55+
56+
var textColor = MaterialColors.GetEntryTextColor(adjustedTextColor);
57+
var placeHolderColors = MaterialColors.GetPlaceHolderColor(element.PlaceholderColor, adjustedTextColor);
5458
var underlineColors = MaterialColors.GetUnderlineColor(element.PlaceholderColor);
5559

5660
textField.TextInput.TextColor = textColor;
@@ -63,7 +67,7 @@ public static void ApplyTheme(IMaterialTextField textField, IMaterialEntryRender
6367
if (Brush.IsNullOrEmpty(brush))
6468
{
6569
// BackgroundColor
66-
textField.ActiveTextInputController.BorderFillColor = MaterialColors.CreateEntryFilledInputBackgroundColor(element.BackgroundColor, element.TextColor);
70+
textField.ActiveTextInputController.BorderFillColor = MaterialColors.CreateEntryFilledInputBackgroundColor(element.BackgroundColor, adjustedTextColor);
6771
}
6872
else
6973
{
@@ -114,11 +118,45 @@ public static void UpdatePlaceholder(IMaterialTextField textField, IMaterialEntr
114118

115119
public static void UpdateTextColor(IMaterialTextField textField, IMaterialEntryRenderer element)
116120
{
117-
var uIColor = MaterialColors.GetEntryTextColor(element.TextColor);
121+
var adjustedTextColor = AdjustTextColor(element);
122+
123+
var uIColor = MaterialColors.GetEntryTextColor(adjustedTextColor);
118124
textField.ContainerScheme.ColorScheme.OnSurfaceColor = uIColor;
119125
textField.ContainerScheme.ColorScheme.PrimaryColor = uIColor;
120126
}
121127

128+
static Color AdjustTextColor(IMaterialEntryRenderer element)
129+
{
130+
if (Forms.IsiOS14OrNewer)
131+
{
132+
// This is a workaround for an iOS/Material bug; https://github.com/xamarin/Xamarin.Forms/issues/12246
133+
// If we are on iOS 14, and we have multiple material text entry fields of the same color,
134+
// and any of them are password fields, setting them to the same TextColor value will cause the application
135+
// to hang when a password field loses focus.
136+
137+
// So to work around this, we make an imperceptible adjustment to the alpha value of the color each time
138+
// we set it; that way, none of the text entry fields have _exactly_ the same color and we avoid the bug
139+
140+
// Obviously this will start to become noticeable after the first 20 million or so text entry fields are displayed.
141+
// We apologize for the inconvenience.
142+
143+
var elementTextColor = element.TextColor;
144+
AlphaAdjustment += 0.0000001;
145+
146+
var adjustedAlpha = elementTextColor.A - AlphaAdjustment;
147+
if (adjustedAlpha < 0)
148+
{
149+
// Below an alpha of 0.01 stuff on iOS doesn't show up in hit tests anyway, so it seems unlikely
150+
// that the entry will get focus and cause the issue.
151+
adjustedAlpha = 0;
152+
}
153+
154+
return new Color(elementTextColor.R, elementTextColor.G, elementTextColor.B, adjustedAlpha);
155+
}
156+
157+
return element.TextColor;
158+
}
159+
122160
static IColorScheming CreateColorScheme()
123161
{
124162
var returnValue = MaterialColors.Light.CreateColorScheme();

0 commit comments

Comments
 (0)