xamarin / Xamarin.Forms

Xamarin.Forms is no longer supported. Migrate your apps to .NET MAUI.
https://aka.ms/xamarin-upgrade
Other
5.62k stars 1.87k forks source link

[iOS] CollectionView scrolling is jittery #7152

Open adrianknight89 opened 5 years ago

adrianknight89 commented 5 years ago

Description

I was hoping to see silky smooth scrolling with CollectionView since a lot of layouting and virtualization are native. A simple item template containing an image and two labels creates scroll performance issues, especially when the scroll slows down to come to an end.

Steps to Reproduce

  1. Run the repro on a physical device (tested on XS)
  2. Scroll down
  3. Observe that scrolling gets jumpy especially when it decelerates
  4. If unable to reproduce, you should scroll up and down several times

Expected Behavior

Scrolling should be perfect to meet production standards. Also, compare this to the Android project. You'll notice that Android scrolls better.

Actual Behavior

Scrolling is not perfect.

I have also observed that removing both labels and using FFImageLoading for images makes scrolling better, so at minimum we need to understand why labels create stutter.

Basic Information

Reproduction Link

App1TT.zip

McCafee commented 5 years ago

I've used CollectionView for a complicated view template and got even much more jittery scrolling. After investigation I found the reason. Method from the ItemsViewController ItemsViewController.cs#L209 creates content for template on every cell reuse. I think we should avoid this and make something like this (IT works much better, but looks like a crazy stuff :) and would not work with templateSelector )

      ` var template = ItemsView.ItemTemplate;
        var item = ItemsView.ItemsSource.GetItem(indexPath.Row);
        template = template.SelectDataTemplate(item, ItemsView);
        if (cell.VisualElementRenderer == null)
        {
            var view = template.CreateContent() as View;
            var renderer = createRenderer(view);
            cell.SetRenderer(renderer);
            ItemsView.AddLogicalChild(view);
        }

        cell.SetRenderer(cell.VisualElementRenderer);
        cell.VisualElementRenderer.Element.BindingContext = item;`
PureWeen commented 5 years ago

I'm guessing this is related to https://github.com/xamarin/Xamarin.Forms/issues/7128

When testing this on an iPad (6th Generation) with 12.4 the CollectionView was crashing even if I didn't have labels present

adrianknight89 commented 5 years ago

You could also see this issue if you go to CollectionView Gallery -> Observable Collection Galleries -> Multi-item add/remove, no index.

adrianknight89 commented 5 years ago

I set up a StopWatch to monitor method invocations in ApplyTemplateAndDataContext. The most expensive calls seem to be the following three lines:

var renderer = CreateRenderer(view);
cell.SetRenderer(renderer);
view.BindingContext = ItemsSource[indexPath];

As Artemio mentioned, creating a renderer and setting its binding context for a complex cell will be quite slow. If we account for a multi-column grid layout, then it can get even worse. Hmm.

adrianknight89 commented 5 years ago

Also, I wonder if we need to register / dequeue multiple cells here based on data template. Right now, everything seems to point to generic horizontal / vertical reuse ids based on scroll direction.

hartez commented 5 years ago

Also, I wonder if we need to register / dequeue multiple cells here based on data template. Right now, everything seems to point to generic horizontal / vertical reuse ids based on scroll direction.

I think you're on the right track - this is the next step for optimization. I want to see how things look with #7285 in place, but if we're looking to eke out some more performance then pooling the cells by template makes a lot of sense.

Especially if someone is using DataTemplateSelector for effects like alternating background colors - with the wrong number of items on screen, we could end up never reusing a renderer. Pooling would address that.

GiampaoloGabba commented 4 years ago

I can confirm that even with simple cells (grid with 3 labels) and a DataTemplateSelector, the scroll performance is bad: jittering even with a couple of row. Had to go back to listview.....

I though that collectionview aims to have much better performance than listview but for my usecase is not true. Sad to see this issue in the Backlog, only to me this seems a HUGE problem? I mean, the whole point to collectionview is to replace, for the better, the listview.

