Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

[Bug] CollectionView is broken on IOS, when items have different sizes and ItemSizingStrategy="MeasureAllItems" #11011

Open
yurii-harkusha opened this issue Jun 11, 2020 · 31 comments

Comments

@yurii-harkusha
Copy link

Description

CollectionView is broken on IOS when items have different sizes and ItemSizingStrategy="MeasureAllItems". It means that we have no way to use items with different sizes

Steps to Reproduce

  1. We have CollectionView with items of different sizes
    Grid or FlexLayout (where Grid or Flex is child of DataTemplate) with 3 labels located in 3 rows (2 of them can be invisible in some cases which means that CollectionView cell height will be different from time to time). Of course we use ItemSizingStrategy="MeasureAllItems".
  2. On page opening our items are overlapping each other. (It seems start to be broken after 4th item - so cell sizes were calculated correctly before item height start increase from item 4 to item 5, but interesting that it is correctly handled on decreasing size between items 2-3 )
    Screenshot 2020-06-11 at 11 07 51
  3. Just move collection view a little bit (small swipe to top or to down - Scroll.Y value on collection view changed?) and it start to look and work correctly. So it seems that something block CollectionView on first loading from measure items sizes correctly.
    Screenshot 2020-06-11 at 11 15 47

Expected Behavior

All items size set correctly like after scrolling (screenshot 2)

Actual Behavior

Some of items overlap each other

Basic Information

  • Version with issue: 4.6.0.847 (Xamarin.Forms)
  • Last known good version: None
  • IDE: Any version of VS or VS for MAc
  • Platform Target Frameworks:
    • iOS: 13.18.2.1 Xamarin.iOS
    • Android: no
    • UWP: no
  • Android Support Library Version: no
  • Nuget Packages: 4.6.0.847 (Xamarin.Forms)
  • Affected Devices: iOS, iPadOS

Probably related issues

#10625 #5455 #9000 #5455 #7788

Screenshots

posted in Steps to Reproduce

Reproduction Link

no

Workaround

no

@yurii-harkusha yurii-harkusha added s/unverified New report that has yet to be verified t/bug 🐛 labels Jun 11, 2020
@hartez
Copy link
Contributor

hartez commented Jun 11, 2020

Grid or FlexLayout (where Grid or Flex is child of DataTemplate) with 3 labels located in 3 rows

Can you post the markup of your DataTemplate?

(2 of them can be invisible in some cases which means that CollectionView cell height will be different from time to time).

How are you handling the visibility of the rows? Are you setting IsVisible via DataBinding? Or some other way?

@hartez hartez added the s/needs-info ❓ A question has been asked that requires an answer before work can continue on this issue. label Jun 11, 2020
@yurii-harkusha
Copy link
Author

yurii-harkusha commented Jun 12, 2020

@hartez, Visibility is handling by text existence - if there is no text in row there will be no label, so no height in "Auto" height row.

<CollectionView Style="{StaticResource ChatsCollectionViewStyle}" ItemsSource="{Binding Path=Items}" SelectedItem="{Binding Path=SelectedItem}" EmptyView="{x:Static p:ResourceManager.PlaceholderEmptyChat}"
                            EmptyViewTemplate="{StaticResource EmptyCollectionViewTemplate}" SelectionMode="Single">
                <CollectionView.ItemTemplate>
                    <DataTemplate x:DataType="{x:Type cm:ChatDetailViewModel}">
                        <Grid AutomationId="{x:Static t:TestResources.ChatsListItemCell}" Style="{StaticResource ChatItemGridStyle}">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="63" />
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="1" />
                            </Grid.RowDefinitions>
                            <Label Grid.Row="0" Grid.Column="0" Text="{Binding Chat, Converter={StaticResource ChatTitlePreviewConverter}}" Style="{StaticResource TitleLabelStyle}" StyleClass="BoldWhenUnread" />
                            <Label Grid.Row="0" Grid.Column="1" HorizontalTextAlignment="End" HorizontalOptions="End" Text="{Binding Path=Chat.Updated, Converter={StaticResource DateTimeToDayConverterWithCustomDateFormat}}" Style="{StaticResource DateLabelStyle}" StyleClass="BoldWhenUnread" />
                            <Label Grid.Row="1" Grid.Column="0" Margin="8,2,0,0" Text="{Binding Chat, Converter={StaticResource ChatParticipantsPreviewConverter}}" Style="{StaticResource SmallerItemLabel}" StyleClass="BoldWhenUnread" />
                            <Label Grid.Row="2" Grid.Column="0" Margin="8,2,0,8" Text="{Binding Chat, Converter={StaticResource ChatLastMessagePreviewConverter}}" Style="{StaticResource SmallerItemLabel}" StyleClass="ItalicBoldWhenUnread" />
                            <Frame HorizontalOptions="End" Grid.Row="2" Grid.Column="1" Margin="8,2,8,8" WidthRequest="20" IsVisible="{Binding Chat.Unread}" Style="{StaticResource BadgeFrameStyle}">
                                <Label Text="{Binding Chat.UnreadCount, Converter={StaticResource NumberToStringLimited}}" MaxLines="1" Style="{StaticResource BadgeLabelStyle}" />
                            </Frame>
                            <BoxView Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Style="{StaticResource SeparatorStyle}" />
                        </Grid>
                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>

