xamarin / Xamarin.Forms

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

[Bug] [iOS] CollectionView iOS inner crash while adding items to group, items aren't displayed #13268

Open bondarenkod opened 3 years ago

bondarenkod commented 3 years ago

Description

I was testing the latest build of XamarinForms when I realized that there is an issue related to a group's item manipulation on the iOS platform. I can't reproduce this error on the Android platform. I've also tried to use ObservableRangeCollection without any noticeable difference from the default ObservableCollection. There are also a few lines of iOS inner exception message:

2021-01-02 14:32:21.361034+0200 xf_cv_range.iOS[3615:42647] *** Assertion failure in -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:animator:collectionViewAnimator:], UICollectionView.m:7294
[0:] Foundation.MonoTouchException: Objective-C exception thrown.  Name: NSInternalInconsistencyException Reason: Invalid update: invalid number of sections.  The number of sections contained in the collection view after the update (1) must be equal to the number of sections contained in the collection view before the update (1), plus or minus the number of sections inserted or deleted (1 inserted, 0 deleted).

Full output log is here 02_01_202114_40_13e09b3d8b-4a6c-4d31-b7c0-9f047d3c1dfa.txt

I will add any additional info if needed.

Steps to Reproduce

  1. Use Xamarin.Forms 4.8.0.1821
  2. Add empty group to the ObservableCollection, then add items to the group:
    var group = new Group();
    Items.Add(group);
    foreach (var item in items)
    group.Add(item);
  3. Run the app.

If you are using my demo project:

  1. Run the app on any iOS device/simulator.
  2. Switch to the second tab (Browse)
  3. Apply pull-to-refresh on the CV.
  4. You can downgrade XF version to 4.8.0.1687 and repeat the test to see that now it's working.

Expected Behavior

Added items are presented (visible) on the screen.

Actual Behavior

Added items aren't presented (visible) on the screen. You can see the exception in the VS's output window.

Basic Information

Environment

not applicable

Build Logs

not applicable

Screenshots

Good result (XF 4.8.0.1687): https://user-images.githubusercontent.com/3184414/103457346-2ec98280-4d07-11eb-81e1-caabdf11bce0.mp4 Bad result (XF 4.8.0.1821): https://user-images.githubusercontent.com/3184414/103457371-6df7d380-4d07-11eb-8916-0615bedaae21.mp4

Reproduction Link

https://github.com/bondarenkod/xf_cv_group_add_range_bug

Workaround

Downgrade to Xamarin.Forms 4.8.0.1687

mphill commented 3 years ago

This is happening in Xamarin Forms 5.0.0.1829-pre6 as well. Code was previously working on older versions of Xamarin <= 4.8.0.1687.

This is only triggered with IsGrouped="True"

AdrianoBinhara commented 3 years ago

Bind your list after the for statement. If you add itens to your list as you bind it to your collectionView the app is going to crash

kgouraw commented 3 years ago

I think issue with ObservableCollection. Generic List works on iOS on XF>4.8.0.1687, but it doesn't bind CollectionView IsGrouped on Android. ObservableCollection is important part of MVVM, it should be fixed. Downgraded XF 4.8.1687 for working on both platforms for now.

bondarenkod commented 3 years ago

@kgouraw nope, the problem inside the synchronizer.

vniehues commented 3 years ago

Are there any updates on this? I know it's only been reported recently but it really slows down our dev/test cycles.

RenatGaliew commented 3 years ago

In version 5.0.0.2012 no changes, we are waiting

vniehues commented 3 years ago

I was actually able to work around this issue by using the ObservableRangeCollection and using it's AddRange() method. It might have been a different underlying issue though.

RenatGaliew commented 3 years ago

I was actually able to work around this issue by using the ObservableRangeCollection and using it's AddRange() method. It might have been a different underlying issue though.

Let's try by AddRange. I will write how the results will be.

RenatGaliew commented 3 years ago

I was actually able to work around this issue by using the ObservableRangeCollection and using it's AddRange() method. It might have been a different underlying issue though.

The result is negative. Did not help. Let's try to think of another solution.

bondarenkod commented 3 years ago

@RenatGaliew try to use dynamic data

RenatGaliew commented 3 years ago

@bondarenkod my data is already dynamic, once the data is loaded everything is fine, then after switching to another page and returning to the current one, an error crashes and nothing more will be added, it crashes on the add method

PCDK commented 3 years ago

I am seeing the same thing, and I am also using the Dynamic Data package to operate the collection.

bondarenkod commented 3 years ago

@RenatGaliew oh, sorry, I meant this https://github.com/reactivemarbles/DynamicData

