Skip to content

Fix CollectionView Header is not visible when ItemsSource is not set and an EmptyView is set in iOS, Mac platform#34989

Merged
kubaflo merged 9 commits intodotnet:inflight/currentfrom
KarthikRajaKalaimani:fix-34897
Apr 16, 2026
Merged

Fix CollectionView Header is not visible when ItemsSource is not set and an EmptyView is set in iOS, Mac platform#34989
kubaflo merged 9 commits intodotnet:inflight/currentfrom
KarthikRajaKalaimani:fix-34897

Conversation

@KarthikRajaKalaimani
Copy link
Copy Markdown
Contributor

@KarthikRajaKalaimani KarthikRajaKalaimani commented Apr 16, 2026

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:

On iOS and macCatalyst, the CollectionView Header/Footer is not visible when ItemsSource is not set (or empty) and an EmptyView is configured.

Root Cause:

iOS/macCatalyst uses UICollectionViewCompositionalLayout, which renders the Header and Footer as global boundary supplementary items configured on NSCollectionLayoutConfiguration. The core problem is that UICollectionViewCompositionalLayout simply does not render these global boundary items when the collection reports 0 sections. When ItemsSource is null or empty, the data source returns 0 sections, causing the layout engine to skip rendering the Header/Footer entirely. Android is unaffected because its layout engine handles this case differently.

Description of Change:

The fix overrides NumberOfSections in StructuredItemsViewController2.cs to return at least 1 section whenever the data source would return 0 sections but a Header or Footer is configured. This tricks the UICollectionViewCompositionalLayout into having one phantom empty section, which satisfies the layout engine and causes the Header/Footer to render correctly. A guard ensures this only applies to non-grouped collections — grouped collections use per-section supplementary items and would crash if given a phantom section that has no corresponding group data in the source.

Tested the behavior in the following platforms:

  • Android
  • Windows
  • iOS
  • Mac

Reference:

N/A

Issues Fixed:

Fixes #34897

Screenshots

Before After
Before_34897 After_34897

@github-actions
Copy link
Copy Markdown
Contributor

🚀 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 -- 34989

Or

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

@dotnet-policy-service dotnet-policy-service Bot added the community ✨ Community Contribution label Apr 16, 2026
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Hey there @@KarthikRajaKalaimani! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

@dotnet-policy-service dotnet-policy-service Bot added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Apr 16, 2026
@sheiksyedm sheiksyedm marked this pull request as ready for review April 16, 2026 06:49
Copilot AI review requested due to automatic review settings April 16, 2026 06:49
@sheiksyedm sheiksyedm added platform/ios area-controls-collectionview CollectionView, CarouselView, IndicatorView labels Apr 16, 2026
@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).

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 an iOS/MacCatalyst CollectionView rendering issue where global header/footer aren’t shown when ItemsSource is null/empty and an EmptyView is set (due to UICollectionViewCompositionalLayout not rendering boundary supplementary items when there are 0 sections).

Changes:

  • Override StructuredItemsViewController2.NumberOfSections to return a minimum of 1 section when header/footer exists and the source would otherwise report 0 sections (non-grouped only).
  • Add a HostApp repro page (Issue34897) with header/footer/empty view and AutomationIds.
  • Add an Appium UI test (Issue34897) to validate header/footer/empty view elements are present.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/Controls/src/Core/Handlers/Items2/iOS/StructuredItemsViewController2.cs Ensures at least one section is reported so compositional layout renders global header/footer for empty sources (non-grouped).
src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt Records new public override API surface for iOS.
src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt Records new public override API surface for MacCatalyst.
src/Controls/tests/TestCases.HostApp/Issues/Issue34897.cs Adds a repro page with AutomationIds for header/footer/empty view.
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34897.cs Adds an Appium test that waits for header/footer/empty view elements.

@@ -0,0 +1,23 @@
#if TEST_FAILS_ON_WINDOWS // This test fails on Windows because we no longer works with CollectionView on Windows.
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

The file-level comment is grammatically incorrect and misleading (CollectionView still exists on Windows). Please update it to accurately describe why this test is excluded on Windows (e.g., EmptyView/header/footer elements not being accessible via Automation on Windows) and ideally reference the tracking issue if there is one.

