dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22.06k stars 1.73k forks source link

[Android] CollectionViewwith multiple bindable layouts (CardWithComplexContent) performance issues #21580

Open eth-ellis opened 6 months ago

eth-ellis commented 6 months ago

Description

When migrating our app from Xamarin.Forms to .NET MAUI we noticed that for most of our CollectionViews the performance when scrolling was worse.

When new views appeared the CollectionView would stutter/jitter resulting in an unpleasant user experience.

Our item templates are somewhat complex but still worked great in Xamarin.Forms.

In the linked repro apps, we have a template called CardWithComplexContent which is somewhat similar to the template in our app.

Note

Android (Samsung A73) - Xamarin.Forms

https://github.com/dotnet/maui/assets/13865151/25eac924-a499-4908-afd9-ab7108d71437

Android (Samsung A73) - MAUI

https://github.com/dotnet/maui/assets/13865151/cd402208-674f-4cd4-8a67-b3038e1e8f88

iOS (iPhone 6s) - Xamarin.Forms

https://github.com/dotnet/maui/assets/13865151/13a64912-3daf-4325-9d0e-4c15d4cee2eb

iOS (iPhone 6s) - MAUI

https://github.com/dotnet/maui/assets/13865151/d46972ec-5e51-479d-aa53-81351874db9e

Steps to Reproduce

  1. Checkout .NET MAUI and Xamarin.Forms repro projects.
  2. Build and deploy the apps to your devices in Release configuration.
  3. Run both apps and select either the CardWithTheLot or CardWithComplexContent template.
  4. Compare scrolling performance on both apps (fast scrolling and slow scrolling).

The apps contain other templates for attempting to isolate elements which impact performance the most.

Accepting PRs or suggestions on the repro app repos for templates to add for comparison.

Acceptance Criteria

The scrolling experience on .NET MAUI performs as well as or better than Xamarin.Forms for all affected platforms.

Link to public reproduction project repository

https://github.com/eth-ellis/Issue-Repro/tree/main/CollectionViewPerformanceXamarin and https://github.com/eth-ellis/Issue-Repro/tree/main/CollectionViewPerformanceMaui

Version with bug

Nightly / CI build (Please specify exact version)

Is this a regression from previous behavior?

Yes, this used to work in Xamarin.Forms

Last version that worked well

Unknown/Other

Affected platforms

iOS, Android

Affected platform versions

No response

Did you find any workaround?

No response

Relevant log output

No response

jonmdev commented 2 months ago

I updated the projects to add similar iOS label update debugging. Maui in iOS does not go into the same circular update loop on start up like Android. However, it demonstrates another major problem.

I suspect we would see the same in Android if it were equally testable (ie. if the feedback loop was removed from load first so we can see better).

Problem 2 (iOS): Maui draws Labels 42% more times than Xamarin for the same workload

Again, this goes back to what has seemed blatantly obvious - Maui is overdoing simple work due to the layout system continuously spamming unnecessary updates.

Here we can objectively and reproduceably measure that Maui draws labels in iOS 42% more than Xamarin for the same workload.

Test Method

Run the project in Maui/Xamarin iOS (iPhone) Debug mode. Let it load. Then click to select in the menu "CardWithComplexContent". Start scrolling down the screen at your own pace until you hit the bottom.

Look at the count of label draws incremented in the Debug output. I did this twice in Xamarin and twice in Maui and got identical results both times on my test device.

Here we are monitoring with:

 public override void Draw(CGRect rect) {
     base.Draw(rect);
     UpdateCounter.addDrawUpdate();
 }

Maui Initial Load:

[0:] DRAW UPDATE 1
[0:] DRAW UPDATE 2
[0:] DRAW UPDATE 3
[0:] DRAW UPDATE 4
[0:] DRAW UPDATE 5
[0:] DRAW UPDATE 6
[0:] DRAW UPDATE 7
[0:] DRAW UPDATE 8
[0:] DRAW UPDATE 9
[0:] DRAW UPDATE 10
[0:] DRAW UPDATE 11
[0:] DRAW UPDATE 12
[0:] DRAW UPDATE 13
[0:] DRAW UPDATE 14
[0:] DRAW UPDATE 15
[0:] DRAW UPDATE 16
[0:] DRAW UPDATE 17

