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
21.83k stars 1.67k forks source link

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

Open daniel-c opened 8 months ago

daniel-c commented 8 months ago

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 https://github.com/dotnet/maui/issues/14978

https://github.com/dotnet/maui/assets/3500501/9cf74ebe-0674-47da-8f39-d8ca7921cb12

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 8 months ago

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.

akhanalcs commented 8 months ago

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 8 months ago

This is related to #16877.

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

mjo151 commented 8 months ago

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.

XamlTest commented 6 months ago

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')

gerhartz commented 4 months ago

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...

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
FelixCEriksson commented 3 months ago

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

GV1072 commented 3 months ago

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?

alexsmi-noveo commented 3 months ago

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.

MAUIoxo commented 3 months ago

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?

alexsmi-noveo commented 3 months ago

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

MAUIoxo commented 3 months ago

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.

MAUIoxo commented 3 months ago

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.

davidortinau commented 3 months ago

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 .

jfversluis commented 1 month ago

/similarissues

github-actions[bot] commented 1 month ago

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.

anpin commented 1 month ago

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.