I tried to do a fix but myself but... i just cant :)

EDIT: Just to clarify: this happens only using DataTemplateSelector

nor0x commented 4 years ago

Any updates on this? I'm having the same issues when using a DataTemplateSelector with cells that contain images. In my case it crashes on fast scrolling gestures - this is a pretty critical bug and complete blocker for us @samhouts

samhouts commented 4 years ago

@nor0x This is on the high priority list of things to resolve. We're working on it!

Alex-111 commented 4 years ago

Will this be fixed in the next release?

It is really a bad experience for the users. We fear to get bad ratings for our App, because of this...

banjahman commented 4 years ago

Any ETA on when we can expect this fix? It's currently blocking releasing my app due to the behavior

ederjbezerra commented 4 years ago

Same issue here and still awaiting for a fix.

hartez commented 4 years ago

For those still experiencing this issue - have you tried a more recent version? The original report was for 4.3, and we've made several changes to the CollectionView's iOS implementation since then.

I'd be interested to hear whether you are still experiencing issues with 4.6 (the latest stable version) and/or the 4.7 pre-release.

ederjbezerra commented 4 years ago

For those still experiencing this issue - have you tried a more recent version? The original report was for 4.3, and we've made several changes to the CollectionView's iOS implementation since then.

I'd be interested to hear whether you are still experiencing issues with 4.6 (the latest stable version) and/or the 4.7 pre-release.

I'm on xamarin 4.6, testing a collection view with template selector(data templates can present images, labels, audio controler, buttons) and my conclusion is that's clear that had improvements in performance but if I say it's performing ideal, I would be lying. Although, yes, I'm seeing improvements comparing to xamarin 4.3/4.4, mainly when scrolling slow(seems almost perfect), but when scrolling fast it freezes a little bit and also, I can see clearly the cells being altered, it's weird but is faster than was.

Since this issue has [iOS] in title, I think it's important inform that this issue occurs in android also.

hartez commented 4 years ago

@ederjbezerra That's good information, thanks! You mentioned problems with a template selector - are you seeing the same issues with a single template? It sounds like you have a selector with 4 templates - is that accurate?

ederjbezerra commented 4 years ago

@ederjbezerra That's good information, thanks! You mentioned problems with a template selector - are you seeing the same issues with a single template? It sounds like you have a selector with 4 templates - is that accurate?

Actually, I have 15 possible templates. With a single template the jitter is smaller but I can't have only one and build others by changing elements visibility, would be a lot of work, maybe I would have a worse performance. I also tried to move from collection view to list view with cache strategy. Having only Labels it was amazingly faster, I was happy and started to move all the code to list view but when others elements entered in scene, the things got worse than with collection view and using RecycleElementAndDataTemplate caused app crashing.

banjahman commented 4 years ago

For those still experiencing this issue - have you tried a more recent version? The original report was for 4.3, and we've made several changes to the CollectionView's iOS implementation since then.

I'd be interested to hear whether you are still experiencing issues with 4.6 (the latest stable version) and/or the 4.7 pre-release.

I'm using 4.7.0.968. The issue for me only shows up when I'm displaying images in the collection view.

hartez commented 4 years ago

@banjahman Are you using any other image loading libraries (e.g., FFImageLoading)? Are you loading local (file system or embedded) images, or images from the web?

banjahman commented 4 years ago

I've been experimenting with as many different things that I can find to make the problem go away. I find it more prominent when using FFImageLoading. When I disabled that and use the built in Xamarin Image loading, the problem is somewhat bearable.

All of the images are coming from the web.

chrisfoulds commented 4 years ago

Still occurs on 4.7

libin85 commented 4 years ago

For reference: https://www.sharpnado.com/gittrends-lags/

roubachof commented 4 years ago

@libin85 my investigations are on the Android platform not iOS.

roubachof commented 4 years ago

