dotnet/maui

CollectionView with grouping show duplicates groups or crash when adding/removing data on iOS and MacCatalyst

daniel-c opened this issue ยท 22 comments

Description

A CollectionView with IsGrouped set to True results in a wrong view rendering with duplicate groups when data is added, usually if not at the top scroll position. It crashes sometimes when data is removed. This happen on iOS and MacCatalyst. It works correctly on Android.

This may be related to issue #14978

Simulator.Screen.Recording.-.iPhone.15.Pro.-.2023-10-12.at.11.12.06.mp4

Steps to Reproduce

  1. Deploy the sample project on iOS Simulator or on Mac.
  2. Scroll a bit down the displayed list
  3. Click 'Add' to add items to the list and observe the rendering of duplicate groups.
  4. Click 'Clear' to reset the list: the app crash or display empty groups.

Link to public reproduction project repository

https://github.com/daniel-c/MauiGroupedCollectionTest.git

Version with bug

8.0.0-rc.1.9171

Is this a regression from previous behavior?

Not sure, did not test other versions

Last version that worked well

Unknown/Other

Affected platforms

iOS, macOS

Affected platform versions

iOS 17, macOS 14.0

Did you find any workaround?

No

Relevant log output

ObjCRuntime.ObjCException: Objective-C exception thrown.  Name: NSInternalInconsistencyException Reason: Invalid batch updates detected: the number of sections and/or items returned by the data source before and after performing the batch updates are inconsistent with the updates.
Data source before updates = { 1 section with item counts: [1] }
Data source after updates = { 1 section with item counts: [1] }
Updates = [
	Insert item (0 - 0)
]
Collection view: <UICollectionView: 0x138bba800; frame = (0 0; 393 754.333); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x600000c689f0>; backgroundColor = UIExtendedGrayColorSpace 0 0; layer = <CALayer: 0x6000006edf20>; contentOffset: {0, 0}; contentSize: {393, 134}; adjustedContentInset: {0, 0, 0, 0}; layout: <Microsoft_Maui_Controls_Handlers_Items_ListViewLayout: 0x132374fc0>; dataSource: <Microsoft_Maui_Controls_Handlers_Items_ReorderableItemsViewController_1: 0x13235cac0>>
Native stack trace:
	0   CoreFoundation                      0x00007ff80048d28d __exceptionPreprocess + 242
	1   libobjc.A.dylib                     0x00007ff800057894 objc_exception_throw + 48
	2   Foundation                          0x00007ff800da2c80 _userInfoForFileAndLine + 0
	3   UIKitCore                           0x000000012068c0a7 -[UICollectionView _Bug_Detected_In_Client_Of_UICollectionView_Invalid_Batch_Updates:] + 115
	4   UIKitCore                           0x000000012068b2bc -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:animator:collectionViewAnimator:] + 13332
	5   UIKitCore                           0x0000000120686fec -[UICollectionView _updateRowsAtIndexPaths:updateAction:updates:] + 492
	6   UIKitCore                           0x00000001206870d2 -[UICollectionView insertItemsAtIndexPaths:] + 64
	7   libxamarin-dotnet-debug.dylib       0x000000010e73e0a9 xamarin_dyn_objc_msgSend + 217
	8   libmonosgen-2.0.dylib               0x000000010ee96db1 do_icall + 193
	9   libmonosgen-2.0.dylib               0x000000010ee955d7 do_icall_wrapper + 295
	10  libmonosgen-2.0.dylib               0x000000010ee862f6 mono_interp_exec_method + 3990
	11  libmonosgen-2.0.dylib               0x000000010ee83a63 interp_runtime_invoke + 259
	12  libmonosgen-2.0.dylib               0x000000010ef6aaed mono_runtime_try_invoke + 157
	13  libmonosgen-2.0.dylib               0x000000010ef6deae mono_runtime_invoke + 478
	14  TestCollectionView                  0x000000010ce571a5 _ZL31native_to_managed_trampoline_10P11objc_objectP13objc_selectorPP11_MonoMethodS0_j + 469
	15  TestCollectionView                  0x000000010cec19f2 -[UIKit_UIBarButtonItem_Callback InvokeAction:] + 50
	16  UIKitCore                           0x00000001212514c6 -[UIApplication sendAction:to:from:forEvent:] + 95
	17  UIKitCore                           0x00000001204682d9 -[UIBarButtonItem _triggerActionForEvent:fallbackSender:] + 267
	18  UIKitCore                           0x00000001204381bb __45-[_UIButtonBarTargetAction _invoke:forEvent:]_block_invoke + 39
	19  UIKitCore                           0x0000000120438068 -[_UIButtonBarTargetAction _invoke:forEvent:] + 152
	20  UIKitCore                           0x00000001212514c6 -[UIApplication sendAction:to:from:forEvent:] + 95
	21  UIKitCore                           0x00000001208e92f3 -[UIControl sendAction:to:forEvent:] + 112
	22  UIKitCore                           0x00000001208e96e8 -[UIControl _sendActionsForEvents:withEvent:] + 334
	23  UIKitCore                           0x00000001208e9744 -[UIControl _sendActionsForEvents:withEvent:] + 426
	24  UIKitCore                           0x00000001208e7f79 -[UIControl touchesEnded:withEvent:] + 485
	25  UIKitCore                           0x0000000121294ae8 -[UIWindow _sendTouchesForEvent:] + 1261
	26  UIKitCore                           0x0000000121296c54 -[UIWindow sendEvent:] + 5284
	27  UIKitCore                           0x000000012126be6c -[UIApplication sendEvent:] + 772
	28  UIKitCore                           0x0000000121318b99 __dispatchPreprocessedEventFromEventQueue + 8406
	29  UIKitCore                           0x000000012131b455 __processEventQueue + 8414
	30  UIKitCore                           0x00000001213118d6 __eventFetcherSourceCallback + 163
	31  CoreFoundation                      0x00007ff8003e9d0f __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
	32  CoreFoundation                      0x00007ff8003e9c51 __CFRunLoopDoSource0 + 157
	33  CoreFoundation                      0x00007ff8003e944e __CFRunLoopDoSources0 + 215
	34  CoreFoundation                      0x00007ff8003e3b83 __CFRunLoopRun + 919
	35  CoreFoundation                      0x00007ff8003e3409 CFRunLoopRunSpecific + 557
	36  GraphicsServices                    0x00007ff80a650187 GSEventRunModal + 137
	37  UIKitCore                           0x000000012124b3a2 -[UIApplication _run] + 972
	38  UIKitCore                           0x000000012124fe10 UIApplicationMain + 123
	39  libxamarin-dotnet-debug.dylib       0x000000010e6f847a xamarin_UIApplicationMain + 58
	40  libmonosgen-2.0.dylib               0x000000010ee96e45 do_icall + 341
	41  libmonosgen-2.0.dylib               0x000000010ee955d7 do_icall_wrapper + 295
	42  libmonosgen-2.0.dylib               0x000000010ee862f6 mono_interp_exec_method + 3990
	43  libmonosgen-2.0.dylib               0x000000010ee83a63 interp_runtime_invoke + 259
	44  libmonosgen-2.0.dylib               0x000000010ef69b68 mono_runtime_invoke_checked + 136
	45  libmonosgen-2.0.dylib               0x000000010ef7133b mono_runtime_exec_main_checked + 107
	46  libmonosgen-2.0.dylib               0x000000010edd61d2 mono_jit_exec + 354
	47  libxamarin-dotnet-debug.dylib       0x000000010e73cbca xamarin_main + 1898
	48  TestCollectionView                  0x000000010ceea694 main + 68
	49  dyld                                0x000000010d7023ee start_sim + 10
	50  ???                                 0x00000001189493a6 0x0 + 4707357606

