Skip to content

Fix CarouselView Regression #28955

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open

Fix CarouselView Regression #28955

wants to merge 7 commits into from

Conversation

kubaflo
Copy link
Contributor

@kubaflo kubaflo commented Apr 12, 2025

Description

A proposed fix for a minor regression introduced in this pr #28225

Issues Fixed

Fixes #28930

Before After

@albyrock87

@Copilot Copilot bot review requested due to automatic review settings April 12, 2025 18:03
@kubaflo kubaflo requested a review from a team as a code owner April 12, 2025 18:03
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 4 out of 5 changed files in this pull request and generated 1 comment.

Files not reviewed (1)
  • src/Controls/tests/TestCases.HostApp/Issues/Issue28930.xaml: Language not supported

@@ -104,6 +106,13 @@ public override UICollectionViewLayoutAttributes PreferredLayoutAttributesFittin
var size = ConstrainedSize == default ? Measure() : ConstrainedSize;
_size = size.ToSize();
_needsArrange = true;

if (IsCarouselViewCell && PlatformHandler?.VirtualView is { } virtualView)
Copy link
Preview

Copilot AI Apr 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding an inline comment explaining the rationale for applying a custom measurement override for carousel view cells to aid future maintainers.

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

@dotnet-policy-service dotnet-policy-service bot added the community ✨ Community Contribution label Apr 12, 2025
@kubaflo kubaflo self-assigned this Apr 12, 2025
@kubaflo kubaflo added platform/iOS 🍎 area-controls-collectionview CollectionView, CarouselView, IndicatorView labels Apr 12, 2025
@PureWeen
Copy link
Member

/azp run

Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@Bamich
Copy link

Bamich commented Apr 13, 2025

Hi! I tried this, it seems ItemsLayout="HorizontalList" is not respected. All CarouselViews in my project become vertical (at least child Grid elements are arranged vertically)

@albyrock87
Copy link
Contributor

@Bamich have you tried this after this merge cfe6e69 ?
If so, may you show a sample code, including whether you're using CV1 or CV2 handler?
Because all UITests pass here, including the one which fixes the issue.
Thanks

@kubaflo kubaflo changed the title Incorrect label LineBreakMode in IOS inside CarouselView - fix Improve iOS CarouselView performance Apr 13, 2025
@Bamich
Copy link

Bamich commented Apr 13, 2025

@albyrock87 @kubaflo I tested it again. CV1 seems to work as expected. But CV2 doesn't - all child elements are arranged vertically.

This is also easily reproduced in the original sample:

  1. Turn on CV2
  2. Run sample
  3. Most importantly - try to scroll the carousel horizontally, and then vertically, you will see that the scroll will go exactly vertically. In statics it is not obvious, you need to start scrolling :)

Direct ItemsLayout="HorizontalList" does not affect the behavior.

scr_rec.mov

You can also replace the MainPage.xml code in the original example with this simpler one, and then scroll the CarouselView in different directions:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
	x:Class="IssueLabeliOS9060.MainPage"
	xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
	xmlns:local="clr-namespace:IssueLabeliOS9060">
	<Grid>
		<Border Padding="10">
			<Grid VerticalOptions="Fill">
				<CarouselView HeightRequest="200" x:Name="MyCarousel" EmptyView="No data">
					<CarouselView.ItemsSource>
						<x:Array Type="{x:Type x:String}">
							<x:String>First</x:String>
							<x:String>Second</x:String>
							<x:String>Third</x:String>
						</x:Array>
					</CarouselView.ItemsSource>
					<CarouselView.ItemTemplate>
						<DataTemplate>
							<Grid
								Padding="20"
								HorizontalOptions="Fill" VerticalOptions="Fill">
								<Label
									Text="{Binding .}"
									HorizontalOptions="Fill" VerticalOptions="Fill"
									BackgroundColor="red" />
							</Grid>
						</DataTemplate>
					</CarouselView.ItemTemplate>
				</CarouselView>
			</Grid>
		</Border>
	</Grid>

</ContentPage>

Copy link
Contributor

@jsuarezruiz jsuarezruiz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some CarouselView UITests failing on iOS/Catalyst:
image