@banjahman @hartez if you have issues with smooth scrolling and using FFImageLoading, I would humbly recommend you to use https://github.com/roubachof/Xamarin.Forms.Nuke instead.

FIELDPOINT commented 4 years ago

Happens even on 4.8. I am using multiple DataTemplates. Its much worse on Android than on iOS. Border line unusable when I have around 20 items due to the Jankyness.

PrimoDev23 commented 4 years ago

I have found an additional "Bug" while creating an own DataGrid-Control. Using CollectionView is needed ofc because I need multi-selection

So the issue appears after selecting an Item, then ordering the ItemsSource and replace current ItemsSource by the ordered one. This causes massive lagging for me. The only way I can prevent the lag is clearing the SelectedItem and SelectedItems-Property and this isn't a great choice. Also I have found out, that if the selection still stays on the screen after replacing the ItemsSource it doesn't lag anymore, aswell as it doesn't lag anymore after selecting an other item.

Greetings Primo :)

Elashi commented 3 years ago

I am experiencing similar misbehavior in CollectionView. I am not sure if it is the same problem but here what I am seeing:

Elashi commented 3 years ago

Here are images of before and after scrolling. 1- scroll the images to hide 2- then scroll them fast back to the top

Simulator Screen Shot - iPhone 11 - 2020-12-05 at 13 56 46 Simulator Screen Shot - iPhone 11 - 2020-12-05 at 14 29 51

Here is the code

Screen Shot 2020-12-05 at 2 12 12 PM Screen Shot 2020-12-05 at 2 12 45 PM
Tommigun1980 commented 3 years ago

Repro for 5.0.0-pre6 at https://github.com/xamarin/Xamarin.Forms/issues/13231 (bug 5).

azrinsani commented 3 years ago

Is there any specific guideline to avoid these jitters? Questions like:

I'm seeing jittering even on simple apps with a collection view source. No pictures, just custom entry fields. I've followed religiously the official performance optimization guidelines from Microsoft but it still jitters.

Its sad when i compare it to chrome. Chrome has no jitter on scrolls even for complex websites with thousands of divs and flex displays.

[EDIT] I have managed to solve these performance issue by finding the culprit, and it was => SYNCFUSION CONTROLS! Fyi syncfusion is a free library (for small companies) with rich UI. Just their simple text input and buttons were bad for scrolling. Using the basic template made scrolling silky smooth.

azrinsani commented 3 years ago

Sad but scroll jittering is back even after removing all syncfusion items. And i am running a prod build, as in no debuggers attached, all bindings compiled and etc

JohnHDev commented 3 years ago

I have just come across this moving from a ListView to CollectionView, the performance is dreadful. XF 5.0.0.1931.

It would be useful if Xamarin had an extra control tag to denote usability. Controls like Label, Button, Entry are production ready. CollectionView is pre-release at best, and you haven't managed to fix it in over 2 years. Deprecate it so that other developers don't waste time trying to use it. Xamarin just seems to be constantly wasting my time these days.

xleon commented 3 years ago

This is indeed very frustrating as I trusted CollectionView to replace about 10 ListViews in my app. Android performance is ok in my case though. The problematic platform is iOS. I'm refactoring back all my code to ListView, which is far from ideal.

azrinsani commented 3 years ago

This is indeed very frustrating as I trusted CollectionView to replace about 10 ListViews in my app. Android performance is ok in my case though. The problematic platform is iOS. I'm refactoring back all my code to ListView, which is far from ideal.

So in your case Android is better than IOS??? I haven't even touched iOS yet for my App. I have been reading that for iOS its supposed to be better.

xleon commented 3 years ago

Yes, I've got multiple CollectionView's as slides of a CarouselView. Android works perfectly. iOS is not only jittery, but a total disaster in terms of performance. My CollectionView is complex, it has template selectors for items and headers, but even when I remove all items and just leave the CollectionView.Header the scroll is horrible. Now I'm back to those (almost forgotten) ViewCells 😒

azrinsani commented 3 years ago