Maui Final Label Draw Count:

TRIAL 1:

[0:] DRAW UPDATE 1324 //final update

TRIAL 2:

[0:] DRAW UPDATE 1324 //final update

Xamarin Initial Load:

[0:] DRAW UPDATE 1
[0:] DRAW UPDATE 2
[0:] DRAW UPDATE 3
[0:] DRAW UPDATE 4
[0:] DRAW UPDATE 5
[0:] DRAW UPDATE 6
[0:] DRAW UPDATE 7
[0:] DRAW UPDATE 8
[0:] DRAW UPDATE 9
[0:] DRAW UPDATE 10
[0:] DRAW UPDATE 11
[0:] DRAW UPDATE 12
[0:] DRAW UPDATE 13
[0:] DRAW UPDATE 14
[0:] DRAW UPDATE 15

Xamarin Final Label Draw Count:

TRIAL 1:

[0:] DRAW UPDATE 933 //final update

TRIAL 2:

[0:] DRAW UPDATE 933 //final update

Conclusion

Thus we see consistently in both trials Xamarin needed precisely 933 label draws to initially load, switch page, and then scroll to the bottom, while in both trials Maui needed 1324 label draws to do the exact same thing.

That is 42% more label draws to accomplish the same thing on the same device. A massive deterioration.

jonmdev commented 2 months ago

I updated the test projects once more to add a Reset Counters button so I could test Android's performance. The outcome was even worse than iOS in the parameter that stood out.

Problem 3 (Android): Maui performs 62% more Label measurements than Xamarin for the same workload

Test Method

Run the project in Android debug to device. Then click Change Template > CardWIthComplexContent.

Scroll down until the feedback loop stops (it stops spamming feedback loop updates and goes quiet), then scroll back to the top. Click Reset Counters to go back to zero on the update counters. Clear your debug output also perhaps. Then scroll down through the full list with this as your "starting point".

This will not be quite as perfectly consistent as we are not starting from identical conditions each time given the need to escape the initial feedback loop. However, the data is still just as stark as you will see.

Here we are testing with:

public CustomAndroidLabelRenderer() {
    this.AddOnLayoutChangeListener(new LayoutListener());

}
class LayoutListener : Java.Lang.Object, IOnLayoutChangeListener {
    public void OnLayoutChange(Android.Views.View? v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
        UpdateCounter.addLayoutUpdate();
    }
};

protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    UpdateCounter.addMeasureUpdate(this.GetHashCode().ToString());
    base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
}

Results

Maui:

TRIAL 1:

[0:] MEASURE UPDATE 3006 
[0:] LAYOUT UPDATE 1224

TRIAL 2:

[0:] MEASURE UPDATE 2994 
[0:] LAYOUT UPDATE 1222

Xamarin:

TRIAL 1:

[0:] MEASURE UPDATE 1882
[0:] LAYOUT UPDATE 1264

TRIAL 2:

[0:] MEASURE UPDATE 1824 
[0:] LAYOUT UPDATE 1248

Conclusion

Thus we see an average of 3000 Label Measure commands in Maui vs. just 1853 in Xamarin.

This represents 62% more Label measurements performed in Maui vs. Xamarin to accomplish the same thing.

Is this stark enough? Is it still any mystery why we are suffering for performance, compared to Xamarin or other systems, both in Android and iOS?

Any thoughts?

jonmdev commented 2 months ago

Evaluating Maui vs. Xamarin Photo Performance

I have also always felt like the Image performance in Maui has also been quite poor. Loading simple images in a "CollectionView" type situation (ie. while scrolling) always leads to stutters and seems far too arduous no matter how much you cache or create platform Bitmaps first.

So I extended the project to add an Image View. I just copied and pasted the "ComplexCard" of the project into "CardWithPhoto" and threw some random cat photos in there.

The outcomes are absolutely disastrous and shocking. I don't usually use XAML (I code in C#) so unless there is something I missed here, while Xamarin handled the change fine for both Android and iOS, it's an absolute wreck in Maui across the board.

Test Method

