-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[iOS] Improve background layer frame mapping performance (#24848)
* [iOS] Improve background layer frame mapping performance * Use a low-level observer object to avoid random crash * Add UITest * Add device test to bring proof of not leaking memory * IAutoSizableCALayer as internal interface * Add UI test for #26057 * Revert compatibility changes --------- Co-authored-by: Rui Marinho <[email protected]> Co-authored-by: Tamilarasan Paranthaman <[email protected]>
- Loading branch information
1 parent
ba3aec2
commit ffb0db2
Showing
25 changed files
with
385 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file added
BIN
+23.3 KB
.../TestCases.Android.Tests/snapshots/android/GradientLayerShouldApplyProperly.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
namespace Maui.Controls.Sample.Issues; | ||
|
||
[Issue(IssueTracker.Github, 24847, "[iOS] Background layer frame mapping has poor performance", PlatformAffected.iOS)] | ||
public class Issue24847 : ContentPage | ||
{ | ||
readonly Border _borderView; | ||
readonly Frame _frameView; | ||
|
||
public Issue24847() | ||
{ | ||
var linearGradient = new LinearGradientBrush( | ||
[ | ||
new GradientStop(Colors.Aqua, 0), | ||
new GradientStop(Colors.SlateBlue, 1) | ||
], | ||
new Point(0, 0), | ||
new Point(1, 1) | ||
); | ||
|
||
_borderView = new Border { Background = linearGradient }; | ||
_frameView = new Frame { Background = linearGradient }; | ||
|
||
var button = new Button | ||
{ | ||
Text = "Change size", | ||
AutomationId = "ChangeSizeBtn", | ||
}; | ||
button.Clicked += (s, e) => | ||
{ | ||
ToggleSize(); | ||
}; | ||
|
||
var vsl = new VerticalStackLayout | ||
{ | ||
Spacing = 16, | ||
Padding = 16 | ||
}; | ||
|
||
vsl.Add(_borderView); | ||
vsl.Add(_frameView); | ||
vsl.Add(button); | ||
|
||
ToggleSize(); | ||
|
||
Content = vsl; | ||
} | ||
|
||
void ToggleSize() | ||
{ | ||
var targetSize = _borderView.WidthRequest == 100 ? 200 : 100; | ||
|
||
_borderView.WidthRequest = targetSize; | ||
_borderView.HeightRequest = targetSize; | ||
_frameView.WidthRequest = targetSize; | ||
_frameView.HeightRequest = targetSize; | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
src/Controls/tests/TestCases.HostApp/Issues/Issue26057.xaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.Issue26057"> | ||
|
||
<StackLayout x:Name="stack"> | ||
<Button Text ="Remeasure the StackLayout" AutomationId="Button" Clicked="OnButtonClicked"/> | ||
<ContentView> | ||
<Button x:Name="button" | ||
WidthRequest="100" | ||
HeightRequest="100" | ||
Text="Hello"> | ||
<Button.Background> | ||
<LinearGradientBrush StartPoint="0,0" | ||
EndPoint="0,1"> | ||
<LinearGradientBrush.GradientStops> | ||
<GradientStop Color="white" | ||
Offset="0"/> | ||
<GradientStop Color="Red" | ||
Offset="1"/> | ||
</LinearGradientBrush.GradientStops> | ||
</LinearGradientBrush> | ||
</Button.Background> | ||
</Button> | ||
</ContentView> | ||
</StackLayout> | ||
</ContentPage> |
20 changes: 20 additions & 0 deletions
20
src/Controls/tests/TestCases.HostApp/Issues/Issue26057.xaml.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
using System; | ||
using Microsoft.Maui.Controls.Internals; | ||
|
||
namespace Maui.Controls.Sample.Issues | ||
{ | ||
[Issue(IssueTracker.Github, 26057, "[iOS & Mac] Gradient background size is incorrect when invalidating parent", PlatformAffected.iOS | PlatformAffected.macOS)] | ||
public partial class Issue26057: ContentPage | ||
{ | ||
public Issue26057() | ||
{ | ||
InitializeComponent(); | ||
} | ||
|
||
private void OnButtonClicked(object sender, EventArgs e) | ||
{ | ||
(stack as IView).InvalidateMeasure(); | ||
} | ||
|
||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue24847.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#if IOS | ||
|
||
using NUnit.Framework; | ||
using UITest.Appium; | ||
using UITest.Core; | ||
|
||
namespace Microsoft.Maui.TestCases.Tests.Issues | ||
{ | ||
public class Issue24847 : _IssuesUITest | ||
{ | ||
public Issue24847(TestDevice testDevice) : base(testDevice) | ||
{ | ||
} | ||
|
||
public override string Issue => "[iOS] Background layer frame mapping has poor performance"; | ||
|
||
[Test] | ||
[Category(UITestCategories.Visual)] | ||
public void BackgroundFrameResizesFastAndCorrectly() | ||
{ | ||
App.WaitForElement("ChangeSizeBtn"); | ||
VerifyScreenshot("BackgroundFrameResizesFastAndCorrectly"); | ||
|
||
App.Tap("ChangeSizeBtn"); | ||
VerifyScreenshot("BackgroundFrameResizesFastAndCorrectlySizeChanged"); | ||
} | ||
} | ||
} | ||
#endif |
22 changes: 22 additions & 0 deletions
22
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue26057.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
using NUnit.Framework; | ||
using UITest.Appium; | ||
using UITest.Core; | ||
|
||
namespace Microsoft.Maui.TestCases.Tests.Issues | ||
{ | ||
public class Issue26057 : _IssuesUITest | ||
{ | ||
public Issue26057(TestDevice device) : base(device) { } | ||
|
||
public override string Issue => "[iOS & Mac] Gradient background size is incorrect when invalidating parent"; | ||
|
||
[Test] | ||
[Category(UITestCategories.Button)] | ||
public void GradientLayerShouldApplyProperly() | ||
{ | ||
App.WaitForElement("Button"); | ||
App.Tap("Button"); | ||
VerifyScreenshot(); | ||
} | ||
} | ||
} |
Binary file added
BIN
+7.72 KB
...ts/TestCases.WinUI.Tests/snapshots/windows/GradientLayerShouldApplyProperly.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+131 KB
...ts/TestCases.iOS.Tests/snapshots/ios/BackgroundFrameResizesFastAndCorrectly.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+409 KB
...s.iOS.Tests/snapshots/ios/BackgroundFrameResizesFastAndCorrectlySizeChanged.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+67.5 KB
...ls/tests/TestCases.iOS.Tests/snapshots/ios/GradientLayerShouldApplyProperly.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
using System; | ||
using CoreAnimation; | ||
using CoreGraphics; | ||
using Foundation; | ||
|
||
namespace Microsoft.Maui.Platform; | ||
|
||
[Register("MauiCALayerAutosizeObserver")] | ||
class CALayerAutosizeObserver : NSObject | ||
{ | ||
static readonly NSString _boundsKey = new("bounds"); | ||
|
||
readonly WeakReference<CALayer> _layerReference; | ||
bool _disposed; | ||
|
||
public static CALayerAutosizeObserver Attach(CALayer layer) | ||
{ | ||
_ = layer ?? throw new ArgumentNullException(nameof(layer)); | ||
|
||
var superLayer = layer.SuperLayer ?? throw new InvalidOperationException("SuperLayer should be set before creating CALayerAutosizeObserver"); | ||
var observer = new CALayerAutosizeObserver(layer); | ||
superLayer.AddObserver(observer, _boundsKey, NSKeyValueObservingOptions.New, observer.Handle); | ||
layer.Frame = superLayer.Bounds; | ||
return observer; | ||
} | ||
|
||
private CALayerAutosizeObserver(CALayer layer) | ||
{ | ||
_layerReference = new WeakReference<CALayer>(layer); | ||
IsDirectBinding = false; | ||
} | ||
|
||
[Preserve (Conditional = true)] | ||
public override void ObserveValue (NSString keyPath, NSObject ofObject, NSDictionary change, IntPtr context) | ||
{ | ||
if (!_disposed && keyPath == _boundsKey && context == Handle && _layerReference.TryGetTarget(out var layer)) | ||
{ | ||
layer.Frame = layer.SuperLayer?.Bounds ?? CGRect.Empty; | ||
} | ||
} | ||
|
||
protected override void Dispose(bool disposing) | ||
{ | ||
if (!_disposed) | ||
{ | ||
_disposed = true; | ||
|
||
if (_layerReference.TryGetTarget(out var layer)) | ||
{ | ||
layer?.SuperLayer?.RemoveObserver(this, _boundsKey); | ||
} | ||
} | ||
|
||
base.Dispose(disposing); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
namespace Microsoft.Maui.Platform | ||
{ | ||
/// <summary> | ||
/// Provides a way to automatically size a CALayer to its super layer. | ||
/// </summary> | ||
// TODO: When we're good with this solution, we should make this public with NET10. | ||
// We may also evaluate to make CALayerAutosizeObserver public to offer a solution for third-party developers. | ||
internal interface IAutoSizableCALayer | ||
{ | ||
/// <summary> | ||
/// Automatically sizes the CALayer to its super layer. | ||
/// </summary> | ||
void AutoSizeToSuperLayer(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.