Native stack trace:
	0   CoreFoundation                      0x00007ff80048d28d __exceptionPreprocess + 242
	1   libobjc.A.dylib                     0x00007ff800057894 objc_exception_throw + 48
	2   Foundation                          0x00007ff800da2c80 _userInfoForFileAndLine + 0
	3   UIKitCore                           0x000000012068c0a7 -[UICollectionView _Bug_Detected_In_Client_Of_UICollectionView_Invalid_Batch_Updates:] + 115
	4   UIKitCore                           0x000000012068b2bc -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:animator:collectionViewAnimator:] + 13332
	5   UIKitCore                           0x0000000120686fec -[UICollectionView _updateRowsAtIndexPaths:updateAction:updates:] + 492
	6   UIKitCore                           0x00000001206870d2 -[UICollectionView insertItemsAtIndexPaths:] + 64
	7   libxamarin-dotnet-debug.dylib       0x000000010e73e0a9 xamarin_dyn_objc_msgSend + 217
	8   libmonosgen-2.0.dylib               0x000000010ee96db1 do_icall + 193
	9   libmonosgen-2.0.dylib               0x000000010ee955d7 do_icall_wrapper + 295
	10  libmonosgen-2.0.dylib               0x000000010ee862f6 mono_interp_exec_method + 3990
	11  libmonosgen-2.0.dylib               0x000000010ee83a63 interp_runtime_invoke + 259
	12  libmonosgen-2.0.dylib               0x000000010ef6aaed mono_runtime_try_invoke + 157
	13  libmonosgen-2.0.dylib               0x000000010ef6deae mono_runtime_invoke + 478
	14  TestCollectionView                  0x000000010ce571a5 _ZL31native_to_managed_trampoline_10P11objc_objectP13objc_selectorPP11_MonoMethodS0_j + 469
	15  TestCollectionView                  0x000000010cec19f2 -[UIKit_UIBarButtonItem_Callback InvokeAction:] + 50
	16  UIKitCore                           0x00000001212514c6 -[UIApplication sendAction:to:from:forEvent:] + 95
	17  UIKitCore                           0x00000001204682d9 -[UIBarButtonItem _triggerActionForEvent:fallbackSender:] + 267
	18  UIKitCore                           0x00000001204381bb __45-[_UIButtonBarTargetAction _invoke:forEvent:]_block_invoke + 39
	19  UIKitCore                           0x0000000120438068 -[_UIButtonBarTargetAction _invoke:forEvent:] + 152
	20  UIKitCore                           0x00000001212514c6 -[UIApplication sendAction:to:from:forEvent:] + 95
	21  UIKitCore                           0x00000001208e92f3 -[UIControl sendAction:to:forEvent:] + 112
	22  UIKitCore                           0x00000001208e96e8 -[UIControl _sendActionsForEvents:withEvent:] + 334
	23  UIKitCore                           0x00000001208e9744 -[UIControl _sendActionsForEvents:withEvent:] + 426
	24  UIKitCore                           0x00000001208e7f79 -[UIControl touchesEnded:withEvent:] + 485
	25  UIKitCore                           0x0000000121294ae8 -[UIWindow _sendTouchesForEvent:] + 1261
	26  UIKitCore                           0x0000000121296c54 -[UIWindow sendEvent:] + 5284
	27  UIKitCore                           0x000000012126be6c -[UIApplication sendEvent:] + 772
	28  UIKitCore                           0x0000000121318b99 __dispatchPreprocessedEventFromEventQueue + 8406
	29  UIKitCore                           0x000000012131b455 __processEventQueue + 8414
	30  UIKitCore                           0x00000001213118d6 __eventFetcherSourceCallback + 163
	31  CoreFoundation                      0x00007ff8003e9d0f __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
	32  CoreFoundation                      0x00007ff8003e9c51 __CFRunLoopDoSource0 + 157
	33  CoreFoundation                      0x00007ff8003e944e __CFRunLoopDoSources0 + 215
	34  CoreFoundation                      0x00007ff8003e3b83 __CFRunLoopRun + 919
	35  CoreFoundation                      0x00007ff8003e3409 CFRunLoopRunSpecific + 557
	36  GraphicsServices                    0x00007ff80a650187 GSEventRunModal + 137
	37  UIKitCore                           0x000000012124b3a2 -[UIApplication _run] + 972
	38  UIKitCore                           0x000000012124fe10 UIApplicationMain + 123
	39  libxamarin-dotnet-debug.dylib       0x000000010e6f847a xamarin_UIApplicationMain + 58
	40  libmonosgen-2.0.dylib               0x000000010ee96e45 do_icall + 341
	41  libmonosgen-2.0.dylib               0x000000010ee955d7 do_icall_wrapper + 295
	42  libmonosgen-2.0.dylib               0x000000010ee862f6 mono_interp_exec_method + 3990
	43  libmonosgen-2.0.dylib               0x000000010ee83a63 interp_runtime_invoke + 259
	44  libmonosgen-2.0.dylib               0x000000010ef69b68 mono_runtime_invoke_checked + 136
	45  libmonosgen-2.0.dylib               0x000000010ef7133b mono_runtime_exec_main_checked + 107
	46  libmonosgen-2.0.dylib               0x000000010edd61d2 mono_jit_exec + 354
	47  libxamarin-dotnet-debug.dylib       0x000000010e73cbca xamarin_main + 1898
	48  TestCollectionView                  0x000000010ceea694 main + 68
	49  dyld                                0x000000010d7023ee start_sim + 10
	50  ???                                 0x00000001189493a6 0x0 + 4707357606