I added a random photo into the XAML like so with some code elsewhere to select and add one of 14 cat photos at that spot:

            <StackLayout
                Margin="10"
                Spacing="10">

                <Label
                    Text="{Binding Source.RestaurantName, Source={x:Reference this}}"
                    FontSize="Title" />

                <Label
                    Text="{Binding Source.RestaurantDescription, Source={x:Reference this}}"
                    FontSize="Body" />

                <Image 
                    Source="{Binding Source.PhotoImageSource, Source= {x:Reference this}}" 
                    />

                <Label
                    Text="{Binding Source.RestaurantAddress, Source={x:Reference this}}"
                    TextColor="DodgerBlue"
                    FontAttributes="Bold"
                    FontSize="Body" />

Problem 4 (Android & iOS): Neither in Maui can accurately lay out the same XAML templates as Xamarin when Images are involved

It looked quite nice and laid out just right in Xamarin iOS and Android:

Xamarin iOS & Android: Lays out perfectly

However we get very different results on Maui iOS and Android. Android was an abomination where there was endless space above and below the images. iOS just truncated the bottom of the views. Both were grotesque by comparison to Xamarin.

Android Maui: Empty space all over

iOS Maui: Truncates everything

I am not sure again on XAML and how much might have changed to Maui, but to me there is no reason this should fail so differently on iOS and Android. Clearly something is broken.

Though this is cosmetic. The abomination goes deeper and it gets weirder.

jonmdev commented 2 months ago

Problem 5 (Android): Android in Maui calls OnDraw at least 21x for each Image State Update

The Android Image data set was difficult to test in Maui because Maui has some terrifying problems. First of all, it was very hard to get out of the startup infinite update loop once the images were added into the mix. Furthermore, scrolling back upwards frequently led to errors and crashes as I will point out in a minute.

However, one of the most strikingly strange things I encountered in all this is the following.

With Android images, we have available to monitor:

        protected override void DrawableStateChanged() {
            UpdateCounter.addImageDrawStateUpdate(); 
            base.DrawableStateChanged();
        }
        protected override void OnDraw(Canvas canvas) {
            UpdateCounter.addImageDrawUpdate(); //doesn't run on xamarin at all
            base.OnDraw(canvas);
        }
        protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            UpdateCounter.addImageMeasureUpdate(this.GetHashCode().ToString());
            base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
        }
        protected override void OnLayout(bool changed, int left, int top, int right, int bottom) {
            UpdateCounter.addImageLayoutUpdate();
            base.OnLayout(changed, left, top, right, bottom);
        }

What was fascinating and bizarre is the Xamarin manages to render the photos without once calling OnDraw. It only calls DrawableStateChanged and roughly in proportion to how many photos it must display.

Xamarin:

Here is the output from Xamarin Android when I load app, clear counters, select CardWithPhoto, and then scroll to the bottom:

TRIAL 1:

[0:] IMAGE STATE UPDATE 119
[0:] IMAGE MEASURE UPDATE 177
[0:] IMAGE LAYOUT UPDATE 177

TRIAL 2:

[0:] IMAGE STATE UPDATE 121
[0:] IMAGE MEASURE UPDATE 177
[0:] IMAGE LAYOUT UPDATE 177

For reference there should be 100 images (100 entries, 1 image per entry), so it is only running image state changed slightly more than the total count of images. It doesn't run OnDraw even once.

Maui:

By contrast, in Maui, it is an absolute disaster. I was not able to scroll through the page due to the infinite loop and crashes, but I could gather a bit of data in that by one point I had before crashing:

[0:] IMAGE STATE UPDATE 21
[0:] IMAGE DRAW UPDATE 455

This suggests that by the 21st photo display or so, it had run OnDraw 455 times (drawn 455 photos?), which is absolutely surreal. How is Xamarin not running OnDraw at all? While Maui is running it 21x more often than there are image state changes???

It blows my mind.

jonmdev commented 2 months ago

Problem 6 (Android): Android in Maui crashes due to its "unique" Image drawing method compared to Xamarin

It seems clear then that Image is now drawing photos in a completely different way from Xamarin and this is not favorable at all. The component seems deeply broken compared to the Xamarin version.

As you scroll you will encounter endless errors of: [View] ContentViewGroup not displayed because it is too large to fit into a software layer (or drawing cache), needs 12932556 bytes, only 10368000 available

(note that Xamarin handles the exact same photos without a single complaint.)

And eventually if you scroll back up you will crash with:

[Bitmap] Called getConfig() on a recycle()'d bitmap! This is undefined behavior!
**Java.Lang.RuntimeException:** 'Canvas: trying to use a recycled bitmap android.graphics.Bitmap@236f184'

Where to begin? 😬

Any thoughts?

jonmdev commented 2 months ago

Problem 7 (iOS): Maui lays out Images ~2.7 times as many times as Xamarin for the same workload

With iOS the image behavior is at least not so broken as to be unquantifiable, but just like everything else it is far poorer than Xamarin.

Loading the app, clearing the counters, clicking the CardWithPhoto option, and then scrolling top to bottom I get:

Maui:

TRIAL 1:

[0:] IMAGE LAYOUT UPDATE 406

TRIAL 2:

[0:] IMAGE LAYOUT UPDATE 396

Xamarin:

TRIAL 1:

[0:] IMAGE LAYOUT UPDATE 138

TRIAL 2:

[0:] IMAGE LAYOUT UPDATE 154

Thus we get an average of 146 image layouts in Xamarin, and 401 image layouts in Maui, meaning Maui lays out Images 2.7x as many times as Xamarin for the same workload.

What more can be said?

😬

jonmdev commented 2 months ago

Final Thoughts?

I am shocked at the severity but also happy the problems are so easily quantified. I think a few things may need to be done if getting the same performance as Xamarin is expected, as there are basically two fundamental issues:

1) IMAGE IN ANDROID

2) LAYOUT SYSTEM

Did I miss anything? Feel free to fork or download my projects as I forked them from @eth-ellis if you want to experience it for yourself. 😬

bcaceiro commented 2 months ago

Great work @jonmdev , seems reasonably obvious that there is an issue, regarding overdrawing that will affect all applications. Hope this gets the right attention asap

Alex-Dobrynin commented 2 months ago

@jonmdev You can just look at their commits, to understand if they need mobile framework: tests, tests, tests, ui tests, windows windows windows, snapshots, tests tests, windows... And NO, Absolutely nothing related to layout problems on ios and android.

Who the f**k even needs that windows? people needs MOBILE framework firstly, and then, maybe when all the problems on mobile platforms are gone, then and only then think about desktop. By the way, if you need desktop, I'd rather use Avalonia, but 100% not MAUI

AlleSchonWeg commented 2 months ago

This should be number one prio (top of tops). The layout system was not the fastest in XF but Maui is a lot slower. Perhaps the team should concentrate to mobile and analyse why layouting, measuring and drawing cycles are increasing.

The reactions on @jonmdev 's tests clearly showing that a lot of people observe similar behaviors. I know this is difficult but we (and our customers) need a performant app.

jonmdev commented 2 months ago

Update: Simple Layout Test

I was curious and wanted to see if (1) the measuring/layout/drawing issues can be reproduced by a very simple layout configuration and (2) if problems would be amplified by increasing depth of nesting layout objects.

I edited my projects here:

By changing App.xaml.cs to allow an alternative application build. This is switched by the bool which remains defaulted to the original CollectionView project. If set true, you will run the simple new project instead.

This project just adds a single or nest of AbsoluteLayout (or StackLayout or VerticalStackLayout) elements and then a Label at the end in the deepest of the hierarchy. Then it clears the update counter on startup and issues one resize command to the hierarchy of layouts.

A perfectly efficient system should only give one label layout and draw after the resize.

RESULTS

Android:

iOS:

Nesting:

CONCLUSIONS

Layout problems in Maui can be very easily reproduced without any "Bindings" or CollectionView both in Android and iOS. It only takes around 10-30 lines of C# code to reproduce.

Maui Android inefficiencies are seen equally with both AbsoluteLayout and StackLayout/VerticalStackLayout in this simple test.

Maui iOS inefficiencies are also reproduced with VerticalStackLayout/StackLayout. I believe there are AbsoluteLayout inefficiencies and problems in iOS as well (as I use them regularly) though we are seeing equivalent Maui and Xamarin inefficiency in this simple test for iOS. I will need to think and test more about this.

IDEA TO SOLVE

If I was you guys (Microsoft staff), I would take a simple project like this one, add some Debug outs inside the Maui measuring/layout system at the various steps and see which steps are being run too many times in each OS and why.

I would guess this would be the easiest way to figure out the problems. Using simple C# like here you can ramp up the complexity of the layouts as things get solved and compare. Then go to the CollectionView project for eventual testing of real world performance.