The app was expected to be running still, investigate as possible crash

Could you review if are related with the changes?

@albyrock87
Copy link
Contributor

albyrock87 commented Apr 14, 2025

@jsuarezruiz the fix is ready here kubaflo#4

I really don't understand why, but passing the scroll direction makes the Carousel2 handler crash the app without any message :/
Anyway we don't need that information in the new CarouselTemplatedCell2 🙂
I added a comment in the code and an additional test in Issue28930 to verify the carousel can scroll horizontally.

We can wait for the merge or you can cherry pick and run AZP immediately

Fix regression due to passing scroll direction into the layout
@Bamich
Copy link

Bamich commented Apr 14, 2025

I have a proposal. It doesn't seem to be directly related to this PR, but since it's called "Improve iOS CarouselView performance", maybe we should fix this right away? If I set the scroll index for a CarouselView with CV2 on iOS "too early" I get a random exception "cannot access disposed object NSPathIndex". Perhaps we should move the projectedPosition calculation a little lower in CarouselViewController2.cs by changing this part:

<...>

// with Task.Delay(100) below it results in "cannot access disposed object NSPathIndex"
var projectedPosition = NSIndexPath.FromItemSection(position, _section);

if (LoopItemsSource.Loop)
{
	//We need to set the position to the correct position since we added 1 item at the beginning
	projectedPosition = GetScrollToIndexPath(position);
}

var uICollectionViewScrollPosition = IsHorizontal ? UICollectionViewScrollPosition.CenteredHorizontally :
	UICollectionViewScrollPosition.CenteredVertically;

await Task.Delay(100).ContinueWith((t) =>
{
	MainThread.BeginInvokeOnMainThread(() =>
	{
		if (!IsViewLoaded)
		{
			return;
		}
		InitialPositionSet = true;

		if (ItemsSource is null || ItemsSource.ItemCount == 0)
		{
			return;
		}

		CollectionView.ScrollToItem(projectedPosition, uICollectionViewScrollPosition, false);

		//Set the position on VirtualView to update the CurrentItem also
		SetPosition(position);

		UpdateVisualStates();
	});

});

<...>

to something like this?

<...>

var uICollectionViewScrollPosition = IsHorizontal ? UICollectionViewScrollPosition.CenteredHorizontally : 
	UICollectionViewScrollPosition.CenteredVertically;

// just wait..
await Task.Delay(100);

MainThread.BeginInvokeOnMainThread(() =>
{
	if (!IsViewLoaded)
	{
		return;
	}

	InitialPositionSet = true;

	if (ItemsSource is null || ItemsSource.ItemCount == 0)
	{
		return;
	}

	// Get projectedPosition immediately before use (no crash)
	NSIndexPath projectedPosition = LoopItemsSource.Loop
		? GetScrollToIndexPath(position)
		: NSIndexPath.FromItemSection(position, _section);

	CollectionView.ScrollToItem(projectedPosition, uICollectionViewScrollPosition, false);

		//Set the position on VirtualView to update the CurrentItem also
		SetPosition(position);

		UpdateVisualStates();
});

<...>

Thanks

@albyrock87
Copy link
Contributor

@Bamich I think that's reasonable.
In the mean time, may you verify if the last commit here fixes the issue?
I believe so given I also added a check in the UITest, but if you can confirm I'd appreciate.
Thanks

@PureWeen
Copy link
Member

/azp run

Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@Bamich
Copy link

Bamich commented Apr 14, 2025

@albyrock87 Yes, I checked and confirm that the last commit fixes the scroll orientation issue

@PureWeen PureWeen changed the title Improve iOS CarouselView performance Fix CarouselView Regression Apr 14, 2025
@PureWeen PureWeen added this to the .NET 9 SR6.1 milestone Apr 14, 2025
Fix carousel initial position bug
@jsuarezruiz
Copy link
Contributor

/azp run

Copy link

Azure Pipelines successfully started running 3 pipeline(s).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-controls-collectionview CollectionView, CarouselView, IndicatorView community ✨ Community Contribution platform/iOS 🍎
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Incorrect label LineBreakMode in IOS inside CarouselView
5 participants