[iOS/MacCatalyst] Fix IndicatorView not updating when IndicatorSize is changed to default value#35215
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35215Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35215" |
|
Hey there @@Shalini-Ashokan! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 2 findings
See inline comments for details.
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 4 findings
See inline comments for details.
kubaflo
left a comment
There was a problem hiding this comment.
It looks good! Is this PR ready?
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 3 findings
See inline comments for details.
There was a problem hiding this comment.
Pull request overview
Fixes an iOS/MacCatalyst-specific IndicatorView.IndicatorSize regression where setting the size back to the default value at runtime failed to visually update the indicators. The change adjusts the iOS MauiPageControl sizing logic to skip updates only when the size is truly unchanged, and adds a HostApp repro + UI tests to validate the behavior.
Changes:
- Initialize the iOS
MauiPageControl“last applied” indicator size to the default and skip size updates based on the last applied value (not a hardcoded default check). - Add a new HostApp issue page (
Issue35214) that reproduces resettingIndicatorSizeback to default at runtime. - Add Appium screenshot-based UI tests for before/after resetting the indicator size.
Reviewed changes
Copilot reviewed 3 out of 7 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/Core/src/Platform/iOS/MauiPageControl.cs | Fixes iOS/MacCatalyst indicator size update logic so resetting to the default value triggers a visual update when needed. |
| src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35214.cs | Adds UI tests validating indicator visuals before and after resetting IndicatorSize to default. |
| src/Controls/tests/TestCases.HostApp/Issues/Issue35214.cs | Adds a HostApp repro page with an IndicatorView and a button that resets IndicatorSize to the default value. |
🤖 AI Summary
📊 Review Session —
|
| Test | Without Fix (expect FAIL) | With Fix (expect PASS) |
|---|---|---|
🖥️ Issue35214 Issue35214 |
✅ FAIL — 233s | ✅ PASS — 86s |
🔴 Without fix — 🖥️ Issue35214: FAIL ✅ · 233s
Determining projects to restore...
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 570 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 652 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 5 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 5.1 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/Foldable/src/Controls.Foldable.csproj (in 5.78 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Xaml/Controls.Xaml.csproj (in 5.78 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/BlazorWebView/src/Maui/Microsoft.AspNetCore.Components.WebView.Maui.csproj (in 5.78 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Core/maps/src/Maps.csproj (in 5.79 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj (in 5.79 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 5.79 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/Maps/src/Controls.Maps.csproj (in 5.8 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.14016115
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.14016115
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.14016115
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.14016115
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.14016115
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.14016115
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14016115
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14016115
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14016115
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:01:58.25
Determining projects to restore...
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/tests/CustomAttributes/Controls.CustomAttributes.csproj (in 713 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 713 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/VisualTestUtils/VisualTestUtils.csproj (in 711 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 716 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.Core/UITest.Core.csproj (in 0.6 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 774 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 803 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 408 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.NUnit/UITest.NUnit.csproj (in 1.14 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.Appium/UITest.Appium.csproj (in 1.96 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.Analyzers/UITest.Analyzers.csproj (in 2.9 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/VisualTestUtils.MagickNet/VisualTestUtils.MagickNet.csproj (in 2.82 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.iOS.Tests/Controls.TestCases.iOS.Tests.csproj (in 3.59 sec).
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14016115
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.14016115
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.14016115
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.14016115
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.NUnit -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.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.05] Discovering: Controls.TestCases.iOS.Tests
[xUnit.net 00:00:00.14] 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 2 of 2 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 5/5/2026 3:23:07 AM FixtureSetup for Issue35214(iOS)
>>>>> 5/5/2026 3:23:11 AM VerifyIndicatorSizeBeforeReset Start
>>>>> 5/5/2026 3:23:12 AM VerifyIndicatorSizeBeforeReset Stop
>>>>> 5/5/2026 3:23:12 AM VerifyIndicatorSizeAfterReset Start
Passed VerifyIndicatorSizeBeforeReset [647 ms]
>>>>> 5/5/2026 3:23:15 AM VerifyIndicatorSizeAfterReset Stop
>>>>> 5/5/2026 3:23:15 AM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
Failed VerifyIndicatorSizeAfterReset [3 s]
Error Message:
VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: IndicatorSizeAfterReset.png (2.48% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
Stack Trace:
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.Issues.Issue35214.VerifyIndicatorSizeAfterReset() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35214.cs:line 29
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
Total tests: 2
Passed: 1
Failed: 1
Test Run Failed.
Total time: 1.1836 Minutes
🟢 With fix — 🖥️ Issue35214: PASS ✅ · 86s
Determining projects to restore...
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 321 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 339 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 304 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 376 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 393 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.14016115
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.14016115
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.14016115
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.14016115
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.14016115
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.14016115
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14016115
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14016115
Controls.Maps -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Maps.dll
Controls.Xaml -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Xaml.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14016115
Controls.Foldable -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Foldable.dll
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
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:43.86
Determining projects to restore...
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 329 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 398 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 398 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 365 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 435 ms).
8 of 13 projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.70-ci+azdo.14016115
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.14016115
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.14016115
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.14016115
Controls.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
VisualTestUtils -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
UITest.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.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 2 of 2 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 5/5/2026 3:24:37 AM FixtureSetup for Issue35214(iOS)
>>>>> 5/5/2026 3:24:40 AM VerifyIndicatorSizeBeforeReset Start
>>>>> 5/5/2026 3:24:41 AM VerifyIndicatorSizeBeforeReset Stop
>>>>> 5/5/2026 3:24:41 AM VerifyIndicatorSizeAfterReset Start
>>>>> 5/5/2026 3:24:42 AM VerifyIndicatorSizeAfterReset Stop
Passed VerifyIndicatorSizeBeforeReset [285 ms]
Passed VerifyIndicatorSizeAfterReset [860 ms]
NUnit Adapter 4.5.0.0: Test execution complete
Test Run Successful.
Total tests: 2
Passed: 2
Total time: 18.0794 Seconds
📁 Fix files reverted (2 files)
eng/pipelines/ci-copilot.ymlsrc/Core/src/Platform/iOS/MauiPageControl.cs
🧪 UI Tests — Category Detection
Detected UI test categories: CarouselView,IndicatorView,ViewBaseTests
🔍 Regression Cross-Reference
🔍 Regression Cross-Reference
🟢 No regression risks detected. No labeled bug-fix PRs in the last 6 months touched the modified files.
🔍 Pre-Flight — Context & Validation
Issue: #35214 - [iOS/MacCatalyst] IndicatorView does not update when IndicatorSize is dynamically changed to the default value
PR: #35215 - [iOS/MacCatalyst] Fix IndicatorView not updating when IndicatorSize is changed to default value
Platforms Affected: iOS, MacCatalyst
Files Changed: 1 implementation, 2 test (+ 4 snapshot PNG files)
Key Findings
- Root cause:
UpdateIndicatorSize()inMauiPageControl.cshad a hard-coded early-return whenIndicatorSize == DefaultIndicatorSize(6), preventing the transform from being reset when navigating back to the default size - Secondary issue:
_lastAppliedIndicatorSizewas initialized to-1(invalid sentinel) instead ofDefaultIndicatorSize(6), causing a logic gap - Fix replaces the
DefaultIndicatorSizeconstant comparison with_lastAppliedIndicatorSizetracking, so the check is "is this size already applied?" rather than "is this the default?" - Initializing
_lastAppliedIndicatorSize = DefaultIndicatorSizecorrectly represents initial state (no scaling applied) - The
CGAffineTransform.MakeScale(1.0, 1.0)path now executes when IndicatorSize returns to 6 — this is the identity transform, which correctly resets scaling - Tests use
VerifyScreenshot(visual snapshot comparison) withretryTimeout: TimeSpan.FromSeconds(2)
Code Review Summary
Verdict: LGTM
Confidence: high
Errors: 0 | Warnings: 1 | Suggestions: 2
Key code review findings:
⚠️ MauiPageControl.cs:68—IndicatorSize == 0guard leaves stale transform after explicit 0-reset (same bug class, narrower scenario — setting size to 0 after non-default leaves indicators visually stuck)- 💡
MauiPageControl.cs:16— No comment explaining why-1sentinel was replaced; brief note would help future maintainers verify PR [iOS] Fix indicator dots not rendering when using indicator size with shadow #31463 compatibility - 💡
Issue35214.cs:25—[Order(2)]depends on[Order(1)]state; a comment documenting the intentional state dependency would help test maintainers
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #35215 | Initialize _lastAppliedIndicatorSize = DefaultIndicatorSize; replace DefaultIndicatorSize comparison with _lastAppliedIndicatorSize in early-return |
✅ PASSED (Gate) | MauiPageControl.cs |
Original PR |
🔬 Code Review — Deep Analysis
Code Review — PR #35215
Independent Assessment
What this changes: Two lines changed in MauiPageControl.cs (iOS/MacCatalyst):
- The tracking field
_lastAppliedIndicatorSizeis initialized toDefaultIndicatorSize(6) instead of-1. - The early-return guard in
UpdateIndicatorSize()compares against_lastAppliedIndicatorSizeinstead of the hard-codedDefaultIndicatorSize.
Inferred motivation: When IndicatorSize is set dynamically at runtime — first to a non-default value (e.g., 20), then back to the default (6) — the guard IndicatorSize == DefaultIndicatorSize fires on the second update, skipping the foreach transform loop entirely. The scale transform is never reset to 1.0×, so the indicators remain visually stuck at the scaled-up size. The -1 sentinel initialization was an earlier patch (PR #31463, for shadow rendering) that ensured the first layout pass always applied the transform; changing it to DefaultIndicatorSize is equally safe for that path (see Reconciliation below).
Is the approach sound? Yes. Tracking "last applied" state and short-circuiting on equality (rather than hard-coding a sentinel value that coincides with a valid user-set value) is the canonical pattern for deferred-apply properties in MAUI platform views. The fix is surgical and minimal.
What problems do I see? One residual logic gap (see Finding 1): the IndicatorSize == 0 guard, which is unchanged, can produce a similar stuck-transform scenario if a consumer resets IndicatorSize to 0 after it was set to a non-default value. This is a narrower edge case (0 is not a meaningful size for an indicator) but is structurally identical to the original bug.
Reconciliation with PR Narrative
Author claims: The root cause was a hard-coded default-value check that skips the transform update even when indicators are currently scaled, and the tracking field was incorrectly seeded at -1.
Agreement/disagreement: Fully agrees with my independent assessment. The description of both the bug and the fix is accurate. The PR also calls out that _lastAppliedIndicatorSize = -1 was the incorrect initial state — correct; -1 was fine as a "force-update-on-first-pass" sentinel but masked the fact that the default size is 6, not -1.
The author validated on Android, Windows, iOS, and Mac — though the code change is iOS/MacCatalyst-only. Android and Windows use separate MauiPageControl implementations that do not share this logic.
Findings
⚠️ Warning — IndicatorSize == 0 guard leaves stale transform after explicit 0-reset
src/Core/src/Platform/iOS/MauiPageControl.cs line 68
The IndicatorSize == 0 early-return was carried forward unchanged. If a consumer sets IndicatorSize = 20 (transform applied, _lastAppliedIndicatorSize = 20) and then sets IndicatorSize = 0, the guard fires and no identity reset is applied. Indicators remain visually at 20/6× scale — structurally the same class of bug as the one being fixed.
0 is not a documented valid size, but there are two code paths where IndicatorSize can be 0: (a) the MauiPageControl.IndicatorSize property default before MapIndicatorSize runs, and (b) an explicit consumer reset. Path (a) is benign (no previous transform was applied). Path (b) is the residual risk.
A complete fix would add an explicit identity-reset branch:
if (IndicatorSize == 0)
{
if (_lastAppliedIndicatorSize != DefaultIndicatorSize)
{
var identity = CGAffineTransform.MakeIdentity();
foreach (var view in Subviews)
view.Transform = identity;
_lastAppliedIndicatorSize = DefaultIndicatorSize;
}
return;
}This is lower-priority than the reported bug (setting size to 0 is unusual and undocumented) but is worth addressing in a follow-up.
💡 Suggestion — Regression note for PR #31463 initialization change
src/Core/src/Platform/iOS/MauiPageControl.cs line 16
The -1 sentinel was introduced in PR #31463 to guarantee the scale transform is applied on the first layout pass (for the shadow-plus-indicator scenario). Reviewers auditing for regressions should note: Math.Abs(20 - DefaultIndicatorSize(6)) = 14 exceeds IndicatorSizeTolerance, so the first-pass behaviour is identical. This is safe, but a brief inline comment documenting why DefaultIndicatorSize is the correct initial value (not -1) would help future maintainers.
💡 Suggestion — Document ordered-test state dependency
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35214.cs line 25
VerifyIndicatorSizeAfterReset [Order(2)] depends on Order(1) having already run (page loaded with IndicatorSize = 20). The _IssuesUITest fixture resets state at fixture level, not per-test, so sequential runs are correct. However, if Order(2) is run in isolation (targeted test run, shard split), the tap fires correctly but no baseline screenshot from Order(1) exists, which makes the snapshot-comparison result harder to interpret. A comment documenting the intentional state dependency would help future test maintainers.
Devil's Advocate
- Am I sure about the residual
IndicatorSize == 0concern? Yes — the transform state machine (_lastAppliedIndicatorSize) is only updated when the transform loop runs. If the== 0guard fires after a non-default size was applied, the field stays dirty. The guard was originally written when the field was always -1 (first run) or a previously applied non-zero value, so the only "0" case was the uninitialized property. With the new initialization, the guard is still correct for the uninitialized case but leaves the stale-transform path open. That said,IndicatorSize = 0is not a realistic user scenario — this is a code quality observation, not a blocker. - Am I leaning too hard on convention? No — the
_lastApplied*pattern is used in other MAUI handlers (e.g.,MaterialActivityIndicator) and is the right approach here. - Platform blind spots? The fix is pure iOS/MacCatalyst UIKit (
CGAffineTransform). The MacCatalyst transform behaviour is the same as iOS forUIPageControlsubviews (confirmed: the test snapshot directories include bothiosandios-26). No Windows/Android path is affected. - CI? All CI checks are passing or pending infra (not code-caused). No red checks linked to the PR's changes.
CI Status
All build, unit test, and integration test checks are passing. Two checks (maui-pr, Build Analysis) are pending — these appear to be infra-level aggregate checks, not code failures. No CI failures are caused by this PR.
Verdict: LGTM
Confidence: high
Summary: The two-line fix is logically correct and directly resolves the reported iOS/MacCatalyst regression where resetting IndicatorSize to the default value left a stale scale transform. The approach is idiomatic for MAUI handler patterns. The one IndicatorSize == 0 stale-transform path) is a narrower variant of the same class of bug and warrants a follow-up issue, but is not a blocker for this fix. CI is clean. Tests use the correct screenshot-comparison pattern with appropriate retryTimeout.
🔧 Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix (claude-opus-4.6) | Explicit MakeIdentity() for default — remove DefaultIndicatorSize from guard, apply identity transform explicitly | ✅ PASS | 1 file | No init change; UIKit-idiomatic identity reset |
| 2 | try-fix (claude-sonnet-4.6) | Nullable double? _lastAppliedIndicatorSize — null=no-transform state, eliminates magic sentinel |
✅ PASS | 1 file | Clean state semantics; more complex |
| 3 | try-fix (gpt-5.3-codex) | Remove DefaultIndicatorSize guard + init to DefaultIndicatorSize | ✅ PASS | 1 file | Functionally similar to PR; simpler guard |
| 4 | try-fix (gpt-5.5) | Normalize 0→DefaultIndicatorSize (effectiveSize), unified single code path — fixes both reported bug AND IndicatorSize=0 stale transform | ✅ PASS | 1 file | Most robust; same approach as expert reviewer |
| PR | PR #35215 | _lastAppliedIndicatorSize = DefaultIndicatorSize init + compare against last applied (not constant) |
✅ PASSED (Gate) | 1 file (+tests) | Minimal 2-line fix |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| claude-opus-4.6 | 2 | Yes | Use UIPageControl.PreferredIndicatorImage (iOS 14+) — eliminate transform tracking entirely. Out of scope for targeted fix. |
| claude-sonnet-4.6 | 2 | Yes | Use Subviews[0].Transform.A as source of truth instead of tracking field. Interesting but higher risk. |
| gpt-5.3-codex | 2 | Yes | Invalidate _lastAppliedIndicatorSize in UpdateIndicatorCount() to handle subview regeneration. Addresses a related but different issue. |
| gpt-5.5 | 2 | Yes | Proactively invalidate before UpdatePages(). Same as gpt-5.3-codex idea. |
| All models | 3 | N/A | No Round 3 needed — all new ideas are larger architectural changes or address different issues; all 4 required candidates already passing |
Exhausted: Yes (4 mandatory attempts all passing; cross-pollination ideas all out of scope for this targeted fix)
Selected Fix: PR's fix — Correct, minimal, idiomatic. Code review LGTM (high confidence). Gate passed. try-fix exploration confirmed the fix direction; Attempt 4 found a slightly more robust variant (effectiveSize normalization) but the PR's fix is simpler and sufficient for the reported bug.
📋 Report — Final Recommendation
✅ Final Recommendation: APPROVE
Phase Status
| Phase | Status | Notes |
|---|---|---|
| Pre-Flight | ✅ COMPLETE | Issue #35214, iOS/MacCatalyst, 1 impl file + 2 test files + 4 snapshots |
| Code Review | LGTM (high) | 0 errors, 1 warning, 2 suggestions |
| Gate | ✅ PASSED | iOS — tests fail without fix, pass with fix |
| Try-Fix | ✅ COMPLETE | 4 attempts, 4 passing |
| Report | ✅ COMPLETE |
Code Review Impact on Try-Fix
The one IndicatorSize == 0 stale-transform residual) influenced two independent models: gpt-5.5 (Attempt 4) and the maui-expert-reviewer both independently proposed normalizing IndicatorSize == 0 → DefaultIndicatorSize as a unified fix. This convergence validates the expert reviewer's inline finding. No ❌ Errors were found, so the hard gate was not triggered and the PR is approvable as-is.
Summary
PR #35215 delivers a correct, minimal two-line fix for the iOS/MacCatalyst IndicatorView sizing regression. The code review returned LGTM with high confidence, the gate confirmed tests fail without the fix and pass with it, and all four try-fix candidates independently passed — confirming the fix direction is sound. The PR's fix is the simplest correct solution and should be approved.
Root Cause
MauiPageControl.UpdateIndicatorSize() had a hard-coded early return when IndicatorSize == DefaultIndicatorSize (6), which prevented the scale transform from being reset when transitioning from a non-default size back to the default. Additionally, _lastAppliedIndicatorSize was initialized to -1 (a sentinel with no semantic meaning), which masked the fact that the initial state should be "default size already applied". The fix replaces both: the guard now compares against the tracked last-applied value, and the field is initialized to DefaultIndicatorSize to correctly represent the initial no-transform state.
Fix Quality
The PR's fix is well-targeted and idiomatic. The two changes are logically coupled and together eliminate the bug completely. Try-fix exploration found three other passing approaches: an explicit MakeIdentity() path, a nullable tracking field, and a simple guard removal — all converging on the same root-cause diagnosis. The expert reviewer identified one residual IndicatorSize = 0 after non-default leaves stale transform) but correctly classified it as low-priority and not a blocker. Tests use appropriate VerifyScreenshot with retryTimeout for visual validation.
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 3 findings
See inline comments for details.
| public void UpdateIndicatorSize() | ||
| { | ||
| if (IndicatorSize == 0 || IndicatorSize == DefaultIndicatorSize) | ||
| if (IndicatorSize == 0 || IndicatorSize == _lastAppliedIndicatorSize) |
There was a problem hiding this comment.
[moderate] Logic and Correctness - The IndicatorSize == 0 early-return guard silently leaves a stale scale transform when IndicatorSize is set to 0 after a non-default value. Concrete scenario: user sets IndicatorSize = 20 (scale 20/6 applied, _lastAppliedIndicatorSize = 20), then sets IndicatorSize = 0 - the old guard fires, the foreach is skipped, indicators remain visually stuck at 20/6 scale. Fixed by treating IndicatorSize == 0 as DefaultIndicatorSize via targetSize = IndicatorSize == 0 ? DefaultIndicatorSize : IndicatorSize, so Math.Abs(6 - 20) = 14 >= tolerance correctly triggers an identity-scale reset. Also removed the redundant exact-equality check since the tolerance check already subsumes it.
| WeakReference<IIndicatorView>? _indicatorView; | ||
| bool _updatingPosition; | ||
| double _lastAppliedIndicatorSize = -1; | ||
| double _lastAppliedIndicatorSize = DefaultIndicatorSize; |
There was a problem hiding this comment.
[minor] Regression Prevention - The original _lastAppliedIndicatorSize = -1 sentinel (introduced by PR #31463 to guarantee the scale transform fires on first layout pass alongside shadows) was changed to DefaultIndicatorSize. Added comment explaining the choice and explicitly verifying PR #31463 compatibility: Math.Abs(anyNonDefaultSize - 6) > 0 so first-pass scaling still fires correctly.
|
|
||
| [Test, Order(2)] | ||
| [Category(UITestCategories.IndicatorView)] | ||
| public void VerifyIndicatorSizeAfterReset() |
There was a problem hiding this comment.
[minor] Regression Prevention / Test Coverage - VerifyIndicatorSizeAfterReset is [Order(2)] and implicitly depends on Order(1) having run first to capture the before-state baseline and leave the page in its initial state (IndicatorSize = 20). Added comment documenting this dependency and noting the after-state assertion remains valid if run in isolation, even though the before-state screenshot would be missing.
…s changed to default value (#35215) <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> <!-- 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! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Details On iOS/MacCatalyst, dynamically changing IndicatorView.IndicatorSize back to the default value (6) at runtime has no visual effect — indicators remain stuck at the previously set non-default size. ### Root Cause The platform control has a hard-coded early return when the indicator size equals the default value, which skips resetting the visual transform even when indicators are currently scaled to a different size. Additionally, the field tracking the last applied size was initialized to -1 instead of the actual default. ### Description of Change Replaced the hard-coded default value check with a comparison against the last applied size, so the update is only skipped when the size is truly unchanged. Initialized the tracking field to the default size to correctly represent the initial state. Validated the behavior in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Issues Fixed Fixes #35214 ### Output ScreenShot |Before|After| |--|--| | <video src="https://github.com/user-attachments/assets/30f6d250-1a65-4061-986f-16d3940ac243" >| <video src="https://github.com/user-attachments/assets/759cc5af-9f5f-4d95-8b2a-262499928d0d">|
…s changed to default value (#35215) <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> <!-- 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! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Details On iOS/MacCatalyst, dynamically changing IndicatorView.IndicatorSize back to the default value (6) at runtime has no visual effect — indicators remain stuck at the previously set non-default size. ### Root Cause The platform control has a hard-coded early return when the indicator size equals the default value, which skips resetting the visual transform even when indicators are currently scaled to a different size. Additionally, the field tracking the last applied size was initialized to -1 instead of the actual default. ### Description of Change Replaced the hard-coded default value check with a comparison against the last applied size, so the update is only skipped when the size is truly unchanged. Initialized the tracking field to the default size to correctly represent the initial state. Validated the behavior in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Issues Fixed Fixes #35214 ### Output ScreenShot |Before|After| |--|--| | <video src="https://github.com/user-attachments/assets/30f6d250-1a65-4061-986f-16d3940ac243" >| <video src="https://github.com/user-attachments/assets/759cc5af-9f5f-4d95-8b2a-262499928d0d">|
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/MacCatalyst, dynamically changing IndicatorView.IndicatorSize back to the default value (6) at runtime has no visual effect — indicators remain stuck at the previously set non-default size.
Root Cause
The platform control has a hard-coded early return when the indicator size equals the default value, which skips resetting the visual transform even when indicators are currently scaled to a different size. Additionally, the field tracking the last applied size was initialized to -1 instead of the actual default.
Description of Change
Replaced the hard-coded default value check with a comparison against the last applied size, so the update is only skipped when the size is truly unchanged. Initialized the tracking field to the default size to correctly represent the initial state.
Validated the behavior in the following platforms
Issues Fixed
Fixes #35214
Output ScreenShot
IndicatorSize-BeforeFix.mov
IndicatorSize-AfterFix.mov