@yurii-harkusha
Copy link
Author

Also it is reproducible in a different types of Root elements for DataTemplate.
When I tested it I just created FlexLayout instead of grid and put there 3 labels from sample of code above (3 labels as 3 rows inside FlexLayout) - and result was the same - rows were cutted when cells height is different.

@samhouts samhouts added a/collectionview p/iOS 🍎 and removed s/needs-info ❓ A question has been asked that requires an answer before work can continue on this issue. labels Jun 12, 2020
@hartez hartez removed the s/unverified New report that has yet to be verified label Jun 12, 2020
@hartez hartez added the e/7 🕖 7 label Jun 12, 2020
@MSiccDev
Copy link

MSiccDev commented Jul 3, 2020

I can confirm this issue. In my case, I have a DataTemplateSelector which selects two very different DataTemplates based on a property in my ItemViewModel. It often happens when the smaller Template is used in the lower third of the screen.

@MSicc
Copy link

MSicc commented Jul 15, 2020

Is there any workaround we could implement for the time being?

@jefffhaynes
Copy link

I can confirm as well. Reason number 70 why CollectionView should still be experimental.

@samhouts samhouts added this to the 5.0.0 milestone Aug 13, 2020
@vladikKBR85
Copy link

Had the same problem. Because of the different Labels' heights their parent containers were overlapping. As a workaround I've set Labels' TextType proberty to HTML, and wrapped their values. Something like: $"<p style='color:#8f92a1;font-size:16px;font-family: Arial;display:inline-block;'>{Text}</p>";. Not good, but seem to work..

@gustavo-cantero
Copy link

I've the same problem.
Is there any workaround or resolution date?

@rihadavid
Copy link

rihadavid commented Sep 8, 2020

I had a similar problem with dynamically resizing items not being correctly resized and solved it by calling this every time after I change an item:

collectionView.ItemSizingStrategy = ItemSizingStrategy.MeasureFirstItem;
collectionView.ItemSizingStrategy = ItemSizingStrategy.MeasureAllItems;

This seems to remeasure all items.. Don't forget to call it on a main thread.

@rudyspano
Copy link

rudyspano commented Sep 21, 2020

After a lot of attempt, I achieved to have a solution that is working on iOS with a CollecitonView Grouped containing different DataTemplate of different Sizes (using DataTemplateSelector) with hundred of items.

If it can help :
In my opinion, there's two main causes with the current implementation

  1. Self Sizing. The implementation used by ItemSizingStrategy.MeasureAllItems is based on self sizing. It seems that, there's still some weird behavior using this approach... I think that's a better practice to define a fixed size for each possible DataTemplate (should be ok for a lot of scenario). Inspired by [Bug] [iOS] CollectionView - Invalid Height Measure when using different datatemplates #10842 (comment), I've added a mechanism that provides the expected cell size for each item type in order to avoid the use of EstimatedSize things if possible...:
public override CGSize GetSizeForItem(UICollectionView collectionView, UICollectionViewLayout layout, NSIndexPath indexPath)
{
    //use CollectionView ItemsSource to get ViewModel type associated to indexPath
    var itemType = GetCellItemViewModel(indexPath);

    if (itemType == null)
    {
        return ItemsViewLayout.EstimatedItemSize;
    }

    //Get Height from your Shared Assembly
    var cellHeight = ProduitItemViewModelsCellAssociations.GetCellHeight(itemType);
    var cellWidth = (double)collectionView.Frame.Width;

    //Special treatments if using GridViewLayout ?
    if (_itemsViewLayout is GridViewLayoutAdvanced gridViewLayoutAdvanced)
    {
        cellWidth /= gridViewLayoutAdvanced.Span;
        cellWidth -= gridViewLayoutAdvanced.HorizontalItemSpacing;
    }

    return new CGSize(cellWidth, cellHeight);
}
  1. ReuseIdentifier mechanism. Foreach VerticalCell, the same ReuseIdentifier is used: see the RegisterViewTypes method https://github.com/xamarin/Xamarin.Forms/blob/f35ae07a0a8471d255f7a1ebdd51499e10e0a4cb/Xamarin.Forms.Platform.iOS/CollectionView/ItemsViewController.cs. I've added a mechanism that creates a ReuseIdentifier dedicated for each DataTemplate (=> ItemViewModel type).
