Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drawing of Borders lags behind other elements on iOS creating bizarre overlaps and glitches (okay in Windows/Android) #18204

Closed
jonmdev opened this issue Oct 20, 2023 · 28 comments · Fixed by #23156
Labels
area-controls-border Border area-drawing Shapes, Borders, Shadows, Graphics, BoxView, custom drawing fixed-in-8.0.70 fixed-in-9.0.0-preview.7.24407.4 has-workaround p/2 Work that is important, but is currently not scheduled for release platform/iOS 🍎 s/triaged Issue has been reviewed s/verified Verified / Reproducible Issue ready for Engineering Triage t/bug Something isn't working t/perf The issue affects performance (runtime speed, memory usage, startup time, etc.) (sub: perf)
Milestone

Comments

@jonmdev
Copy link

jonmdev commented Oct 20, 2023

Description

Currently, whenever a Border is resized in iOS it lags behind other elements by a large margin creating strange visual overlaps and effects.

This is likely the same overlap phenomenon seen here, which indicates the overlap/lag phenomenon is due to Borders, not Editor: #17757

That thread is more relevant therefore for the other strange Editor bugs like the textfield scrolling itself off the screen, unless perhaps these are all related and caused by the same thing.

DEMO PROJECT
This is the same project used to demonstrate the Android Shadow bug here: #18202

It draws 3 purple borders on screen and then oscillates their height continuously.

Correct animation should look like this:

resize lag android 1 resize lag android 2 resize lag android 3

Nothing should overlap at any time. However, in iOS we get the following bizarre overlaps as the animated Borders lag behind everything else and desynchronize in how they are drawn:

IMG_0008 IMG_0009 IMG_0010

Steps to Reproduce

  1. Open demo project.
  2. Play in Windows/Android and see the Border elements all remain in sync.
  3. Play in iOS and observe that nothing is staying in sync as the Border elements seem to lag behind where they should be drawn.

Link to public reproduction project repository

https://github.com/jonmdev/Resize-Lag-Bug

Version with bug

8.0.0-rc.2.9373

Is this a regression from previous behavior?

No, this is something new

Affected platforms

iOS

@XamlTest
Copy link

Verified this on Visual Studio Enterprise 17.8.0 Preview 4.0(8.0.0-rc.2.9373). Repro on iOS 16.4, not repro on Windows 11 and Android 13.0-API33 with below Project:
ResizeLagBug.zip

@mattleibow mattleibow added legacy-area-perf Startup / Runtime performance platform/iOS 🍎 labels Oct 24, 2023
@mattleibow mattleibow added this to the Backlog milestone Oct 24, 2023
@ghost
Copy link

ghost commented Oct 24, 2023

We've added this issue to our backlog, and we will work to address it as time and resources allow. If you have any additional information or questions about this issue, please leave a comment. For additional info about issue management, please read our Triage Process.

@mattleibow mattleibow added area-drawing Shapes, Borders, Shadows, Graphics, BoxView, custom drawing area-controls-border Border labels Oct 24, 2023
@ghost ghost added the legacy-area-controls Label, Button, CheckBox, Slider, Stepper, Switch, Picker, Entry, Editor label Oct 24, 2023
@jonmdev
Copy link
Author

jonmdev commented Oct 24, 2023

@albyrock87
Copy link
Contributor

albyrock87 commented Jan 19, 2024

It took me a lot of time to find a workaround, but here it is. I hope this also helps fixing the issue.

handlers.AddHandler<Border, NotAnimatedBorderHandler>();

The following workaround removes sizing animations but keeps other kind of animations which are good (i.e. background).

using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
using ContentView = Microsoft.Maui.Platform.ContentView;

namespace YourNamespace;

public class NotAnimatedBorderHandler : BorderHandler
{
    private class BorderContentView : ContentView
    {
        public override void LayoutSubviews()
        {
            // This is the only workaround I found to avoid the animation when the border size is updated.
            // https://github.com/dotnet/maui/issues/15363
            // https://github.com/dotnet/maui/issues/18204

            if (Layer.Sublayers?.FirstOrDefault(layer => layer is MauiCALayer) is { AnimationKeys: not null } caLayer)
            {
                caLayer.RemoveAnimation("bounds");
                caLayer.RemoveAnimation("position");
            }

            base.LayoutSubviews();
        }
    }
    
    protected override ContentView CreatePlatformView()
    {
        _ = VirtualView ?? throw new InvalidOperationException($"{nameof(VirtualView)} must be set to create a {nameof(ContentView)}");
        _ = MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} cannot be null");
    
        return new BorderContentView
               {
                   CrossPlatformLayout = VirtualView
               };
    }
}

Please note that setting Frame property (and probably also other things - happens for example on MapStrokeShape) implicitly re-adds animation keys, that's why I'm removing them every time.

CC @durandt

@durandt
Copy link

durandt commented Jan 21, 2024

Related #15363 (I see now it was closed during Fall, but from my latest tests on net8 the animation still existed)
Thanks @albyrock87 , I will make sure to try this next week ! Are you on net7 or net8 ?
I will make sure to test again whether the fix was really fixed in net8 (I've successfully upgraded a couple of weeks ago but haven't had time to do all tests and release yet)

@albyrock87
Copy link
Contributor

@durandt I am on .NET MAUI 8.0.3 and I can confirm what they did to prevent this issue is NOT effective, my workaround appears the only way to get around it.
When I have time I'll try to make a PR to fix the issue in one of the following SR releases.

@durandt
Copy link

durandt commented Mar 6, 2024

@albyrock87 Now with Maui 8.0.6/8.0.7 removing a few blocking bugs I have finally had time to upgrade and test my app. As you say, the Border animations seem even worse but your work-around seems to work well for me too! Thank you.

@jonmdev
Copy link
Author

jonmdev commented Mar 21, 2024

Thank you very much @albyrock87!!! I finally worked up the courage to test your fix and it did work as you suggested. It was my first time adding a custom Handler.

I have one question as I am very curious about how this works in terms of the custom Handler.

When you add the custom Handler in MauiProgram.cs, does this automatically replace the standard Border Handler inside Maui? Or is it a second Handler now attached to Border? Ie. Would each Border with this method now have two Handlers? One standard BorderHandler and one NotAnimatedBorderHandler? Or does the command addHandler check for duplicate handlers of a given type (Border) and use the one we added in exclusion to the default Maui one for Border type?

Obviously your NotAnimatedBorderHandler inherits from BorderHandler so we don't need BorderHandler anymore. I am just wondering if this is automatically dealt with in Maui or if this method is then adding two handlers per Border.

Thanks for any clarification. This is a nice relatively easy technique to customize Handlers if it doesn't just add a duplicate Handler which would be less ideal. (All commands may run twice if two handlers per Border now, if that is even possible?)

Thanks for any clarification.


For anyone who has never done this before, this is how I used it (more specific instructions for amateurs like me who have to figure it out 😊).

1) MauiProgram.cs

Add to default builder string so it reads:

        public static MauiApp CreateMauiApp() {
            var builder = MauiApp.CreateBuilder();
            builder
                .UseMauiApp<App>()
                .ConfigureFonts(fonts => {
                    fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                    fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
                })
                .ConfigureMauiHandlers((handlers) => {
#if IOS
                    handlers.AddHandler<Border, NotAnimatedBorderHandler>();
#endif
                });

#if DEBUG
            builder.Logging.AddDebug();
#endif

            return builder.Build();
        }

2) NotAnimatedBorderHandler.cs

Create a file by this name and enter in its body within the namespace:

using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
#if IOS
using ContentView = Microsoft.Maui.Platform.ContentView;
#endif 

namespace YOUR_NAMESPACE {

    public class NotAnimatedBorderHandler : BorderHandler {
#if IOS
        private class BorderContentView : ContentView {
            public override void LayoutSubviews() {

                if (Layer.Sublayers?.FirstOrDefault(layer => layer is MauiCALayer) is { AnimationKeys: not null } caLayer) {
                    caLayer.RemoveAnimation("bounds");
                    caLayer.RemoveAnimation("position");
                }

                base.LayoutSubviews();
            }
        }

        protected override ContentView CreatePlatformView() {
            _ = VirtualView ?? throw new InvalidOperationException($"{nameof(VirtualView)} must be set to create a {nameof(ContentView)}");
            _ = MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} cannot be null");

            return new BorderContentView {
                CrossPlatformLayout = VirtualView
            };
        }
#endif
    }
}

@albyrock87
Copy link
Contributor

albyrock87 commented Mar 21, 2024

@jonmdev I can confirm it replaces the default handler! :)
Especially because there can be only one handler instance on a view.

And btw, you can place iOS specific handler in Platforms/iOS folder so that you can avoid compilation flags (at least on the handler definition).

@zeniya-takeshi
Copy link

I've found a related issue.
#21643

@MarMarIV
Copy link

I tried to use the mentioned workaround with handler, but it doesn't work (v. 8.0.20)

@matheusouz
Copy link

When i was working with .NET Maui 6.0 this workaround fixed the issue, but now the workaround dont work anymore. We are in 8.0.21 version.

@albyrock87
Copy link
Contributor

@matheusouz have you tried the one above?
#18204 (comment)

@matheusouz
Copy link

Yes i tried too but no lucky.

@Eilon Eilon removed the legacy-area-controls Label, Button, CheckBox, Slider, Stepper, Switch, Picker, Entry, Editor label May 10, 2024
@jonmdev
Copy link
Author

jonmdev commented Jun 14, 2024

@albilaga I have posted the new "1 frame lag on redraw" bug report here:

#23070

I used your test project and another one I made combined into one to show the issue, as your test project and gif best showed the square border effect which I am also seeing my real projects but had trouble figuring out how to reproduce consistently.

My project in there shows there is a redraw lag also on the background color. Likely this is all the same bug and the order of operations must be changed on Border redraw to get them to redraw immediately when needed rather than after 1 frame to fix both.

If anyone has any understanding of the Border and can think of any way to fix it that would be great also. I looked at BorderHandler and Border code and I can't see how it really works to understand a fix.

One problem down, one more to go.

Also just for reference the simplest NotAnimatedBorderHandler.cs code I have tested and works is:

using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;

#if IOS
using UIKit;
using ContentView = Microsoft.Maui.Platform.ContentView;
using Foundation;
using CoreAnimation;
#endif 

namespace iOSBorderShadowAnimationBug {

    //TEMP BUG FIX FOR: https://github.com/dotnet/maui/issues/18204
    public class NotAnimatedBorderHandler : BorderHandler {

#if IOS
        private class BorderContentView : ContentView {
            public override void LayoutSubviews() {

                Layer.RemoveAllAnimations();

                if (Layer.Sublayers != null) {

                    for (int i=0; i < Layer.Sublayers.Count(); i++) {
                        Layer.Sublayers[i].RemoveAllAnimations();
                    }
                }
                
                base.LayoutSubviews();
            }

        }

        protected override ContentView CreatePlatformView() {
            _ = VirtualView ?? throw new InvalidOperationException($"{nameof(VirtualView)} must be set to create a {nameof(ContentView)}");
            _ = MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} cannot be null");

            return new BorderContentView {
                CrossPlatformLayout = VirtualView
            };
        }
#endif
    }
}

@samhouts
Copy link
Member

We agree that this is an important issue. As our roadmap indicates, for the near future, we are focused on:

  • Issues impacting Xamarin.Forms upgrades to .NET MAUI
  • CollectionView
  • Layout

I am marking this as a p/2 issue because it does not meet our focus areas right now, but we are constantly revisiting the backlog for important issues to address as time allows. Thank you for your patience and understanding!

@samhouts samhouts added the p/2 Work that is important, but is currently not scheduled for release label Jun 18, 2024
albyrock87 added a commit to albyrock87/maui that referenced this issue Jun 20, 2024
albyrock87 added a commit to albyrock87/maui that referenced this issue Jun 20, 2024
@PureWeen PureWeen modified the milestones: Backlog, .NET 8 SR8 Jun 20, 2024
albyrock87 added a commit to albyrock87/maui that referenced this issue Jun 21, 2024
albyrock87 added a commit to albyrock87/maui that referenced this issue Jun 21, 2024
albyrock87 added a commit to albyrock87/maui that referenced this issue Jun 21, 2024
albyrock87 added a commit to albyrock87/maui that referenced this issue Jun 21, 2024
albyrock87 added a commit to albyrock87/maui that referenced this issue Jun 21, 2024
albyrock87 added a commit to albyrock87/maui that referenced this issue Jun 24, 2024
albyrock87 added a commit to albyrock87/maui that referenced this issue Jul 5, 2024
@github-project-automation github-project-automation bot moved this from Todo to Done in MAUI SDK Ongoing Jul 5, 2024
@github-actions github-actions bot locked and limited conversation to collaborators Oct 6, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-controls-border Border area-drawing Shapes, Borders, Shadows, Graphics, BoxView, custom drawing fixed-in-8.0.70 fixed-in-9.0.0-preview.7.24407.4 has-workaround p/2 Work that is important, but is currently not scheduled for release platform/iOS 🍎 s/triaged Issue has been reviewed s/verified Verified / Reproducible Issue ready for Engineering Triage t/bug Something isn't working t/perf The issue affects performance (runtime speed, memory usage, startup time, etc.) (sub: perf)
Projects
Status: Done