Perfect CollectionView performance can be measured by counting the number of labels/images in each entry and multiplying by the number of elements (100 by default). So if 8 labels per element, once counter is cleared, then CollectionView is loaded from menu, then scrolled to bottom, perfect performance would be 800 label layouts/measures/draws.

This is all quantifiable in such simple terms. Perhaps there are other ways to approach this. That is just the best I can think of.

TEST CODE

App.xaml.cs is updated in each to:

public App() {

    //===================
    //toggle projects
    //===================
    bool testSimpleLayout = false;

    //============================================
    //1) ORIGINAL PROJECT USING MAUI SYSTEM
    //============================================
    if (!testSimpleLayout) {
        InitializeComponent();
        MainPage = new AppShell();
    }
    //============================================
    //2) SIMPLE PROJECT USING BASIC LAYOUT
    //============================================
    else {
        ContentPage mainPage = new();
        MainPage = mainPage;

        int numNested = 10; //set number of extra layouts to nest from 0-n, does not change result

        List<VerticalStackLayout> layoutList = new(); //can try alternative layout types here
        VerticalStackLayout layout = new(); //can try alternative layout types here

        mainPage.Content = layout;
        layoutList.Add(layout);

        for (int i=0; i < numNested; i++) {
            layout = new();
            layoutList[layoutList.Count - 1].Add(layout);
            layoutList.Add(layout);
        }

        Label label = new();
        label.Text = "HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO HELLO";
        layoutList[layoutList.Count-1].Add(label);

        mainPage.SizeChanged += delegate {
            UpdateCounter.resetCounters();
            Debug.WriteLine("====================RESET COUNTERS ON RESIZE");
            if (mainPage.Width > 0) {
                for (int i = 0; i < layoutList.Count; i++) {
                    layoutList[i].WidthRequest = mainPage.Width;
                    layoutList[i].HeightRequest = mainPage.Height;
                }
            }
        };

        //ABSOLUTE LAYOUT:
        //== android maui - measure 3x, layout 2x | android xamarin - measure 1x, layout 1x
        //== iOS maui - layout 2x, draw 1x | iOS xamarin - layout 2x, draw 1x

        //VERTICAL STACK LAYOUT & STACK LAYOUT:
        //== android maui - measure 3x, layout 2x | android xamarin - measure 1x, layout 1x
        //== iOS maui - layout 3x, draw 1x | iOS xamarin - layout 2x, draw 1x
    }

}

Thanks for your ongoing efforts.

Equabyte commented 2 months ago

Improving CollectionView performance is the one single most convincing MAUI enhancement that will produce the highest short-term reward as literally anybody creating professional apps with complex layouts will benefit from it: I see this card keeps switching across product increments but it's worth considering for prioritization.

chrisg32 commented 2 months ago

I am glad this one has been assigned but I would love to see the p/1 tag on it.

With all the open verified performance issues for android we are very hesitant on releasing our app to production. We were on a time crunch to migrate from Xamarin Forms to MAUI support Android 15. Since Android 15 is expected to be released today, there will be customers we will be unable to support.

jonmdev commented 2 months ago

Looks like @brentpbc posted another good example of the problem for iOS here:

https://github.com/dotnet/maui/issues/24224

The original title for this current SR9 issue was "[iOS/Android] CollectionView scrolling performance worse in .NET MAUI when compared to Xamarin.Forms (lag/stutter)"

I notice the current issue title for this thread was edited to remove iOS by @PureWeen in June seemingly by @davidortinau 's suggestion.

I don't mean to rub it in or again be rude or abrasive, but as @PureWeen is the current assignee and @davidortinau is the Microsoft head of Maui, is it currently recognized by the Maui team that an issue exists with both Android and iOS? Will both be fixed?

I hope performance in both will be corrected. No one is using Maui with the intention of only developing mobile for Android. In my own application I have worse performance in iOS actually at this point.

@davidortinau you also asked for more examples. Looks like another very good one there in that linked report for iOS showing the same issue including again videos. Perhaps that will help? Thanks again for your efforts.

bcaceiro commented 1 month ago

No news on this subject?

simon-biber commented 1 month ago