//Custom Controller
 private class GroupableItemsViewAdvancedController<TItemsView> : GroupableItemsViewController<TItemsView>
            where TItemsView : GroupableItemsView
{
    public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
    {
        var cellItemViewModel = GetCellItemViewModel(indexPath);

        if (cellItemViewModel == null)
        {
            return base.GetCell(collectionView, indexPath);
        }

        var defaultCellReuseIdentifier = DetermineCellReuseId();
        var cellReuseIdentifier = defaultCellReuseIdentifier + cellItemViewModel.GetType().ToString() ?? string.Empty;

        //Ensure that each type of item (particular DataTemplate) has its proper identifier
        //to avoid recycling problems and follow iOS best practice
        if (!_customCellsIdentidiers.Contains(cellReuseIdentifier))
        {
            //Get VerticalCell internal type if not already get
            _defaultCellInstanceType = _defaultCellInstanceType ?? CollectionView.DequeueReusableCell(defaultCellReuseIdentifier, indexPath).GetType();

            CollectionView.RegisterClassForCell(_defaultCellInstanceType, cellReuseIdentifier);
            _customCellsIdentidiers.Add(cellReuseIdentifier);
        }

        var nativeCell = collectionView.DequeueReusableCell(cellReuseIdentifier, indexPath) as UICollectionViewCell;

        //Code from base.GetCell
        switch (nativeCell)
        {
            case DefaultCell defaultCell:
                UpdateDefaultCell(defaultCell, indexPath);
                break;
            case TemplatedCell templatedCell:
                UpdateTemplatedCell(templatedCell, indexPath);
                break;
        }

        return nativeCell;
    }
}

Feel free to comment/enhance my solution :)

@hartez
Copy link
Contributor

hartez commented Feb 8, 2021

This is fixed in 5.0.0.1931.

@hartez hartez closed this as completed Feb 8, 2021
@IncreaseComputers
Copy link

This appears to be reoccurring in 5.0.0.2012

@AndreyBespamyatnov
Copy link

we still have the same issue

@azrinsani
Copy link

Still same issue.. not to mention the jitterh scroll and initial janks

@Redth Redth reopened this May 7, 2021
@justinasfour04
Copy link

justinasfour04 commented May 7, 2021

@Redth I can confirm that MeasureAllItems in not working on iOS. I just tried it and I lose scrolling when I have it set. If I change it to MeasureFirstItem it works fine but not what I want

@vhugogarcia
Copy link

@Redth I can confirm that MeasureAllItems in not working on iOS. I just tried it and I lose scrolling when I have it set. If I change it to MeasureFirstItem it works fine but not what I want

@justinasfour04 I'm exactly facing the same issue.

Sadly, it appears it won't be fixed until MAUI is released, maybe by the end of the year.

@WanftMoon
Copy link

@justinasfour04 @vhugogarcia

Is not a workaround, but when i need to have different sized cells, i'm using ListView instead of collectionview

@vhugogarcia
Copy link

@justinasfour04 @vhugogarcia

Is not a workaround, but when i need to have different sized cells, i'm using ListView instead of collectionview

Thanks @WanftMoon It appears that is what I need to implement then :(

I was expecting to use the new SwipeViews on the colllection view.

What a sad thing.

@justinasfour04
Copy link

@vhugogarcia what a shame. No one will be using MAUI right away when it comes out especially in a production environment. How is the Xamarin team not dedicating support right now to fixing these

@bares43
Copy link

bares43 commented May 7, 2021

Don't waste your time with CollectionView because it has ton of bugs. Use ScrollView + StackLayout with BindableLayout instead. I've been using this for some time without any problem.

@justinasfour04
Copy link

@bares43 Android is fine. iOS is the buggy one

@bares43
Copy link

bares43 commented May 7, 2021

@justinasfour04 I know, but in Xamarin we usually do apps for both, Android and iOS :D

@justinasfour04
Copy link

I know that but I really don't want to build a collectionview from scratch

@bares43
Copy link

bares43 commented May 7, 2021

@justinasfour04 It depends what you want but it's not that difficult. If you need just a feed of items, then just put a StackLayout inside ScrollView, bind items to BindableLayout in StackLayout and set some view to StackLayout. Your new CollectionVIew is ready 🎆 🎆 🎆 ... If you need something more complicated, it may not be so easy though :D However I guess, that to build something will be much faster than somebody will fix this issue :D

@vhugogarcia
Copy link

Maybe there is out there a nuget packages that provides a good collection view and listview alternative. I don't know.

@justinasfour04
Copy link

Syncfusion

@Elashi
Copy link
Contributor

Elashi commented May 8, 2021

May be

Sadly, it appears it won't be fixed until MAUI is released, maybe by the end of the year.

@Depechie
Copy link
Contributor

Depechie commented Jul 1, 2021

Maybe there is out there a nuget packages that provides a good collection view and listview alternative. I don't know.

Take a look at https://github.com/roubachof/Sharpnado.HorizontalListView it is called Horizontal list, but has vertical and grid too.

@KMWenyon
Copy link

Still not fixed in 5.0.0.2299...

@MosCD3
Copy link

MosCD3 commented Dec 24, 2021

The same exact issue #10842
Still not fixed (5.0.0.2291) that's frustrating
Our app is public and many users complain about that

@cjsharp01
Copy link

I have a very similar issue (in XF 5.0.0.2401, ios only) - it resizes everything to the height of the first item resulting in the stacks overlapping. But this only happens for me if the refreshview (containing the collectionview) has a refresh triggered. And for some reason it works when the screen is vertical or the app is running on ios version 12.4 (at least in the simulator).

@samhouts samhouts added the p/1 label Jan 6, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests