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

Ordering children while adding #8231

Merged
merged 7 commits into from
Sep 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;
using System;

#if UITEST
using Xamarin.Forms.Core.UITests;
using Xamarin.UITest;
using NUnit.Framework;
#endif

namespace Xamarin.Forms.Controls.Issues
{
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 8129, "[Bug] Adding children to iOS VisualElementPackager has O(N^2) performance and thrashes the native layer", PlatformAffected.iOS)]
public class Issue8129 : TestContentPage
{
protected override void Init()
{
Title = "Page with too many elements (2000) Tests";

var grid = new Grid();
var stackLayout = new StackLayout();

var addChildrenCommand = new Command((parameter) =>
{
Enumerable
.Range(0, 2000)
.Select(_ => new Label() { HeightRequest = 200, Text = $"I am Label #{_}" })
.ForEach(x => stackLayout.Children.Add(x));
});

var addChildrenButton = new Button
{
Text = "Add 2000 Labels",
Command = addChildrenCommand
};

var addZChildrenButton = new Button
{
Text = "Add BoxView on Top",
Command = new Command((parameter) =>
{
grid.AddChild(new BoxView
{
HeightRequest = 300,
WidthRequest = 300,
BackgroundColor = Color.Green
}, 0, 0, 1, 3);
})
};
grid.AddChild(addChildrenButton, 0, 0);
grid.AddChild(addZChildrenButton, 0, 1);
grid.AddChild(stackLayout, 0, 2);

Content = grid;
}
#if UITEST
[Test]
public void AddTooManyContentsTest()
{
RunningApp.WaitForElement(q => q.Button("Add 2000 Labels"));
RunningApp.Screenshot("Before adding 2000 Labels");
RunningApp.Tap(q => q.Button("Add 2000 Labels"));
RunningApp.WaitForElement(q => q.Marked("I am Label #1"));
RunningApp.Screenshot("After adding 2000 Labels");
}

[Test]
public void ZIndexAfterAddingContentsTest()
{
RunningApp.WaitForElement(q => q.Button("Add BoxView on Top"));
RunningApp.Screenshot("Before adding BoxView on Top");
RunningApp.Tap(q => q.Button("Add BoxView on Top"));
RunningApp.WaitForNoElement(q => q.Button("Add BoxView on Top"));
RunningApp.Screenshot("After adding BoxView on Top");
}
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1761,6 +1761,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Issue13616.xaml.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue13670.xaml.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue13684.xaml.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue8129.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue12300.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue12150.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue12590.xaml.cs" />
Expand Down
49 changes: 38 additions & 11 deletions Xamarin.Forms.Platform.iOS/VisualElementPackager.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Xamarin.Forms.Internals;

#if __MOBILE__
Expand Down Expand Up @@ -46,7 +47,10 @@ public void Load()
{
var child = ElementController.LogicalChildren[i] as VisualElement;
if (child != null)
{
OnChildAdded(child);
OrderElement(child, i);
}
}
}

Expand Down Expand Up @@ -120,8 +124,6 @@ protected virtual void OnChildAdded(VisualElement view)

if (Renderer.ViewController != null && viewRenderer.ViewController != null)
Renderer.ViewController.AddChildViewController(viewRenderer.ViewController);

EnsureChildrenOrder();
}

Performance.Stop(reference);
Expand All @@ -141,34 +143,59 @@ protected virtual void OnChildRemoved(VisualElement view)
viewRenderer.Dispose();
}

void EnsureChildrenOrder()
void EnsureChildrenOrder(VisualElement element = null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add some tests validating the logic to order and ensure the order of the elements?

{
if (ElementController.LogicalChildren.Count == 0)
return;

for (var z = 0; z < ElementController.LogicalChildren.Count; z++)
var effectedChildIndex = 0;
if (element != null)
effectedChildIndex = ElementController.LogicalChildren.IndexOf(element);

for (var z = effectedChildIndex; z < ElementController.LogicalChildren.Count; z++)
{
var child = ElementController.LogicalChildren[z] as VisualElement;
if (child == null)
continue;
var childRenderer = Platform.GetRenderer(child);
OrderElement(child, z);
}
}

if (childRenderer == null)
continue;
void OrderElement(VisualElement element, int order)
{
Performance.Start(out string reference);
if (CompressedLayout.GetIsHeadless(element))
{
Performance.Stop(reference);

var nativeControl = childRenderer.NativeView;
return;
}

var childRenderer = Platform.GetRenderer(element);

if (childRenderer == null)
{
Performance.Stop(reference);

return;
}

var nativeControl = childRenderer.NativeView;
#if __MOBILE__
Renderer.NativeView.BringSubviewToFront(nativeControl);
Renderer.NativeView.BringSubviewToFront(nativeControl);
#endif
nativeControl.Layer.ZPosition = z * 1000;
}
nativeControl.Layer.ZPosition = order * 1000;
Performance.Stop(reference);
jfversluis marked this conversation as resolved.
Show resolved Hide resolved
}

void OnChildAdded(object sender, ElementEventArgs e)
{
var view = e.Element as VisualElement;
if (view != null)
{
OnChildAdded(view);
EnsureChildrenOrder(view);
}
}

void OnChildRemoved(object sender, ElementEventArgs e)
Expand Down