I have never used list views, I heard it's terrible for large number of items. My Collection View has 3000 items, and like yourself it is also complex. It uses a Data Template Selector, with many FlexLayouts and hundreds of binding (Fyi I'm making a profile like scroller). Scrolling is smooth to some extent. However there is an initial 'Jank/Jitter' on the first scroll gesture. Do you have this as well?

xleon commented 3 years ago

Well, I have similar experience in Android, reproducing that Jitter initially, and then again after a while (probably when GC does its job). However as I said, iOS performance is simple horrible, not comparable to Android.

azrinsani commented 3 years ago

I think it's a about time to move to flutter. This is just unbearable. Flutter uses Skia, which powers Android Chrome buttery smooth. Though as much as I hate learning a new language, it's probably worth it.

azrinsani commented 3 years ago

I'm also having a look at the UNO platform.. It sounds very promising. However, the fact that it's targeting EVERYTHING is quite worry some for me.... why can't they just stick to IOS and ANDROID ??

ederjbezerra commented 3 years ago

The funniest thing is the issue, for me, appears only on Android, different of previous comments here. For my project, which has 18 possible data templates(with maps, labels, spans, activity indicators, images, audio players etc), iOS works smoothly, absolutely no jitter. I also wanted to let you guys know that months ago, I have exchanged 100+ emails with microsoft xamarin team support and sent them my whole project, so they could analyze each line of code. They came back with a suggestion of a custom collection view renderer:

using Android.Content;
using CustomControls;
using MasterDetailPageNavigation;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(MessageCollectionView), typeof(CustomCollectionViewRenderer))]
namespace CustomControls
{

    public class CustomCollectionViewRenderer : CollectionViewRenderer
    {
        public CustomCollectionViewRenderer(Context context) : base(context)  {  }
        public override void OnScrollStateChanged(int state)
        {
            //State of 0 == Not Scrolling
            if (state != 0)
            {
                ((MessageCollectionView)this.Element).IsScrolling = true;
            }
            else
            {
                ((MessageCollectionView)this.Element).IsScrolling = false;
            }

            base.OnScrollStateChanged(state);
        }

        protected override GroupableItemsViewAdapter<GroupableItemsView, IGroupableItemsViewSource> CreateAdapter()
        {
            var adapter = base.CreateAdapter();
            adapter.HasStableIds = true;
            return adapter;
        }
    }
}

That really fixes the issue, everything starts to scroll smoothly, but after some minutes I noticed the elements sequence were being altered, you guys are free to test if it works for you, it really increases performance but in my case it messes the sequence of the messages(my project is a chat). They also suggested me to observe IsScrolling property and try don't load images/maps while it's scrolling, but that didn't help, and if did, was insignificantly. If someone here can test this renderer and be able to solve sequence issue, please share with us.

AlleSchonWeg commented 3 years ago

ATM we remove CollectionViews from our App. Replaceing with ListViews or BindableLayouts with Scrollview.

CollectionView is a great idea, but not production ready. Tons of bugs and only ready for Hello World app.

azrinsani commented 3 years ago

ATM we remove CollectionViews from our App. Replaceing with ListViews or BindableLayouts with Scrollview.

CollectionView is a great idea, but not production ready. Tons of bugs and only ready for Hello World app.

Probably ok if you have 10 items. I have 3000 items in my scroll list. List view wont cut it

azrinsani commented 3 years ago

The funniest thing is the issue, for me, appears only on Android, different of previous comments here. For my project, which has 18 possible data templates(with maps, labels, spans, activity indicators, images, audio players etc), iOS works smoothly, absolutely no jitter. I also wanted to let you guys know that months ago, I have exchanged 100+ emails with microsoft xamarin team support and sent them my whole project, so they could analyze each line of code. They came back with a suggestion of a custom collection view renderer:

using Android.Content;
using CustomControls;
using MasterDetailPageNavigation;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(MessageCollectionView), typeof(CustomCollectionViewRenderer))]
namespace CustomControls
{

