Skip to content

[iOS] Fix vertical CarouselView MandatorySingle snapping on iOS#34700

Open
Vignesh-SF3580 wants to merge 3 commits intodotnet:mainfrom
Vignesh-SF3580:fix-33308
Open

[iOS] Fix vertical CarouselView MandatorySingle snapping on iOS#34700
Vignesh-SF3580 wants to merge 3 commits intodotnet:mainfrom
Vignesh-SF3580:fix-33308

Conversation

@Vignesh-SF3580
Copy link
Copy Markdown
Contributor

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Issue details

After a swipe, the carousel could move visually, but it did not consistently snap to a single item or reliably update the Position.

Root cause

The vertical CarouselView path in LayoutFactory2.cs had inconsistent behavior:

  • Vertical CarouselView was not routed through the custom snap-enabled compositional layout
  • Page tracking still used horizontal offset/width calculations
  • Loop correction still relied on a horizontal scroll anchor

Because these were not aligned to the vertical axis, the control could move visually while leaving Position updates stale or inconsistent.

Description of change

This PR updates the iOS CarouselView layout path in LayoutFactory2.cs to handle the vertical flow consistently end to end:

  • Route vertical linear CarouselView through CustomUICollectionViewCompositionalLayout
  • Compute page progression using the vertical axis (offset.Y and container height)
  • Use UICollectionViewScrollPosition.Top for vertical loop correction

With these changes, snapping, page calculation, loop repositioning, and Position updates all follow a consistent vertical model.

Issues Fixed

Fixes #33308

Technical Details

The fix is implemented in src/Controls/src/Core/Handlers/Items2/iOS/LayoutFactory2.cs.

  • CreateCarouselLayout(...) now detects vertical LinearItemsLayout and routes it through CustomUICollectionViewCompositionalLayout.

  • VisibleItemsInvalidationHandler calculates page progression using vertical offset and container height for vertical carousels.

  • Vertical loop correction uses UICollectionViewScrollPosition.Top instead of a horizontal anchor.

This ensures that layout selection, snap behavior, page calculation, and final Position updates are all aligned along the same vertical axis.

Screenshots

Before Issue Fix After Issue Fix
33308BeforeFix.mov
33308AfterFix.mov

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 27, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34700

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34700"

@dotnet-policy-service dotnet-policy-service bot added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Mar 27, 2026
@Vignesh-SF3580 Vignesh-SF3580 added the community ✨ Community Contribution label Mar 27, 2026
@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Mar 28, 2026

🚦 Gate — Test Verification

📊 Expand Full Gate2e31d74 · Fixed-33308 : CarouselView vertical snap points ignored on iOS with Microsoft.Maui.Controls v10.0.20

Gate Result: ❌ FAILED

Platform: IOS

Tests Detected

# Type Test Name Filter
1 UITest Issue33308 Issue33308

Verification

Step Expected Actual Result
Without fix FAIL FAIL
With fix PASS FAIL