.NET 9 Preview 7 has CollectionView & CarouselView improvements with a new opt-in handler for iOS and Mac Catalyst

This release introduces two new handlers for developers to try that bring sweeping performance and stability improvements for both CollectionView and CarouselView. These new implementations are based on newer UICollectionView APIs. Opt-in by adding the following into your Program.cs:

#if IOS || MACCATALYST
appBuilder.ConfigureMauiHandlers(handlers =>
{
handlers.AddHandler<Microsoft.Maui.Controls.CollectionView, Microsoft.Maui.Controls.Handlers.Items2.CollectionViewHandler2>();
handlers.AddHandler<Microsoft.Maui.Controls.CarouselView, Microsoft.Maui.Controls.Handlers.Items2.CarouselViewHandler2>();
});
#endif
chrisg32 commented 1 month ago

.NET 9 Preview 7 has CollectionView & CarouselView improvements with a new opt-in handler for iOS and Mac Catalyst

This release introduces two new handlers for developers to try that bring sweeping performance and stability improvements for both CollectionView and CarouselView. These new implementations are based on newer UICollectionView APIs. Opt-in by adding the following into your Program.cs:

#if IOS || MACCATALYST
appBuilder.ConfigureMauiHandlers(handlers =>
{
  handlers.AddHandler<Microsoft.Maui.Controls.CollectionView, Microsoft.Maui.Controls.Handlers.Items2.CollectionViewHandler2>();
  handlers.AddHandler<Microsoft.Maui.Controls.CarouselView, Microsoft.Maui.Controls.Handlers.Items2.CarouselViewHandler2>();
});
#endif

That is well in good for iOS but those of us needing an immediate stable release for Android are still stuck.

jonmdev commented 1 month ago

.NET 9 Preview 7 has CollectionView & CarouselView improvements with a new opt-in handler for iOS and Mac Catalyst

This release introduces two new handlers for developers to try that bring sweeping performance and stability improvements for both CollectionView and CarouselView. These new implementations are based on newer UICollectionView APIs. Opt-in by adding the following into your Program.cs:

#if IOS || MACCATALYST
appBuilder.ConfigureMauiHandlers(handlers =>
{
  handlers.AddHandler<Microsoft.Maui.Controls.CollectionView, Microsoft.Maui.Controls.Handlers.Items2.CollectionViewHandler2>();
  handlers.AddHandler<Microsoft.Maui.Controls.CarouselView, Microsoft.Maui.Controls.Handlers.Items2.CarouselViewHandler2>();
});
#endif

Thanks. That's interesting but not of any particular benefit to me. I do not use CollectionView but rather my own custom systems. Again, the problem is not CollectionView. The problem is Maui's layout system.

Xamarin was fast using the old API. As proven by numerous projects and reports. Which again shows that the problem is in the layout system of Maui, not the API.

There are numerous projects and tests showing that Maui's layout system is defective. I spent almost a week straight doing tests and experiments to try to illustrate it here.

Can you acknowledge whether you guys recognize this issue as such and are working on trying to fix the basic layout issues in both iOS and Android and attain the same performance as Xamarin had with the same code?

I think we all understand this is hard work but we would all very much appreciate it. It is demoralizing to be working in a system like Maui where we can be micromanaging our own code optimizations but in the background Maui is still sluggish because the fundamental layouts are doubling and tripling the OS's workload.

Thanks again for your help and interest in the problem.

AlleSchonWeg commented 1 month ago

Hi @jonmdev perhaps you should create a new issue with your tests. Hopefully the new issue will get more attention than the comments in this issue.

lucapan commented 1 month ago

@Alex-Dobrynin thanks a lot !!!

jonmdev commented 2 weeks ago

I believe I have found the primary cause of the poor scrolling/collectionview/translation performance in iOS Maui.

Every time you change the translation of any objects in iOS, Maui is causing the whole hierarchy to re-arrange itself and re-measure itself in iOS. This is not similarly happening in Android/Windows.

This matches my experience where the poorest performing systems I now have are in iOS. It makes sense as any scrolling or translation our systems perform will therefore spam massively expensive re-measurements and re-arrangements which permeate down through the hierarchy on every frame.

I have shared my bug report here which demonstrates the issue: https://github.com/dotnet/maui/issues/24996

As far as a solution, @PureWeen (or anyone here), any thoughts?