kalprajkakani commented 3 years ago

Any working workaround?

samirgcofficial commented 3 years ago

Still Crashing !! Xamarin Forms : 5.0.0.2012

Nadraw commented 3 years ago

This happens when grouped items are added to an ObservableCollection that is bound to a CollectionView. It works fine up to XF 4.8.0.1687. Any version since then generates the error in the original post. As a workaround, I don't bind the CollectionView in xaml. My CollectionView is inside a RefreshView, so I use code behind PropertyChanged event "IsRefreshing". If true then RemoveBinding. If false then SetBinding. Basically removing the binding before populating the collection, then adding the binding once the collection is fully populated. In the ViewModel, don't reuse the ObservableCollection with the .Clear method. Always instantiate the variable as a new ObservableCollection. I battled this for days, and this was the only way I could avoid the error.

samirgcofficial commented 3 years ago

await Task.Delay(300);
//Then again bind the cleaned Observable collection object. This trick just worked for me.

gurkan8941 commented 3 years ago

I worked around the issue by adding a private method to do the updating where the binding is removed/added. Everywhere in the code where I previously updated my ObservableCollection I now instead call the new private method using a new ObservableCollection each time.

before:

MeetingLocationsGrouped.Clear();
MeetingLocationsGrouped = new ObservableCollection<Grouping<DayOfWeek, MeetingLocationPageModel>>(result);

after:

UpdateMeetingLocationsGrouped(new ObservableCollection<Grouping<DayOfWeek, MeetingLocationPageModel>>());
UpdateMeetingLocationsGrouped(new ObservableCollection<Grouping<DayOfWeek, MeetingLocationPageModel>>(result));
private void UpdateMeetingLocationsGrouped(ObservableCollection<Grouping<DayOfWeek, MeetingLocationPageModel>> data)
{
     _meetingLocationsCollectionView.RemoveBinding(CollectionView.ItemsSourceProperty);
     MeetingLocationsGrouped = data;
     _meetingLocationsCollectionView.SetBinding(CollectionView.ItemsSourceProperty, nameof(this.MeetingLocationsGrouped));
}
bondarenkod commented 3 years ago

I've just tested this bug again, this time directly in XF sources, it seems like the issue is still there and 100% reproducible. For those who are stuck with it, it can be fixed by adding a small delay between .Clear() and .Add(). In my demo adding even await Task.Delay(100); eliminated the crash.

var itemsToAdd = Enumerable.Range(0, 20).Select(x => new Member($"Guardian-{x}")).ToArray();
var group = new ObservableTeam("Excalibur", new List<Member>(0));
itemsSource.Clear();
await Task.Delay(100);
itemsSource.Add(group);
foreach (var item in itemsToAdd)
    group.Add(item);                

https://user-images.githubusercontent.com/3184414/121402297-5f6eb180-c962-11eb-88de-800b1dccd450.mp4

bondarenkod commented 3 years ago

Adding .LayoutIfNeeded() into L:\src_github\xf500\Xamarin.Forms.Platform.iOS\CollectionView\ObservableGroupedSource.cs eliminated the crash.

void Reload()
{
    ResetGroupTracking();

    _collectionView.ReloadData();
    _collectionView.LayoutIfNeeded();
    _collectionView.CollectionViewLayout.InvalidateLayout();
}