Details

  • Failed: VerticalCarouselMandatorySingleSnapAdvancesOneCard [4 s]
  • 📋 Error: VisualTestUtils.VisualTestFailedException :
    Snapshot different than baseline: VerticalCarouselMandatorySingleSnapAdvancesOneCard.png (5.13% difference)
    If the correct baseline has changed (this isn't...

Fix Files Reverted

  • eng/pipelines/ci-copilot.yml
  • src/Controls/src/Core/Handlers/Items2/iOS/LayoutFactory2.cs

Base Branch: main | Merge Base: 720a9d4


@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Mar 28, 2026

🤖 AI Summary

📊 Expand Full Review2e31d74 · Fixed-33308 : CarouselView vertical snap points ignored on iOS with Microsoft.Maui.Controls v10.0.20
🔍 Pre-Flight — Context & Validation

Issue: #33308 - CarouselView vertical snap points ignored on iOS with Microsoft.Maui.Controls v10.0.20 (regression from v9.0.120)
PR: #34700 - [iOS] Fix vertical CarouselView MandatorySingle snapping on iOS
Platforms Affected: iOS
Files Changed: 1 implementation (LayoutFactory2.cs), 2 test files, 3 snapshot files

Key Findings

  • Regression: Vertical CarouselView with SnapPointsType=MandatorySingle stopped snapping in CV2 (Items2 handler) introduced in .NET 10. v9 handler still works as workaround.
  • Root cause (PR author's analysis): Vertical CarouselView was not routed through CustomUICollectionViewCompositionalLayout (the snap-aware layout), relying instead on a plain UICollectionViewCompositionalLayout with no TargetContentOffset override.
  • CRITICAL BUG IN PR: The constructor call at line 431–440 uses 4 arguments but CustomUICollectionViewCompositionalLayout(LayoutSnapInfo, LayoutGroupingInfo?, LayoutHeaderFooterInfo?, UICollectionViewCompositionalLayoutSectionProvider, UICollectionViewCompositionalLayoutConfiguration, ItemsUpdatingScrollMode) requires 6 arguments. The call passes sectionProvider where LayoutGroupingInfo? is expected, layoutConfiguration where LayoutHeaderFooterInfo? is expected, and omits the actual sectionProvider and configuration arguments entirely. This is a compile error.
  • Gate failure reason: Gate FAILED with a 5.13% screenshot mismatch. The fix cannot build (constructor arity error), so the test likely ran against the pre-fix app binary — the unsnapped carousel screenshot doesn't match the PR's snapped baseline.
  • Test issues: HostApp/Issues/Issue33308.cs sets no AutomationId on the CarouselView or cards. App.WaitForElement("Card 1") relies on label text (fragile). App.ScrollDown("Card 1", ...) scrolls via the child label text rather than the carousel itself.
  • Android snapshot added: PR adds VerticalCarouselMandatorySingleSnapAdvancesOneCard.png to Android snapshots but the [Issue] attribute marks PlatformAffected.iOS only; the test class has no Android-specific exclusion.
  • Fix direction is correct: Routing vertical carousels through CustomUICollectionViewCompositionalLayout and using TargetContentOffset pipeline for MandatorySingle snapping is the right approach. The page-offset calculation change (offset.Y / container height for vertical) and loop-correction axis change (UICollectionViewScrollPosition.Top) are also correct.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #34700 Route vertical CarouselView through CustomUICollectionViewCompositionalLayout; fix offset/loop axis for vertical ❌ FAILED (Gate) LayoutFactory2.cs Constructor arity bug — 4 args passed, 6 required; snapshot mismatch

🔧 Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix (claude-opus-4.6) Fix constructor arity bug: add for missing / params; keep PRs Y-axis and loop-correction fixes PASS LayoutFactory2.cs Minimal change; completes PR's exact intent; test passed ~3s
2 try-fix (claude-sonnet-4.6) scroll delegate override in with page-pitch arithmetic; fix Y-axis tracking in LayoutFactory2 PASS CarouselViewDelegator2.cs, LayoutFactory2.cs Touches 2 files; different mechanism from layout routing
3 try-fix (gpt-5.3-codex) Set OrthogonalScrollingBehavior = GroupPagingCentered for all carousel FAIL LayoutFactory2.cs 2.34% snapshot diff; orthogonal paging for vertical sections misaligns orientations
4 try-fix (gpt-5.4, sub gemini) Route ALL carousels through ; set from orientation PASS LayoutFactory2.cs Unified path; potentially affects existing horizontal carousel behavior
PR PR #34700 Route vertical CarouselView through CustomUICollectionViewCompositionalLayout; fix offset/loop FAILED (Gate) LayoutFactory2.cs Constructor arity 4 args passed, 6 required; code doesn't compile bug axis

Cross-Pollination

Model Round New Ideas? Details
claude-opus-4.6 2 Yes PagingEnabled = true on only works if item == viewport size; doesn't handle peek insets or item spacing
claude-sonnet-4.6 2 Yes DecelerationRate = Fast + override ` same as what CustomUICollectionViewCompositionalLayout already does; not new
gpt-5.3-codex 2 Yes Use VisibleItemsInvalidationHandler to compute nearest complex; wrong lifecycle; not a cleaner solution
gpt-5.4 2 Yes Sort LayoutAttributesForElementsInRect by axis before ` quality improvement to ScrollSingle, not a root-cause fix

Exhausted: all 4 models queried; no cross-pollination ideas materially better than existing passing candidatesYes
Selected Fix: Candidate #1 (Attempt fix constructor arity bug with null, null)1

Reason: Most surgical fix. Resolves the exact compile bug with the fewest changes, directly completes the PR author's intent, and has no risk of affecting horizontal carousel behavior. The PR's overall approach is architecturally it just needs the two missing null arguments.correct


📋 Report — Final Recommendation

⚠️ Final Recommendation: REQUEST CHANGES

Phase Status

Phase Status Notes
Pre-Flight ✅ COMPLETE Issue #33308, iOS-only regression, LayoutFactory2.cs
Gate ❌ FAILED iOS — 5.13% screenshot diff; code doesn't compile (constructor arity bug)
Try-Fix ✅ COMPLETE 4 attempts, 3 passing; best fix identified
Report ✅ COMPLETE

Summary

PR #34700 fixes a real iOS regression (issue #33308) where vertical CarouselView with MandatorySingle snap points freely scrolls instead of snapping. The PR's conceptual approach is correct: route vertical carousels through CustomUICollectionViewCompositionalLayout so the existing TargetContentOffset pipeline handles snapping, and fix the page-calculation and loop-correction axes for vertical.

However, the PR does not compile due to a constructor arity bug. The PR calls new CustomUICollectionViewCompositionalLayout(snapInfo, sectionProvider, layoutConfiguration, itemsUpdatingScrollMode) with 4 arguments, but the constructor requires 6: (snapInfo, groupingInfo?, headerFooterInfo?, sectionProvider, configuration, itemsUpdatingScrollMode). The two nullable parameters groupingInfo and headerFooterInfo are omitted and should be null. This prevents the fix from building, which is why the Gate failed with a 5.13% screenshot mismatch (the app ran without the fix applied).

Root Cause

Vertical CarouselView in the CV2 iOS handler was not routed through CustomUICollectionViewCompositionalLayout, which overrides TargetContentOffset to implement MandatorySingle snapping. Without this routing, snapping was silently ignored. Additionally, the page-offset calculation used offset.X / width (horizontal) instead of offset.Y / height (vertical), causing Position to stay stale, and the loop correction used UICollectionViewScrollPosition.Left instead of .Top for vertical.

Fix Quality

The PR's fix logic is sound and well-targeted. The only required change to make it work is adding null, null for the two missing constructor arguments. Once corrected, the test VerticalCarouselMandatorySingleSnapAdvancesOneCard passes.

Required changes (to unblock the PR):

-		var layout = linearItemsLayout?.Orientation == ItemsLayoutOrientation.Vertical
-			? new CustomUICollectionViewCompositionalLayout(
-				new LayoutSnapInfo
-				{
-					SnapType = linearItemsLayout.SnapPointsType,
-					SnapAligment = linearItemsLayout.SnapPointsAlignment
-				},
-				sectionProvider,
-				layoutConfiguration,
-				linearItemsLayout.ItemsUpdatingScrollMode)
-			: new UICollectionViewCompositionalLayout(sectionProvider, layoutConfiguration);
+		var layout = linearItemsLayout?.Orientation == ItemsLayoutOrientation.Vertical
+			? new CustomUICollectionViewCompositionalLayout(
+				new LayoutSnapInfo
+				{
+					SnapType = linearItemsLayout.SnapPointsType,
+					SnapAligment = linearItemsLayout.SnapPointsAlignment
+				},
+				null,
+				null,
+				sectionProvider,
+				layoutConfiguration,
+				linearItemsLayout.ItemsUpdatingScrollMode)
+			: new UICollectionViewCompositionalLayout(sectionProvider, layoutConfiguration);

Additional issues to address:

  1. Issue33308.cs HostApp — no AutomationId: The carousel view and cards have no AutomationId. The test finds elements by label text ("Card 1") which is fragile. Recommend adding AutomationId = "CardsCarousel" to the CarouselView.

  2. Android snapshot added for iOS-only issue: src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerticalCarouselMandatorySingleSnapAdvancesOneCard.png was added, but the [Issue] attribute specifies PlatformAffected.iOS and the test has no Android-specific restriction. This snapshot may be incorrect or unnecessary.

  3. PR is marked Draft: The PR is still in draft state. The author should mark it ready when the constructor bug is fixed.


@MauiBot MauiBot added s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Mar 28, 2026
@sheiksyedm sheiksyedm marked this pull request as ready for review March 30, 2026 08:26
Copilot AI review requested due to automatic review settings March 30, 2026 08:26
@sheiksyedm
Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests , maui-pr-devicetests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

@Vignesh-SF3580
Copy link
Copy Markdown
Contributor Author

🤖 AI Summary

📊 Expand Full Review2e31d74 · Fixed-33308 : CarouselView vertical snap points ignored on iOS with Microsoft.Maui.Controls v10.0.20

I have resolved the compilation errors.

Copy link
Copy Markdown
Contributor

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.

Pull request overview

Fixes iOS vertical CarouselView with MandatorySingle snapping by aligning layout selection, page progression, and loop correction to the vertical axis, and adds a regression test/sample for issue #33308.

Changes:

  • Route vertical linear CarouselView through CustomUICollectionViewCompositionalLayout to enable snap-aware behavior.
  • Compute page progression and loop correction using the active axis (Y/top for vertical).
  • Add UI test + host app repro page for issue 33308.

Reviewed changes

Copilot reviewed 3 out of 6 changed files in this pull request and generated 4 comments.

File Description
src/Controls/src/Core/Handlers/Items2/iOS/LayoutFactory2.cs Aligns snapping/page tracking/loop correction with vertical axis; routes vertical layouts through custom compositional layout.
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33308.cs Adds UI regression test coverage for the vertical mandatory single snap scenario.
src/Controls/tests/TestCases.HostApp/Issues/Issue33308.cs Adds a host app repro page creating a vertical CarouselView with MandatorySingle snap points.

@sheiksyedm
Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 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 collectionview-cv2 community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration platform/ios s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CarouselView vertical snap points ignored on iOS with Microsoft.Maui.Controls v10.0.20 (regression from v9.0.120)

6 participants