mjo151 commented

Collection view appears to be completely broken on iOS in .NET 8 and the latest .NET 7 SR. In additional to the bug listed in this issue, scrolling performance is very bad and memory usage goes through the roof when scrolling the collection view. It almost seems like views are not getting recycled and are being recreated. This used to work fine in .NET 7 before the latest service release.

It baffles me to see what's going on with MAUI. Why is it so hard to get basic controls working without issues?

mjo151 commented

This is related to #16877.

Can someone take a look at this issue for the .NET 8 release?

mjo151 commented

I just tested this on an iPhone 12 Pro running iOS 16.7.1 and did not notice the problem. The problem occurs for me on an iPhone 15 Pro running iOS 17.0.3. Based on that, I don't think this is a regression in the collection view. I think this is caused by some change in iOS 17.

Verified this on Visual Studio Enterprise 17.9.0 Preview 1(8.0.3). Repro on iOS 17.0 and MacCatalyst, not repro on Android 14.0-API34 with below Project:
MauiGroupedCollectionTest.zip

On Windows: Run failed with below exception.
The value "TestCollectionView.ViewModels.AnimalGroup" is not of type "TestCollectionView.ViewModels.Animal" and cannot be used in this generic collection. (Parameter 'value')

My workaround is to reinitialize a new CollectionView each time the underlying data changes, rather than relying on a single CollectionView with a dynamically changing ItemSource. It works well on Android to change the ItemsSource, but for some reason on iOS it doesn't play nice with changing groups.

