Skip to content

Commit

Permalink
Fixes #18204 border lagging behind content on iOS and improves initia…
Browse files Browse the repository at this point in the history
…l render performance
  • Loading branch information
albyrock87 committed Jun 21, 2024
1 parent 4fabb3e commit 8f3a94f
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 24 deletions.
17 changes: 17 additions & 0 deletions src/Controls/tests/TestCases/Issues/Issue18204.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?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.Issue18204"
Title="Issue18204">

<VerticalStackLayout Padding="24">
<Border
HorizontalOptions="Center"
x:Name="TheBorder"
BackgroundColor="LightBlue"
Shadow="{Shadow Brush=Black, Offset='0,2', Radius=2, Opacity=0.20}"
StrokeShape="{RoundRectangle CornerRadius=20}">
<Button x:Name="TheButton" Clicked="ButtonClicked" BackgroundColor="LightGreen" WidthRequest="200" HeightRequest="500" />
</Border>
</VerticalStackLayout>
</ContentPage>
25 changes: 25 additions & 0 deletions src/Controls/tests/TestCases/Issues/Issue18204.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using Microsoft.Maui;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Xaml;

namespace Maui.Controls.Sample.Issues;

[XamlCompilation(XamlCompilationOptions.Compile)]
[Issue(IssueTracker.Github, 18204, "[iOS] Drawing of Borders lags behind other elements creating bizarre overlaps and glitches", PlatformAffected.iOS)]

public partial class Issue18204 : ContentPage
{
public Issue18204()
{
InitializeComponent();
}

private void ButtonClicked(object sender, EventArgs e)
{
var button = (Button)sender;
button.CancelAnimations();
var targetHeight = button.HeightRequest == 200.0 ? 500.0 : 200.0;
button.Animate("Height", new Animation(v => button.HeightRequest = v, button.Height, targetHeight, Easing.Linear));
}
}
11 changes: 0 additions & 11 deletions src/Core/src/Handlers/Border/BorderHandler.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,5 @@ static partial void UpdateContent(IBorderHandler handler)
platformView.AddSubview(platformContent);
}
}

public override void PlatformArrange(Rect rect)
{
// Disable the animation during arrange for the Border; otherwise, all resizing actions
// will animate, and it makes the Border lag behind its content.

CATransaction.Begin();
CATransaction.AnimationDuration = 0;
base.PlatformArrange(rect);
CATransaction.Commit();
}
}
}
2 changes: 1 addition & 1 deletion src/Core/src/Platform/iOS/ContentView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public override void LayoutSubviews()
base.LayoutSubviews();

UpdateClip();
this.UpdateMauiCALayer();
this.UpdateMauiCALayerFrame();
}

internal IBorderStroke? Clip
Expand Down
55 changes: 44 additions & 11 deletions src/Core/src/Platform/iOS/StrokeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,15 @@ internal static void UpdateMauiCALayer(this UIView platformView, IBorderStroke?
{
CALayer? backgroundLayer = platformView.Layer as MauiCALayer;

var initialRender = false;
if (backgroundLayer == null)
{
backgroundLayer = platformView.Layer?.Sublayers?
.FirstOrDefault(x => x is MauiCALayer);

if (backgroundLayer == null)
{
initialRender = true;
backgroundLayer = new MauiCALayer
{
Name = ViewExtensions.BackgroundLayerName
Expand All @@ -122,6 +124,13 @@ internal static void UpdateMauiCALayer(this UIView platformView, IBorderStroke?
}
}

// While we're in the process of connecting the handler properties will not change
// So it's useless to update the layer many times with the same value
if (platformView is ContentView { View: null } && !initialRender)
{
return;
}

if (backgroundLayer is MauiCALayer mauiCALayer)
{
backgroundLayer.Frame = platformView.Bounds;
Expand Down Expand Up @@ -149,28 +158,52 @@ internal static void UpdateMauiCALayer(this UIView platformView, IBorderStroke?
contentView.Clip = border;
}

internal static void UpdateMauiCALayer(this UIView view)
internal static void UpdateMauiCALayerFrame(this UIView view)
{
if (view == null || view.Frame.IsEmpty)
if (view.Frame.IsEmpty)
{
return;
}

var layer = view.Layer;

UpdateBackgroundLayer(layer, view.Bounds);
if (layer.Sublayers is { Length: > 0 } sublayers)
{
UpdateBackgroundLayers(sublayers, view.Bounds);
}
}

static void UpdateBackgroundLayer(this CALayer layer, CGRect bounds)
static void UpdateBackgroundLayers(this CALayer[] layers, CGRect bounds, bool rootLayers = true)
{
var sublayers = layer?.Sublayers;
if (sublayers is not null)
var caTransactionBegun = false;
foreach (var layer in layers)
{
foreach (var sublayer in sublayers)
if (layer.Sublayers is { Length: > 0 } sublayers)
{
UpdateBackgroundLayers(sublayers, bounds);
}

if (layer.Name != ViewExtensions.BackgroundLayerName || layer.Frame == bounds)
{
UpdateBackgroundLayer(sublayer, bounds);
continue;
}

if (sublayer.Name == ViewExtensions.BackgroundLayerName && sublayer.Frame != bounds)
sublayer.Frame = bounds;
// iOS by default adds animations to certain actions such as layer resizing (setting the Frame property).
// This can result in the background layer not keeping up with animations controlled by MAUI.
// To prevent this undesired effect, native animations will be turned off for the duration of the operation.
if (rootLayers && !caTransactionBegun)
{
CATransaction.Begin();
CATransaction.AnimationDuration = 0;
caTransactionBegun = true;
}

layer.Frame = bounds;
}

if (caTransactionBegun)
{
CATransaction.Commit();
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Core/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -160,5 +160,5 @@ Microsoft.Maui.Platform.MauiView.IsMeasureValid(double widthConstraint, double h
*REMOVED*override Microsoft.Maui.Platform.ContentView.SetNeedsLayout() -> void
Microsoft.Maui.Platform.UIEdgeInsetsExtensions
static Microsoft.Maui.Platform.UIEdgeInsetsExtensions.ToThickness(this UIKit.UIEdgeInsets insets) -> Microsoft.Maui.Thickness
override Microsoft.Maui.Handlers.BorderHandler.PlatformArrange(Microsoft.Maui.Graphics.Rect rect) -> void
*REMOVED*override Microsoft.Maui.Handlers.BorderHandler.PlatformArrange(Microsoft.Maui.Graphics.Rect rect) -> void
*REMOVED*override Microsoft.Maui.Platform.MauiLabel.InvalidateIntrinsicContentSize() -> void

0 comments on commit 8f3a94f

Please sign in to comment.