    public class CustomCollectionViewRenderer : CollectionViewRenderer
    {
        public CustomCollectionViewRenderer(Context context) : base(context)  {  }
        public override void OnScrollStateChanged(int state)
        {
            //State of 0 == Not Scrolling
            if (state != 0)
            {
                ((MessageCollectionView)this.Element).IsScrolling = true;
            }
            else
            {
                ((MessageCollectionView)this.Element).IsScrolling = false;
            }

            base.OnScrollStateChanged(state);
        }

        protected override GroupableItemsViewAdapter<GroupableItemsView, IGroupableItemsViewSource> CreateAdapter()
        {
            var adapter = base.CreateAdapter();
            adapter.HasStableIds = true;
            return adapter;
        }
    }
}

That really fixes the issue, everything starts to scroll smoothly, but after some minutes I noticed the elements sequence were being altered, you guys are free to test if it works for you, it really increases performance but in my case it messes the sequence of the messages(my project is a chat). They also suggested me to observe IsScrolling property and try don't load images/maps while it's scrolling, but that didn't help, and if did, was insignificantly. If someone here can test this renderer and be able to solve sequence issue, please share with us.

Wow this is amazing I just tried and I finally felt my app was actually an App!!! Scrolling was insanely smooth. However, in my case I had 1000+ items and it only shows 4 items and repeats the 4 items again and again, ommitting the other 996 items.... It's basically useless.

Though the underlying magic was => adapter.HasStableIds = true;

If I remove this statement its back to square one.

This however got me thinking, what is it that causes the scroll jitters? Is it the layout?? or is it bindings?? if it's layouts, which one??

AlleSchonWeg commented 3 years ago

ATM we remove CollectionViews from our App. Replaceing with ListViews or BindableLayouts with Scrollview. CollectionView is a great idea, but not production ready. Tons of bugs and only ready for Hello World app.

Probably ok if you have 10 items. I have 3000 items in my scroll list. List view wont cut it

Yes, it depends on the use case. We have from 1 to 30000 elements in the list. We use a InfinityScroll behavior, like this: https://www.c-sharpcorner.com/article/infinite-scrolling-listview-for-xamarin-forms-app-using-behaviors/ and a the ObservableRangeCollection from the xamarin community toolkit. Works great with ListView.

xleon commented 3 years ago

It would be good to have some feedback from the dev team (perhaps @jsuarezruiz ?) on this. What is preventing you from fixing it?

liveinvarun commented 3 years ago

@samhouts Any update on this issue??

zzyzy commented 3 years ago

Any updates? ListView is better

Musacca commented 3 years ago

The funniest thing is the issue, for me, appears only on Android, different of previous comments here. For my project, which has 18 possible data templates(with maps, labels, spans, activity indicators, images, audio players etc), iOS works smoothly, absolutely no jitter. I also wanted to let you guys know that months ago, I have exchanged 100+ emails with microsoft xamarin team support and sent them my whole project, so they could analyze each line of code. They came back with a suggestion of a custom collection view renderer:

using Android.Content;
using CustomControls;
using MasterDetailPageNavigation;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(MessageCollectionView), typeof(CustomCollectionViewRenderer))]
namespace CustomControls
{

    public class CustomCollectionViewRenderer : CollectionViewRenderer
    {
        public CustomCollectionViewRenderer(Context context) : base(context)  {  }
        public override void OnScrollStateChanged(int state)
        {
            //State of 0 == Not Scrolling
            if (state != 0)
            {
                ((MessageCollectionView)this.Element).IsScrolling = true;
            }
            else
            {
                ((MessageCollectionView)this.Element).IsScrolling = false;
            }

            base.OnScrollStateChanged(state);
        }

        protected override GroupableItemsViewAdapter<GroupableItemsView, IGroupableItemsViewSource> CreateAdapter()
        {
            var adapter = base.CreateAdapter();
            adapter.HasStableIds = true;
            return adapter;
        }
    }
}