I'm seeing duplicate groups when the CollectionView goes from having 2 groups down to 1 group. Its only a problem on iOS. Not seeing problems when there is only ever 1 group.

My setup is...

  • iPhone Xr running iOS 16.7.2
  • MAUI workload 7.0.96
  • CollectionView displaying 2 groups with 1+ items
  • Filter out items such that we only have 1 group now

Here's a screenshot with black boxes as an example. In the 2nd screenshot it should only be displaying the "Older" group but for some reason it displays that group twice and the 2nd group has an empty slot.

Screenshot 2024-02-15 at 3 39 03 PM

The issue dissapeared for me when i set the HeightRequest on the root element in the ItemTemplate and the GroupHeaderTemplate

I am also facing the same issue. We have a large enterprise application in Xamarin Forms. We planned to migrate it to MAUI in Q1-24. But this issue is stopping me from moving further, as my completed app depends on the Exaplandable Collection View.

Even I checked with .NET 8 latest release but still reproducing.
Can you please take this as a priority item?

We faced this issue too after upgrade to .NET 8.0. We have group collection view and 2 buttons that navigates between weeks. The combination of scroll a tap button event clears data, but headers one covers old one. This affects iOS release. And yes, collection view performance is terrible in release. Items in collection ~8.

UPD: Upgrade MAUI libraries to 8.0.14 or 8.0.20-nightly significantly increased Group CollectionView performance and responsiveness. And glitch with overlapping headers seems to disappear.