But it continues writing such messages to the console (the same in the attached demo, so I'll skip it for now):

2021-06-09 23:47:34.850570+0300 XamarinFormsControlGalleryiOS[33463:330322] [UICollectionView] Invalid update:
 invalid number of items in section 0.  
The number of items contained in an existing section after the update (1)
 must be equal to the number of items contained in that section before the update (1), 
plus or minus the number of items inserted or deleted from that section (1 inserted, 0 deleted) 
and plus or minus the number of items moved into or out of that section (0 moved in, 0 moved out). -
 will perform reloadData. UICollectionView instance: <UICollectionView: 0x7ffaa5057800; 
frame = (0 0; 390 753); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = 
<NSArray: 0x600001cb48d0>; layer = <CALayer: 0x6000016ef8a0>; contentOffset: {0, 0}; contentSize: {390, 65}; adjustedContentInset: {0, 0, 0, 0}; layout: <Xamarin_Forms_Platform_iOS_ListViewLayout: 
0x7ffaa601cc80>; dataSource: <Xamarin_Forms_Platform_iOS_GroupableItemsViewController_1: 
0x7ffaa601d4f0>>; currentUpdate: [UICollectionViewUpdate - 0
x7ffaa3d6a890: old:<UICollectionViewData: 0x600002dc1fe0> 
new<UICollectionViewData: 0x600002df83c0> items:<(
    "I(0,0)"
)>]
iupchris10 commented 3 years ago

I've opened up a similar issue for this, when the CollectionView is Grouped and I try to .Add() multiple items. Very frustrating. https://github.com/xamarin/Xamarin.Forms/issues/14362

lndalmd commented 2 years ago

Adding “await Task.Delay(100);”, or even 1ms helps, but does introduce a strange behavior when users scrolls the list. It seems to flick and get very slow - almost unusable, in my case. Another issue is the pull to refresh. It stopped working.

Going back to old version of XF. Thanks for help.

jfversluis commented 2 years ago

Seems like some people reported this as still not working unfortunately.

jfversluis commented 2 years ago

Everyone still experiencing this and watching this issue, a PR for this is open now, would you be able to grab the NuGet as described here and let us know if this fixes this issue? That will greatly speed up the review process. Make sure to take the exact version number that is listed on the PR.

Besides verifying if this particular issue is fixed also be sure to check other scenarios in the same area to make sure that this fix doesn't accidentally has side-effects 🙂

Thanks!

jfversluis commented 2 years ago

Anyone able to test this?

BenBtg commented 2 years ago

@jfversluis I've been working with a customer who is still seeing this issue. They have helped create a repro sample app which consistently crashes ReproSample

bondarenkod commented 2 years ago

I'm sorry guys, I'm currently closing release, so I'm not able to pay any attention to this issue nor this or the next week.

SEngelsKTS commented 2 years ago

Anyone able to test this?

@jfversluis I have tested the PR. My problems with the CollectionView were solved with it!

kazutsugu commented 2 years ago

@jfversluis ?? Where to find the NuGet? Nothing in the DevOps artifact. Sorry. I am newbie, may be making a stupid mistake.

SEngelsKTS commented 2 years ago

@jfversluis ?? Where to find the NuGet? Nothing in the DevOps artifact. Sorry. I am newbie, may be making a stupid mistake.

You have to add an additional package source (https://aka.ms/forms-prs/index.json), then the NuGet will be found.

kazutsugu commented 2 years ago

@jfversluis Super! Your PR appears fixing my issue too. Another quick advice please. What can I do, in order to be notified when this particular PR is merged into an official release?

ikettansinha commented 2 years ago

I have downgraded the Xamarin Forms version to 5.0.0.7396 as advised Gerald by using given package source (https://aka.ms/forms-prs/index.json) but still my issue is occurring. below is exception

image image

Invalid update: invalid number of sections. The number of sections contained in the collection view after the update (1) must be equal to the number of sections contained in the collection view before the update (1), plus or minus the number of sections inserted or deleted (1 inserted, 0 deleted). Collection view: <UICollectionView: 0x7f82c23fe200; frame = (0 0; 388 452); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x600002fdefd0>; layer = <CALayer: 0x6000067ef700>; contentOffset: {0, 0}; contentSize: {388, 204.99999999999994}; adjustedContentInset: {0, 0, 0, 0}; layout: <Xamarin_Forms_Platform_iOS_ListViewLayout: 0x7f82c17d7070>; dataSource: <Xamarin_Forms_Platform_iOS_GroupableItemsViewController_1: 0x7f82c3837810>>

Objective-C exception thrown. Name: NSInternalInconsistencyException Reason: Invalid update: invalid number of sections. The number of sections contained in the collection view after the update (1) must be equal to the number of sections contained in the collection view before the update (1), plus or minus the number of sections inserted or deleted (1 inserted, 0 deleted). Collection view: <UICollectionView: 0x7f82c23fe200; frame = (0 0; 388 452); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x600002fdefd0>; layer = <CALayer: 0x6000067ef700>; contentOffset: {0, 0}; contentSize: {388, 204.99999999999994}; adjustedContentInset: {0, 0, 0, 0}; layout: <Xamarin_Forms_Platform_iOS_ListViewLayout: 0x7f82c17d7070>; dataSource: <Xamarin_Forms_Platform_iOS_GroupableItemsViewController_1: 0x7f82c3837810>> Native stack trace: 0 CoreFoundation 0x0000000111121ba4 __exceptionPreprocess + 242 1 libobjc.A.dylib 0x000000011fa12be7 objc_exception_throw + 48 2 Foundation 0x0000000119523aa2 _userInfoForFileAndLine + 0 3 UIKitCore 0x0000000138f7ab6f -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:animator:collectionViewAnimator:] + 14108 4 UIKitCore 0x0000000138f75f90 -[UICollectionView _updateSections:updateAction:] + 435 5 UIKitCore 0x0000000138f7605a -[UICollectionView insertSections:] + 64 6 UIKit 0x0000000166aa9537 -[UICollectionViewAccessibility insertSections:] + 42 7 AppName 0x000000010edd8239 xamarin_dyn_objc_msgSend + 217 8 ??? 0x00000001676c1730 0x0 + 6030104368

SEngelsKTS commented 2 years ago

@ikettansinha The correct version of Gerald is XF 5.0.0.8268.

Version 5.0.0.7396 is only chosen as an example in the description. The correct version can be found in the linked pull request #15439.

ikettansinha commented 2 years ago

@SEngelsKTS It solved the issue thanks you very much for the prompt response. @jfversluis Thanks gerald versluis

kazutsugu commented 2 years ago

@jfversluis Super! Your PR appears fixing my issue too. Another quick advice please. What can I do, in order to be notified when this particular PR is merged into an official release?

It stops crushing indeed. But the scrolling gets very slow, actually unusable. So I went back to official latest package, abandoned ObservableObjects, and have List instead.

ikettansinha commented 2 years ago

Hi @kazutsugu So now you are using simple list as item source for your collection view?

ikettansinha commented 2 years ago

Same Happening now. Scrolling is not usable now

kazutsugu commented 2 years ago

Hi @kazutsugu So now you are using simple list as item source for your collection view?

Yes. If I understand correctly, this issues comes in the combination of CollectionView grouping enabled and ObservableObjects. I saw in a sample code provided by MS that for CollectionView grouping enabled, it is actually List is used.

reydev-sourcecoder commented 2 years ago

Bind your list after the for statement. If you add itens to your list as you bind it to your collectionView the app is going to crash

This solves the issue in my case. Thanks.

Markus2807 commented 1 year ago

Hi I ran into the same issue.

Here is a great workaround for this problem. Instead of using an ObservableCollection use DynamicData.

https://github.com/RolandPheasant/DynamicData.Snippets/blob/e294b28247e41a032105828b6ca663ca16104690/DynamicData.Snippets/Group/XamarinFormsGrouping.cs

This one worked for me and got some benefits sorting the list

mrobraven commented 1 year ago

Still not working 2023

draganDBL commented 1 year ago

do we know what is causing the issue ? on android it is ok but on ios still not working. I'm using dynamic data SourceCache

` var subject = new SourceCache<Subject, long>(s => s.Id); subject.Edit(innerCashe => { innerCashe.Clear(); innerCashe.AddOrUpdate(dbSubjects); });

        var dataConnection = subject.Connect(); 
        dataConnection
                .Where(x => x != null)
                .RefCount()
                .Filter(queryFilter)
                .Transform(s=>new SubjectCellViewModel(s))
                .Group(GroupByGroupName)
                .Transform(subj => new ObservableGroupedCollection<string, SubjectCellViewModel, long>(subj))
                .Sort(SortExpressionComparer<ObservableGroupedCollection<string, SubjectCellViewModel, long>>.Ascending(a => a.Key))
                .ObserveOn(MainThreadScheduler)
                .Bind(out _subjects)
                .DisposeMany()
                .Subscribe();

                 private string  GroupByGroupName(SubjectCellViewModel subjectCellViewModel) {
                                      return subjectCellViewModel.GroupName;}                    
                `
bondarenkod commented 1 year ago

@draganDBL try these methods:

.Batch(TimeSpan.FromMilliseconds(100))
.ObserveOn(RxApp.MainThreadScheduler)
.SubscribeOn(RxApp.MainThreadScheduler)

I heavily rely on DynamicData for some fresh features in my projects. I decided not to use native grouping feature in my project long time ago, instead I do group using this:

    public interface IRxViewModel
    {
        string Handle { get; }
        IRxOrderMetadata Rx { get; }
    }

 public interface IRxOrderMetadata
{
    int RxGroup { get; set; }
    DateTimeOffset RxOrderDate { get; set; }
    ulong RxOrder { get; set; }
}

This gives me possibility to have all the grouping features except native features like sticky scrolling, etc.

draganDBL commented 1 year ago

Hello @bondarenkod still the same same exception happens.

draganDBL commented 1 year ago

Hello @bondarenkod I have added .Batch(TimeSpan.FromMilliseconds(100)) after

dataConnection
                .Where(x => x != null)
                .Batch(TimeSpan.FromMilliseconds(100)) 

and this fix my issued when I have tested on real device and no more exception. Thank you