That really fixes the issue, everything starts to scroll smoothly, but after some minutes I noticed the elements sequence were being altered, you guys are free to test if it works for you, it really increases performance but in my case it messes the sequence of the messages(my project is a chat). They also suggested me to observe IsScrolling property and try don't load images/maps while it's scrolling, but that didn't help, and if did, was insignificantly. If someone here can test this renderer and be able to solve sequence issue, please share with us.

Is there any update about this solution?I have the same problem too.

Satvik-web commented 2 years ago

The funniest thing is the issue, for me, appears only on Android, different of previous comments here. For my project, which has 18 possible data templates(with maps, labels, spans, activity indicators, images, audio players etc), iOS works smoothly, absolutely no jitter. I also wanted to let you guys know that months ago, I have exchanged 100+ emails with microsoft xamarin team support and sent them my whole project, so they could analyze each line of code. They came back with a suggestion of a custom collection view renderer:

using Android.Content;
using CustomControls;
using MasterDetailPageNavigation;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(MessageCollectionView), typeof(CustomCollectionViewRenderer))]
namespace CustomControls
{

    public class CustomCollectionViewRenderer : CollectionViewRenderer
    {
        public CustomCollectionViewRenderer(Context context) : base(context)  {  }
        public override void OnScrollStateChanged(int state)
        {
            //State of 0 == Not Scrolling
            if (state != 0)
            {
                ((MessageCollectionView)this.Element).IsScrolling = true;
            }
            else
            {
                ((MessageCollectionView)this.Element).IsScrolling = false;
            }

            base.OnScrollStateChanged(state);
        }

        protected override GroupableItemsViewAdapter<GroupableItemsView, IGroupableItemsViewSource> CreateAdapter()
        {
            var adapter = base.CreateAdapter();
            adapter.HasStableIds = true;
            return adapter;
        }
    }
}

That really fixes the issue, everything starts to scroll smoothly, but after some minutes I noticed the elements sequence were being altered, you guys are free to test if it works for you, it really increases performance but in my case it messes the sequence of the messages(my project is a chat). They also suggested me to observe IsScrolling property and try don't load images/maps while it's scrolling, but that didn't help, and if did, was insignificantly. If someone here can test this renderer and be able to solve sequence issue, please share with us.

Is there any update about this solution?I have the same problem too.

Mee Too! Even I have the same problem in android..

cjsharp01 commented 2 years ago

I think that the issues with Android and IOS are different bugs. I am having an issue with ios but during my searches I came across this that might be useful for Android issues: https://codetraveler.io/2020/07/12/improving-collectionview-scrolling/. I cannot use ListViews because the screenreader doesn't work with it when your scroll down (so I was directed to us CollectionView instead - very good in Android but caused me several issues in iOS).

With regards to iOS, I have managed to mitigate the issue (though it isn't ideal). I notice that the CollectionView starts to jump around and jitter on the bigger cells. In the data template I now have the height request set the same on all the cells in the DataTemplate and that seems to have made it a lot better. I suspect the issue is to do with the size calculation when the cell is loaded (i.e. it could be that the scroll is expecting a cell of the same size when it isn't so it has to add more space to the scroll which causes the scroll to re-focus, hence the jittering). There might also be a case for having a smaller number of cells showing on the screen in one go (i.e. make the cells bigger) but I don't know if that helps.

Update 13/06/2022: I have just updated my phone from OS versions 15.3.1 to 15.5 and the jittering has gotten much worse on ios!

AlleSchonWeg commented 2 years ago

On iOS XF uses UICollectionView. Somebody suggests to set these two properties: https://stackoverflow.com/a/18460906/4506984 Perhaps you can modify XF or create a custom renderer for testing. I think this is the location for dequeue: https://github.com/xamarin/Xamarin.Forms/blob/d22e7d311b84deb8f5dc163906cda349f5662ffb/Xamarin.Forms.Platform.iOS/CollectionView/ItemsViewController.cs#L86