I am facing the same issue with nightly build of 8.0.20. Did you try the workaround described above or without? Is this still a way to go with the above?

I am facing the same issue with nightly build of 8.0.20. Did you try the workaround described above or without? Is this still a way to go with the above?

Yes, i setup fixed height for header template and data template in collection view

Thanks, then I will give it a try. First of all I was thinking, that my queries to get the grouped ObservableRangeCollection was somehow wrong. But, I figured out there was just one element in one of my groups and it showed two group headers. Even when I have an empty grouped ObservableRangeCollection, I can see 2 empty headers.

I could figure it out, how I could get my scenario working. Just wanted to share it, in case somebody else needs ideas what to try to get the own scenario working.

This is how it behaved before:

SearchfilterCollectionCorrupted

<Grid RowDefinitions="Auto, *">
    
    <searchBarControl:SearchBarLang Grid.Row="0" ios:SearchBar.SearchBarStyle="Minimal" Text="{Binding SearchText}" Placeholder="Search item..." CancelButtonText="{loc:Translate CancelButtonText}" Margin="0, 10, 0, 5"/>

    <CollectionView Grid.Row="1" ItemsSource="{Binding GroupedItems, Mode=OneWay}" VerticalScrollBarVisibility="Always" IsGrouped="True" Margin="0, 0, 0, 50">

        <!-- Group Header Template:  Titles of a Group -->
        <CollectionView.GroupHeaderTemplate>
            <DataTemplate x:DataType="viewModels:GroupedSelectionItemList">
                <StackLayout VerticalOptions="CenterAndExpand" HorizontalOptions="FillAndExpand">
					<Label FontSize="16" FontAttributes="Bold" Text="{Binding GroupName, StringFormat=' {0}'}" TextColor="{StaticResource Blue100Accent}" Margin="0, 25, 0, 0" />
					<BoxView HeightRequest="0.5" Color="{StaticResource Gray200}" HorizontalOptions="FillAndExpand" Margin="0, 5, 0, 5" />
                </StackLayout>
            </DataTemplate>
        </CollectionView.GroupHeaderTemplate>

        <!-- Item Template:  Selected Item -->
        <CollectionView.ItemTemplate>
            <DataTemplate x:DataType="{x:Type databaseModels:ItemSelection}">
                <Grid ColumnDefinitions="*, Auto" RowDefinitions="Auto" Padding="{OnPlatform Default='5, 0, 5, 0', iOS='5, 0, 5, 10'}">
                    <Label Grid.Row="0" Grid.Column="0" Text="{Binding Item.Name}" VerticalOptions="Center" />
                    <Switch Grid.Row="0" Grid.Column="1" IsEnabled="True" IsToggled="{Binding IsSelected, Mode=TwoWay}" ThumbColor="{StaticResource Gray100}" OnColor="{StaticResource DarkOrange1}" VerticalOptions="Center" HorizontalOptions="Center" Margin="{OnPlatform Android='0, -5, 0, -5'}"/>
                </Grid>
            </DataTemplate>
        </CollectionView.ItemTemplate>

    </CollectionView>

</Grid>

With this change it went to this:

SearchfilterCollectionWorking