Suggested change
#if TEST_FAILS_ON_WINDOWS // This test fails on Windows because we no longer works with CollectionView on Windows.
#if TEST_FAILS_ON_WINDOWS // Excluded on Windows because the CollectionView EmptyView, header, and footer are not reliably accessible via UI Automation there.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,54 @@
using Microsoft.Maui.Controls;
using System.Collections.ObjectModel;
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

System.Collections.ObjectModel is not used in this file. Please remove the unused using to avoid IDE0005/style-analyzer noise in builds.

Suggested change
using System.Collections.ObjectModel;

Copilot uses AI. Check for mistakes.
@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Apr 16, 2026

🚦 Gate — Test Before and After Fix

👋 @KarthikRajaKalaimani — new gate results are available. Please review the latest session below.

🚦 Gate Session05ee0fb · Additional scenario added in test case and improved the fix. · 2026-04-16 12:08 UTC

Gate Result: ✅ PASSED

Platform: IOS · Base: main · Merge base: eb0b82fe

Test Without Fix (expect FAIL) With Fix (expect PASS)
🖥️ Issue34897 Issue34897 ✅ FAIL — 283s ✅ PASS — 102s
🔴 Without fix — 🖥️ Issue34897: FAIL ✅ · 283s
  Determining projects to restore...
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 647 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 669 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 7.85 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/Foldable/src/Controls.Foldable.csproj (in 18.98 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 18.99 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Xaml/Controls.Xaml.csproj (in 19.01 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/BlazorWebView/src/Maui/Microsoft.AspNetCore.Components.WebView.Maui.csproj (in 19.02 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj (in 19.03 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/Maps/src/Controls.Maps.csproj (in 19.03 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Core/maps/src/Maps.csproj (in 19.06 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 19.05 sec).
/Users/cloudtest/vss/_work/1/s/.dotnet/packs/Microsoft.iOS.Sdk.net10.0_26.0/26.0.11017/targets/Xamarin.Shared.Sdk.targets(309,3): warning : RuntimeIdentifier was set on the command line, and will override the value for RuntimeIdentifiers set in the project file. [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-ios]
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  Graphics -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Graphics/Debug/net10.0-ios26.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0-ios26.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Core/Debug/net10.0-ios26.0/Microsoft.Maui.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  Maps -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Maps/Debug/net10.0-ios26.0/Microsoft.Maui.Maps.dll
  Controls.BindingSourceGen -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  Controls.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Core/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  Microsoft.AspNetCore.Components.WebView.Maui -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Microsoft.AspNetCore.Components.WebView.Maui/Debug/net10.0-ios26.0/Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.Xaml -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Xaml.dll
  Controls.Foldable -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Foldable.dll
  Controls.Maps -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Maps.dll
  Detected signing identity:
    Code Signing Key: "" (-)
    Provisioning Profile: "" () - no entitlements
    Bundle Id: com.microsoft.maui.uitests
    App Id: com.microsoft.maui.uitests
  Controls.TestCases.HostApp -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-ios/iossimulator-arm64/Controls.TestCases.HostApp.dll
  Optimizing assemblies for size may change the behavior of the app. Be sure to test after publishing. See: https://aka.ms/dotnet-illink
  Optimizing assemblies for size. This process might take a while.

Build succeeded.

/Users/cloudtest/vss/_work/1/s/.dotnet/packs/Microsoft.iOS.Sdk.net10.0_26.0/26.0.11017/targets/Xamarin.Shared.Sdk.targets(309,3): warning : RuntimeIdentifier was set on the command line, and will override the value for RuntimeIdentifiers set in the project file. [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-ios]
    1 Warning(s)
    0 Error(s)

Time Elapsed 00:02:12.62
  Determining projects to restore...
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/tests/CustomAttributes/Controls.CustomAttributes.csproj (in 734 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 747 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 734 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/VisualTestUtils/VisualTestUtils.csproj (in 739 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 751 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.Core/UITest.Core.csproj (in 0.7 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 771 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 824 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.NUnit/UITest.NUnit.csproj (in 939 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.Appium/UITest.Appium.csproj (in 1.9 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.Analyzers/UITest.Analyzers.csproj (in 2.91 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/VisualTestUtils.MagickNet/VisualTestUtils.MagickNet.csproj (in 3.32 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.iOS.Tests/Controls.TestCases.iOS.Tests.csproj (in 3.34 sec).
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  Controls.CustomAttributes -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
  Graphics -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  Controls.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
  UITest.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
  VisualTestUtils -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
  UITest.NUnit -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
  VisualTestUtils.MagickNet -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
  UITest.Appium -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
  UITest.Analyzers -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
  Controls.TestCases.iOS.Tests -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
Test run for /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (arm64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
/Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.06]   Discovering: Controls.TestCases.iOS.Tests
[xUnit.net 00:00:00.19]   Discovered:  Controls.TestCases.iOS.Tests
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
   NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 4/16/2026 5:06:13 AM FixtureSetup for Issue34897(iOS)
>>>>> 4/16/2026 5:06:17 AM CollectionViewHeaderVisibleWithEmptyViewAndNullItemsSource Start
>>>>> 4/16/2026 5:06:33 AM CollectionViewHeaderVisibleWithEmptyViewAndNullItemsSource Stop
>>>>> 4/16/2026 5:06:33 AM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
  Failed CollectionViewHeaderVisibleWithEmptyViewAndNullItemsSource [15 s]
  Error Message:
   System.TimeoutException : Timed out waiting for element...
  Stack Trace:
     at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
   at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
   at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
   at Microsoft.Maui.TestCases.Tests.Issues.Issue34897.CollectionViewHeaderVisibleWithEmptyViewAndNullItemsSource() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34897.cs:line 19
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)

NUnit Adapter 4.5.0.0: Test execution complete

Test Run Failed.
Total tests: 1
     Failed: 1
 Total time: 1.3541 Minutes

🟢 With fix — 🖥️ Issue34897: PASS ✅ · 102s
  Determining projects to restore...
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 377 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 387 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 390 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 435 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 450 ms).
  6 of 11 projects are up-to-date for restore.
/Users/cloudtest/vss/_work/1/s/.dotnet/packs/Microsoft.iOS.Sdk.net10.0_26.0/26.0.11017/targets/Xamarin.Shared.Sdk.targets(309,3): warning : RuntimeIdentifier was set on the command line, and will override the value for RuntimeIdentifiers set in the project file. [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-ios]
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  Graphics -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Graphics/Debug/net10.0-ios26.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0-ios26.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Core/Debug/net10.0-ios26.0/Microsoft.Maui.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  Maps -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Maps/Debug/net10.0-ios26.0/Microsoft.Maui.Maps.dll
  Controls.BindingSourceGen -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  Controls.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Core/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  Microsoft.AspNetCore.Components.WebView.Maui -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Microsoft.AspNetCore.Components.WebView.Maui/Debug/net10.0-ios26.0/Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.Xaml -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Xaml.dll
  Controls.Maps -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Maps.dll
  Controls.Foldable -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Foldable.dll
  Detected signing identity:
    Code Signing Key: "" (-)
    Provisioning Profile: "" () - no entitlements
    Bundle Id: com.microsoft.maui.uitests
    App Id: com.microsoft.maui.uitests
  Controls.TestCases.HostApp -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-ios/iossimulator-arm64/Controls.TestCases.HostApp.dll
  Optimizing assemblies for size may change the behavior of the app. Be sure to test after publishing. See: https://aka.ms/dotnet-illink
  Optimizing assemblies for size. This process might take a while.

Build succeeded.

/Users/cloudtest/vss/_work/1/s/.dotnet/packs/Microsoft.iOS.Sdk.net10.0_26.0/26.0.11017/targets/Xamarin.Shared.Sdk.targets(309,3): warning : RuntimeIdentifier was set on the command line, and will override the value for RuntimeIdentifiers set in the project file. [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-ios]
    1 Warning(s)
    0 Error(s)

Time Elapsed 00:00:55.77
  Determining projects to restore...
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 380 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 397 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 355 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 412 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 434 ms).
  8 of 13 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  Graphics -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  Controls.CustomAttributes -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
  Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13856150
  Controls.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
  UITest.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
  VisualTestUtils -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
  VisualTestUtils.MagickNet -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
  UITest.Appium -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
  UITest.NUnit -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
  UITest.Analyzers -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
  Controls.TestCases.iOS.Tests -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
Test run for /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (arm64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
/Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.05]   Discovering: Controls.TestCases.iOS.Tests
[xUnit.net 00:00:00.15]   Discovered:  Controls.TestCases.iOS.Tests
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
   NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 4/16/2026 5:08:13 AM FixtureSetup for Issue34897(iOS)
>>>>> 4/16/2026 5:08:17 AM CollectionViewHeaderVisibleWithEmptyViewAndNullItemsSource Start
>>>>> 4/16/2026 5:08:17 AM CollectionViewHeaderVisibleWithEmptyViewAndNullItemsSource Stop
  Passed CollectionViewHeaderVisibleWithEmptyViewAndNullItemsSource [219 ms]
NUnit Adapter 4.5.0.0: Test execution complete

Test Run Successful.
Total tests: 1
     Passed: 1
 Total time: 17.6740 Seconds

📁 Fix files reverted (3 files)
  • src/Controls/src/Core/Handlers/Items2/iOS/StructuredItemsViewController2.cs
  • src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt
  • src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt

@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Apr 16, 2026

🤖 AI Summary

👋 @KarthikRajaKalaimani — new AI review results are available. Please review the latest session below.

📊 Review Session05ee0fb · Additional scenario added in test case and improved the fix. · 2026-04-16 14:14 UTC
🔍 Pre-Flight — Context & Validation

Issue: #34897 - CollectionView Header is not visible when ItemsSource is not set and EmptyView is set in iOS, Mac platform
PR: #34989 - Fix CollectionView Header is not visible when ItemsSource is not set and an EmptyView is set in iOS, Mac platform
Platforms Affected: iOS, macCatalyst
Files Changed: 1 implementation, 2 test, 2 PublicAPI

Key Findings

  • UICollectionViewCompositionalLayout doesn't render global boundary supplementary items (header/footer) when the data source reports 0 sections
  • Fix overrides NumberOfSections in StructuredItemsViewController2 to return 1 (phantom section) when ItemsSource is empty but a header/footer is configured
  • Guard prevents applying the fix to grouped collections (would crash)
  • EmptySource.GroupCount == 0 and ItemCountInGroup(0) == 0 — phantom section has 0 items, EmptyView continues to show
  • Prior copilot review flagged: (1) misleading #if TEST_FAILS_ON_WINDOWS comment, (2) unused using System.Collections.ObjectModel;

Code Review Summary

Verdict: LGTM
Confidence: medium
Errors: 0 | Warnings: 1 | Suggestions: 1

Key code review findings:

  • ⚠️ iOS UI test CI tier broadly failing (pre-existing infra issue, not PR-related) — no automated CI confirmation for new test
  • 💡 Test coverage thin: doesn't cover grouped+header/footer guard or non-null empty ItemsSource paths

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #34989 Override NumberOfSections to return 1 when count==0 and header/footer configured (non-grouped only) ✅ PASSED (Gate) StructuredItemsViewController2.cs, PublicAPI ×2 Original PR

🔬 Code Review — Deep Analysis

Code Review — PR #34989

Independent Assessment

What this changes: Overrides NumberOfSections in StructuredItemsViewController2<TItemsView> to return at least 1 when the data source would return 0 but a Header, Footer, HeaderTemplate, or FooterTemplate is configured. A guard prevents this from applying to grouped collections. The PublicAPI.Unshipped.txt files for iOS and macCatalyst are updated, and a new UI test reproduces the reported scenario (null ItemsSource + Header + Footer + EmptyView).

Inferred motivation: UICollectionViewCompositionalLayout silently skips rendering global boundary supplementary items (configured on NSCollectionLayoutConfiguration) when the data source reports 0 sections. This is an iOS layout engine quirk with no workaround other than reporting a phantom non-zero section count.


Reconciliation with PR Narrative

Author claims: iOS/macCatalyst UICollectionViewCompositionalLayout doesn't render Header/Footer when ItemsSource is null/empty because 0 sections suppresses global boundary items. The fix returns 1 phantom section to satisfy the layout engine; a guard prevents crashes on grouped collections.

Agreement/disagreement: Code exactly matches the stated approach. Root cause analysis is accurate and matches the iOS layout engine's documented behavior.


Findings

💡 Suggestion — Test coverage is thin for adjacent CollectionView scenarios

Per §21 of the review rules, CollectionView Header/Footer changes "must be tested across all four: empty collection, single item, many items, and with grouping." The new test covers only one of these: null ItemsSource with Header + Footer + EmptyView. Two important regressions are unguarded by any new test:

  1. Grouped collection with Header + Footer + empty source: The code correctly guards with !(ItemsView is GroupableItemsView { IsGrouped: true }), but there's no test to verify the guard holds — a future refactor could inadvertently remove it.
  2. Non-null but empty ItemsSource (new ObservableCollection<T>()): In this case ObservableItemsSource.GroupCount == 1, so count == 1 from the base and the override is never reached. Header/Footer would render without the fix. A test confirming this path isn't regressed would be valuable.

⚠️ Warning — iOS UITests failing broadly in CI

maui-pr-uitests shows failures across every iOS UI test category (Border, Button, Entry, Label, etc.) — approximately 17 categories all failing. This is clearly a pre-existing infrastructure issue unrelated to this PR (the main maui-pr build, all device tests, and integration tests all pass). However, there is no CollectionView-specific UI test run visible in CI results, so it's not possible to confirm from CI output alone that the new Issue34897 test passed on the PR itself.


Devil's Advocate

Challenge on the phantom section approach: Could returning section count=1 cause GetItemsCount(collectionView, 0) to be called on the EmptySource? Yes, it will — but EmptySource.ItemCountInGroup(0) == 0, so the phantom section correctly has zero items. CheckForEmptySource() is called inside GetItemsCount, sets _isEmpty = true, and shows the EmptyView. The empty view and the header/footer coexist correctly.

Challenge on the grouped guard: !(ItemsView is GroupableItemsView { IsGrouped: true }) — if ItemsView is null, this evaluates to true, which could in theory bypass the guard. However, if ItemsView is null, all the ItemsView?.Header is not null checks will also be null-propagated to false, so the combined && condition fails and we return the unmodified count. Safe.

Challenge on null ItemsSource: The condition ItemsSource is not null filters out the transient null state that exists briefly before ViewDidLoad sets ItemsSource = CreateItemsViewSource(). At that point the base already returns 0 anyway, so this guard is correct belt-and-suspenders rather than redundant.

Am I sure about the scoping? The fix only fires when EmptySource is active (GroupCount == 0). A non-null but empty IEnumerable produces an ObservableItemsSource with GroupCount == 1, so count == 1 from the base and this override never fires. The scope of the fix is narrower than it looks — it only helps the specific case of ItemsSource = null (or explicitly disposed/cleared). This matches the issue exactly.


Verdict: LGTM

Confidence: medium

Summary: The fix is correct, well-guarded, and the approach (returning a phantom section count) is the canonical workaround for this UICollectionViewCompositionalLayout limitation. The logic handles null ItemsSource, grouped collections, and the disposal path correctly. The test covers the reported scenario. Confidence is medium rather than high only because CollectionView is the highest-regression component in the repo, the test coverage doesn't exercise the adjacent scenarios recommended by the review rules (particularly grouped collection with header/footer), and the iOS UI test CI tier is entirely red so there's no automated confirmation from CI that the new test passed.


🔧 Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 claude-opus-4.6 Override to return new class (GroupCount=1, ItemCount=0) when header/footer configured (non-grouped) PASS StructuredItemsViewController2.cs, PublicAPI 2 Data-source-level fix; keeps ViewController/source in sync
2 claude-sonnet-4.6 Post-process after ViewDidLoad/UpdateItemsSource; replace EmptySource with existing (GroupCount=1); no new class PASS StructuredItemsViewController2.cs, PublicAPI 2 Uses existing ListSource; data source fully consistent
3 gpt-5.3-codex Render header/footer as standalone UIView subviews directly on UICollectionView when source has 0 groups; bypasses UIKit supplementary views PASS StructuredItemsViewController2.cs Most invasive (~114 lines); no section count change
4 gpt-5.4 Synthetic layout attributes in FAIL LayoutFactory2.cs Layout-only fix UIKit requires data source backing insufficient LayoutFactory2
PR PR #34989 Override to return 1 (phantom section) when count==0 and header/footer configured (non-grouped only) PASSED (Gate) StructuredItemsViewController2.cs, PublicAPI 2 Simplest/most surgical (20 lines)

Cross-Pollination

Model Round New Ideas? Details
claude-opus-4.6 2 No Solution space covered; two viable fix classes: phantom-section (PR/1/2) or bypass UIKit supplementary views (3)
gpt-5.3-codex 2 Yes Inject hidden sentinel item (1 zero-sized more complex variant of phantom-section class; not run

Exhausted: Yes
Selected Fix: PR's simplest and most surgical (single override, 20 lines). Phantom-section approach confirmed canonical workaround for UICollectionViewCompositionalLayout limitation. Three independent alternatives that passed were more complex or more invasive.fix


📋 Report — Final Recommendation

✅ Final Recommendation: APPROVE

Phase Status

Phase Status Notes
Pre-Flight ✅ COMPLETE Issue #34897 — iOS/macCatalyst CollectionView header/footer invisible when ItemsSource=null + EmptyView
Code Review LGTM (medium) 0 errors, 1 warning (pre-existing CI infra issue), 1 suggestion (thin test coverage)
Gate ✅ PASSED ios — test fails without fix, passes with fix
Try-Fix ✅ COMPLETE 4 attempts; 3 passing (1–3), 1 failing (4); PR's fix selected as simplest
Report ✅ COMPLETE

Code Review Impact on Try-Fix

No ❌ Errors were found in code review, so try-fix models were not asked to address any mandatory code changes. The warning about thin test coverage and the grouped-collection failure-mode probe guided models to focus on the grouping guard. Attempt 4 (layout-only approach) empirically confirmed the failure mode identified by the devil's advocate analysis: UIKit requires data source backing, not just layout attributes.

Summary

PR #34989 fixes a confirmed iOS/macCatalyst bug where CollectionView global boundary supplementary items (Header/Footer) fail to render when ItemsSource is null and an EmptyView is configured. The fix is correct, well-guarded, and the simplest viable approach. The gate confirmed the bug and fix. Three independent alternative approaches were explored and all passed, validating the fix class. The PR's own approach was the simplest of all passing candidates.

Two minor pre-existing code review findings (unused using System.Collections.ObjectModel; and a misleading #if TEST_FAILS_ON_WINDOWS comment) were flagged by the prior Copilot code reviewer and are unresolved. These are low-severity and do not affect correctness.

Root Cause

UICollectionViewCompositionalLayout does not render global boundary supplementary items (configured via NSCollectionLayoutConfiguration) when the data source reports 0 sections. When ItemsSource is null on the CollectionView, ItemsSourceFactory.Create(null) returns an EmptySource with GroupCount == 0, causing the base NumberOfSections to return 0, which silently suppresses header/footer rendering. Android is unaffected because its layout engine handles the empty case differently.

Fix Quality

The fix is minimal (20 lines), correctly scoped, and well-commented. Key correctness properties:

  • count == 0 && ItemsSource is not null: filters out the transient pre-ViewDidLoad null state
  • !(ItemsView is GroupableItemsView { IsGrouped: true }): prevents crash on grouped collections whose per-section supplementary items would reference non-existent group data
  • ItemsView?.Header/Footer != null condition: only activates when header/footer actually configured
  • Phantom section returns 0 items (EmptySource.ItemCountInGroup(0) == 0), so EmptyView continues to display correctly
  • PublicAPI.Unshipped.txt entries correctly added for iOS and macCatalyst

The PR's NumberOfSections override is the simplest and most surgical of the tested approaches. Alternatives that passed (CreateItemsViewSource override, post-ViewDidLoad source swap) were functionally correct but more complex.

Selected Fix: PR — Gate passed, code review LGTM, PR's fix is simplest among passing alternatives.


@MauiBot MauiBot added s/agent-approved AI agent recommends approval - PR fix is correct and optimal s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Apr 16, 2026
@kubaflo kubaflo changed the base branch from main to inflight/current April 16, 2026 18:52
@kubaflo kubaflo merged commit 7671b66 into dotnet:inflight/current Apr 16, 2026
144 of 169 checks passed
devanathan-vaithiyanathan pushed a commit to Tamilarasan-Paranthaman/maui that referenced this pull request Apr 21, 2026
…and an EmptyView is set in iOS, Mac platform (dotnet#34989)

<!-- Please let the below note in for people that find this PR -->
> [!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](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

### Issue Details:

On iOS and macCatalyst, the CollectionView Header/Footer is not visible
when ItemsSource is not set (or empty) and an EmptyView is configured.
       
### Root Cause:

iOS/macCatalyst uses UICollectionViewCompositionalLayout, which renders
the Header and Footer as global boundary supplementary items configured
on NSCollectionLayoutConfiguration. The core problem is that
UICollectionViewCompositionalLayout simply does not render these global
boundary items when the collection reports 0 sections. When ItemsSource
is null or empty, the data source returns 0 sections, causing the layout
engine to skip rendering the Header/Footer entirely. Android is
unaffected because its layout engine handles this case differently.

### Description of Change:

The fix overrides NumberOfSections in StructuredItemsViewController2.cs
to return at least 1 section whenever the data source would return 0
sections but a Header or Footer is configured. This tricks the
UICollectionViewCompositionalLayout into having one phantom empty
section, which satisfies the layout engine and causes the Header/Footer
to render correctly. A guard ensures this only applies to non-grouped
collections — grouped collections use per-section supplementary items
and would crash if given a phantom section that has no corresponding
group data in the source.

**Tested the behavior in the following platforms:**

- [ ] Android
- [ ] Windows
- [x] iOS
- [x] Mac

### Reference:

N/A

### Issues Fixed:

Fixes  dotnet#34897          

### Screenshots
| Before  | After  |
|---------|--------|
| <img width="300" height="600" alt="Before_34897"
src="https://github.com/user-attachments/assets/9e883564-d5cd-4f89-b238-f5b7c8bf434c"
/> | <img width="300" height="600" alt="After_34897"
src="https://github.com/user-attachments/assets/eb045764-1bee-4f52-b97f-65e39f0cdfc7"
/> |
PureWeen pushed a commit that referenced this pull request Apr 22, 2026
…and an EmptyView is set in iOS, Mac platform (#34989)

<!-- Please let the below note in for people that find this PR -->
> [!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](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

### Issue Details:

On iOS and macCatalyst, the CollectionView Header/Footer is not visible
when ItemsSource is not set (or empty) and an EmptyView is configured.
       
### Root Cause:

iOS/macCatalyst uses UICollectionViewCompositionalLayout, which renders
the Header and Footer as global boundary supplementary items configured
on NSCollectionLayoutConfiguration. The core problem is that
UICollectionViewCompositionalLayout simply does not render these global
boundary items when the collection reports 0 sections. When ItemsSource
is null or empty, the data source returns 0 sections, causing the layout
engine to skip rendering the Header/Footer entirely. Android is
unaffected because its layout engine handles this case differently.

### Description of Change:

The fix overrides NumberOfSections in StructuredItemsViewController2.cs
to return at least 1 section whenever the data source would return 0
sections but a Header or Footer is configured. This tricks the
UICollectionViewCompositionalLayout into having one phantom empty
section, which satisfies the layout engine and causes the Header/Footer
to render correctly. A guard ensures this only applies to non-grouped
collections — grouped collections use per-section supplementary items
and would crash if given a phantom section that has no corresponding
group data in the source.

**Tested the behavior in the following platforms:**

- [ ] Android
- [ ] Windows
- [x] iOS
- [x] Mac

### Reference:

N/A

### Issues Fixed:

Fixes  #34897          

### Screenshots
| Before  | After  |
|---------|--------|
| <img width="300" height="600" alt="Before_34897"
src="https://github.com/user-attachments/assets/9e883564-d5cd-4f89-b238-f5b7c8bf434c"
/> | <img width="300" height="600" alt="After_34897"
src="https://github.com/user-attachments/assets/eb045764-1bee-4f52-b97f-65e39f0cdfc7"
/> |
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 partner/syncfusion Issues / PR's with Syncfusion collaboration platform/ios s/agent-approved AI agent recommends approval - PR fix is correct and optimal s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates 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.

CollectionView Header is not visible when ItemsSource is not set and EmptyView is set in iOS, Mac platform

6 participants