<Grid RowDefinitions="Auto, Auto">
    
    <searchBarControl:SearchBarLang Grid.Row="0" ios:SearchBar.SearchBarStyle="Minimal" Text="{Binding SearchText}" Placeholder="Search item..." CancelButtonText="{loc:Translate CancelButtonText}" Margin="0, 10, 0, 5"/>

    <Grid Grid.Row="1">
        <CollectionView ItemsSource="{Binding GroupedItems, Mode=OneWay}" VerticalScrollBarVisibility="Always" IsGrouped="True" Margin="0, 0, 0, 50">

            <!-- Group Header Template:  Titles of a Group -->
            <CollectionView.GroupHeaderTemplate>
                <DataTemplate x:DataType="viewModels:GroupedSelectionItemList">
                    <StackLayout VerticalOptions="CenterAndExpand" HorizontalOptions="FillAndExpand">
                        <Label FontSize="16" FontAttributes="Bold" Text="{Binding GroupName, StringFormat=' {0}'}" TextColor="{StaticResource Blue100Accent}" Margin="0, 25, 0, 0" />
                        <BoxView HeightRequest="0.5" Color="{StaticResource Gray200}" HorizontalOptions="FillAndExpand" Margin="0, 5, 0, 5" />
                    </StackLayout>
                </DataTemplate>
            </CollectionView.GroupHeaderTemplate>

            <!-- Item Template:  Selected Item -->
            <CollectionView.ItemTemplate>
                <DataTemplate x:DataType="{x:Type databaseModels:ItemSelection}">
                    <Grid ColumnDefinitions="*, Auto" RowDefinitions="Auto" Padding="{OnPlatform Default='5, 0, 5, 0', iOS='5, 0, 5, 10'}">
                        <Label Grid.Row="0" Grid.Column="0" Text="{Binding Item.Name}" VerticalOptions="Center" />
                        <Switch Grid.Row="0" Grid.Column="1" IsEnabled="True" IsToggled="{Binding IsSelected, Mode=TwoWay}" ThumbColor="{StaticResource Gray100}" OnColor="{StaticResource DarkOrange1}" VerticalOptions="Center" HorizontalOptions="Center" Margin="{OnPlatform Android='0, -5, 0, -5'}"/>
                    </Grid>
                </DataTemplate>
            </CollectionView.ItemTemplate>

        </CollectionView>
    </Grid>

</Grid>

Basically, changed the RowDefinitions of the outer Grid from <Grid RowDefinitions="Auto, *"> to <Grid RowDefinitions="Auto, Auto"> and also wrapped the CollectionView inside another Grid which did the job here for me.

I found the NSInternalInconsistencyException was occurring for me after the Clear() and when immediate adding back to the collection object. By adding a delay of 100ms here the crash went away.

This seems like a regression of #10163 @PureWeen .

/similarissues

Hi I'm an AI powered bot that finds similar issues based off the issue title.

Please view the issues below to see if they solve your problem, and if the issue describes your problem please consider closing this one and thumbs upping the other issue to help us prioritize it. Thank you!

Open similar issues:

Closed similar issues:

Note: You can give me feedback by thumbs upping or thumbs downing this comment.

I found the NSInternalInconsistencyException was occurring for me after the Clear() and when immediate adding back to the collection object. By adding a delay of 100ms here the crash went away.

This seems like a regression of #10163 @PureWeen .

Adding 100 ms doesn't resolve it for me. This issue existed in the XF and delay was the workaround we used which after porting to MAUI doesn't work anymore.

Hi, This is still an issue and we are restraining from publishing our App due to this.
None of the workarounds mentioned here & there worked for me.
Is there any ETA for this burning issue that restricting many from publishing their apps.
Regards

Just stumble upon this... seems to be many problems with grouped collectionview !

Running into the same bug.

For me it appears in a chat, with items that are not a fixed size, where I adjust the bottom margin of a CollectionView when the keyboard opens, which then results in the group headers being duplicated and all over the place.

Bildschirmaufnahme.2024-09-13.um.08.37.43.webm

Check out the repro here:
https://github.com/FlavioGoncalves-Cayas/CollectionViewDuplicateHeadersRepro

It's really frustrating to see that nobody cares. I just gave you a reproduction and there is no action taken whatsoever. We are encountering this problem in a production app and honestly it's embarassing.
Especially since there does not seem to be a viable workaround, or did I miss something?

Good news. This PR #24873 from @Tamilarasan-Paranthaman fixes the issue at least for my repro.