[iOS & Mac] Fixed IndicatorView Size doesnt update dynamically#31129
[iOS & Mac] Fixed IndicatorView Size doesnt update dynamically#31129kubaflo merged 7 commits intodotnet:inflight/currentfrom
Conversation
|
Hey there @@SubhikshaSf4851! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
|
/azp run MAUI-UITests-public |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/rebase |
9a1664e to
401de4a
Compare
|
/azp run MAUI-UITests-public |
|
Azure Pipelines successfully started running 1 pipeline(s). |
| { | ||
| pageControl.IndicatorSize = indicatorView.IndicatorSize; | ||
| pageControl.LayoutSubviews(); | ||
| pageControl.InvalidateMeasure(); |
There was a problem hiding this comment.
Here, can check if the size actually changed before calling InvalidateMeasure().
There was a problem hiding this comment.
@jsuarezruiz The UpdateIndicatorSize method is invoked through the mapper only, but the mapper won’t be triggered if the same value is set again. Please let me know if you have any other concerns.
|
|
||
| [Test] | ||
| [Category(UITestCategories.IndicatorView)] | ||
| public void Issue31064IndicatorViewSizeUpdatesDynamically() |
There was a problem hiding this comment.
@jsuarezruiz I have committed the snapshot
401de4a to
279e77e
Compare
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
279e77e to
489c244
Compare
🤖 AI Summary📊 Expand Full Review🔍 Pre-Flight — Context & Validation📝 Review Session — Updated windows snapshot ·
|
| File:Line | Reviewer Says | Author Says | Status |
|---|---|---|---|
| UIPageControlExtensions.cs:22 | "Check if size actually changed before calling InvalidateMeasure()" (jsuarezruiz) |
"Mapper won't trigger if same value is set again, so check is redundant" (author) | |
| Issue31064.cs:17 | "Pending snapshot on Windows - can you commit it?" (jsuarezruiz) | "I have committed the snapshot" (author) | ✅ RESOLVED |
Potential Concerns
UpdateIndicatorShapestill usespageControl.LayoutSubviews()(unchanged) — possible inconsistency withUpdateIndicatorSizewhich now usesInvalidateMeasure(). ShouldUpdateIndicatorShapealso useInvalidateMeasure()?- Test's
[Issue]attribute marks onlyPlatformAffected.iOS, but issue title and labels include both iOS and MacCatalyst. - Test is screenshot-based (
VerifyScreenshot()) - snapshot files are present for all platforms.
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #31129 | Replace LayoutSubviews() with InvalidateMeasure() in UpdateIndicatorSize |
⏳ PENDING (Gate) | UIPageControlExtensions.cs (+1 -1) |
Original PR |
🚦 Gate — Test Verification
📝 Review Session — Updated windows snapshot · 489c244
Result: ✅ PASSED
Platform: ios
Mode: Full Verification
- Tests FAIL without fix ✅ (issue is detected)
- Tests PASS with fix ✅ (fix works)
Test: Issue31064IndicatorViewSizeUpdatesDynamically (Category: IndicatorView)
🔧 Fix — Analysis & Comparison
📝 Review Session — Updated windows snapshot · 489c244
Fix Exploration Results
Total Attempts: 5
Passed: 2 (PR fix confirmed via Gate + 2 alternatives)
Failed: 3
Fix Candidates
| # | Model | Approach | Result | Notes |
|---|---|---|---|---|
| [Gate] | N/A | PR fix: InvalidateMeasure() only |
✅ PASS | Cleanest: 1 line. MAUI ancestor propagation via InvalidateAncestorsMeasures(). |
| 1 | claude-sonnet-4.5 | UpdateIndicatorSize() + SetNeedsLayout() + Superview?.SetNeedsLayout() |
✅ PASS | 3 lines. Applies transform explicitly then notifies parent via UIKit. Works but more verbose. |
| 2 | claude-opus | Superview?.InvalidateMeasure() in MauiPageControl |
❌ FAIL | 2.54% visual diff. Calling InvalidateMeasure on a UIView ancestor (not MauiPageControl itself) doesn't trigger the right path. |
| 3 | gpt-5.2 | SetNeedsLayout() only |
❌ FAIL | 2.54% visual diff. UIKit-only invalidation without MAUI ancestor propagation is insufficient. |
| 4 | gpt-5.2-codex | SetNeedsLayout() + LayoutIfNeeded() + Superview?.SetNeedsLayout() |
❌ FAIL | 2.54% visual diff. LayoutIfNeeded is synchronous but doesn't reach MAUI layout hierarchy. |
| 5 | gemini | UpdateIndicatorSize() + InvalidateMeasure() |
✅ PASS | 2 lines. Redundant: UpdateIndicatorSize is already called via LayoutSubviews inside InvalidateMeasure cycle. |
Cross-Pollination Round
Three additional models were queried for new ideas. Model 1 suggested InvalidateArrange(), SizeToFit(), Handler?.VirtualView?.InvalidateMeasure(), etc. Models 2-3 indicated the solution space was adequately covered. No new viable alternatives identified.
Best Fix Selection
Selected: PR's fix (InvalidateMeasure()) is the optimal solution.
Rationale:
- Cleanest (1 line vs 2-3 for alternatives)
- Idiomatic – uses the canonical MAUI invalidation path
- Propagates correctly –
InvalidateMeasure()callsSetNeedsLayout()ANDInvalidateAncestorsMeasures(), ensuring the full MAUI hierarchy re-measures - Gate confirmed it passes the actual UI test
- No redundant operations (unlike Attempt 5 which applies transform twice)
The key insight: InvalidateMeasure() on a UIView (that doesn't implement IPlatformMeasureInvalidationController) falls into the else branch of ViewExtensions.InvalidateMeasure(), calling both SetNeedsLayout() and InvalidateAncestorsMeasures(). This walks up MAUI parent views calling InvalidateMeasure(isPropagating: true), triggering a full re-measure that picks up the new indicator size.
Pattern Learned
Fixes that only used UIKit APIs (SetNeedsLayout, LayoutIfNeeded) consistently failed (2.54% visual diff) because they don't reach the MAUI layout hierarchy. Any fix must involve the MAUI InvalidateMeasure() path to propagate layout invalidation up through MAUI parent views.
📋 Report — Final Recommendation
📝 Review Session — Updated windows snapshot · 489c244
PR #31129 Review Report
PR: [iOS & Mac] Fixed IndicatorView Size doesnt update dynamically
Issue: #31064 – [iOS, Catalyst] IndicatorView.IndicatorSize does not update dynamically at runtime
Platforms: iOS + MacCatalyst (fix) | Android + Windows (unaffected, snapshots only)
Recommendation: ✅ APPROVE (with minor suggestions)
The fix is correct, minimal, and well-tested. Gate passed on iOS. All alternative fix approaches explored during try-fix confirmed that InvalidateMeasure() is the optimal solution.
Phase 1: Pre-Flight
Root cause confirmed: UIPageControlExtensions.UpdateIndicatorSize called LayoutSubviews() after updating IndicatorSize. This applied the CGAffineTransform scale on the native control but did NOT propagate the size change up through the MAUI layout hierarchy. Parent views retained their stale measured size for the child, so the indicator dots appeared unchanged at runtime.
Fix: Replace LayoutSubviews() with InvalidateMeasure() (1-line change in UIPageControlExtensions.cs).
Why InvalidateMeasure() works: On a UIView that doesn't implement IPlatformMeasureInvalidationController, InvalidateMeasure() falls into the else branch of ViewExtensions.InvalidateMeasure(), calling both SetNeedsLayout() AND InvalidateAncestorsMeasures(). The latter walks up MAUI parent views calling InvalidateMeasure(isPropagating: true), triggering a full re-measure cycle that picks up the new indicator size.
Existing reviewer thread: jsuarezruiz asked about adding a size-change guard. Author's response was correct — the mapper system prevents same-value calls. Thread is technically moot but remains unresolved in GitHub.
Phase 2: Gate ✅ PASSED
Platform: iOS
Test: Issue31064IndicatorViewSizeUpdatesDynamically
Category: UITestCategories.IndicatorView
| Run | State | Result |
|---|---|---|
| Baseline (revert fix) | LayoutSubviews() |
❌ FAIL – screenshot mismatch (indicators not resized) |
| With fix | InvalidateMeasure() |
✅ PASS |
Tests correctly fail without the fix and pass with the fix. Gate is fully validated.
Phase 3: Try-Fix Exploration
5 alternative approaches explored. Key findings:
| Approach | Result | Notes |
|---|---|---|
PR fix: InvalidateMeasure() |
✅ PASS (Gate) | Cleanest, 1 line, idiomatic |
UpdateIndicatorSize() + SetNeedsLayout() + Superview?.SetNeedsLayout() |
✅ PASS | 3 lines, more verbose |
UpdateIndicatorSize() + InvalidateMeasure() |
✅ PASS | 2 lines, redundant transform |
Superview?.InvalidateMeasure() |
❌ FAIL (2.54%) | Wrong target |
SetNeedsLayout() only |
❌ FAIL (2.54%) | No MAUI ancestor propagation |
SetNeedsLayout() + LayoutIfNeeded() + Superview?.SetNeedsLayout() |
❌ FAIL (2.54%) | UIKit-only, no MAUI hierarchy |
Pattern: Any fix that doesn't reach the MAUI ancestor hierarchy fails. The PR's InvalidateMeasure() is the simplest correct solution. The 3-line alternative (Attempt 1) also works but is redundant.
Cross-pollination round: 3 models queried. No new viable approaches identified beyond the 5 explored.
Code Review (pr-finalize)
🟡 Suggestions (Non-blocking)
-
PlatformAffectedshould include MacCatalystIssue31064.csHostApp:PlatformAffected.iOS→PlatformAffected.iOS | PlatformAffected.MacCatalyst- The PR title says
[iOS & Mac]and a Mac snapshot is included, but the attribute only marks iOS
-
UpdateIndicatorShapeinconsistencyUpdateIndicatorShapein the same file still callsLayoutSubviews()directly- If
IndicatorShapechanges at runtime, the same bug may manifest - Recommend a follow-up PR to apply the same fix
-
Title grammar
- "Fixed IndicatorView Size doesnt update dynamically" → "IndicatorView: Fix IndicatorSize not updating dynamically at runtime"
-
Missing newlines at end of both new test files (cosmetic)
✅ Looks Good
- Fix is minimal and correct (1 line)
InvalidateMeasure()is the idiomatic MAUI invalidation API- Snapshots provided for all 4 platforms
- No breaking changes
- UI test design is sound and provides clear regression coverage
Summary
| Phase | Result |
|---|---|
| Pre-Flight | ✅ Complete |
| Gate (iOS) | ✅ PASSED |
| Try-Fix | ✅ Complete (PR fix confirmed as optimal) |
| Code Review | ✅ Minor suggestions only |
Final recommendation: APPROVE. The fix is correct, minimal, well-tested, and has been empirically validated both in Gate and through exploration of 5 alternative approaches. Minor suggestions (PlatformAffected, follow-up for UpdateIndicatorShape) are non-blocking.
🔧 Try-Fix Analysis: ✅ 2 passed, ❌ 3 failed
✅ Fix 1
Approach 1: Apply Transform in MauiPageControl and Use SetNeedsLayout on Parent
Instead of fixing in UIPageControlExtensions.UpdateIndicatorSize (extension method), fix directly in MauiPageControl by:
- Calling
UpdateIndicatorSize()(the method on MauiPageControl) immediately after setting IndicatorSize in the extension - Calling
pageControl.SetNeedsLayout()to schedule a layout pass
Different from PR's fix: PR uses InvalidateMeasure() which propagates up through the MAUI hierarchy.
This approach calls UpdateIndicatorSize() immediately (no waiting for LayoutSubviews) AND also calls SetNeedsLayout() for a follow-up. We additionally call pageControl.Superview?.SetNeedsLayout() to make the parent also re-layout.
Key difference: We directly apply the CGAffineTransform immediately (sync) instead of waiting for the async layout cycle.
diff --git a/src/Core/src/Platform/iOS/UIPageControlExtensions.cs b/src/Core/src/Platform/iOS/UIPageControlExtensions.cs
index a89bdf1d92..902373536d 100644
--- a/src/Core/src/Platform/iOS/UIPageControlExtensions.cs
+++ b/src/Core/src/Platform/iOS/UIPageControlExtensions.cs
@@ -19,7 +19,9 @@ namespace Microsoft.Maui.Platform
public static void UpdateIndicatorSize(this MauiPageControl pageControl, IIndicatorView indicatorView)
{
pageControl.IndicatorSize = indicatorView.IndicatorSize;
- pageControl.LayoutSubviews();
+ pageControl.UpdateIndicatorSize();
+ pageControl.SetNeedsLayout();
+ pageControl.Superview?.SetNeedsLayout();
}
public static void UpdateHideSingle(this UIPageControl pageControl, IIndicatorView indicatorView)
Analysis: Approach PASSED
The fix works because calling pageControl.UpdateIndicatorSize() directly applies the CGAffineTransform scale immediately (synchronously),
without waiting for the layout cycle. Combined with SetNeedsLayout() on both the page control and its superview,
the parent layout is triggered to redraw.
Why it worked: MauiPageControl.UpdateIndicatorSize() computes (float)IndicatorSize / DefaultIndicatorSize and applies a CGAffineTransform.
Calling this directly means the transform is applied immediately before the screenshot is taken. The SetNeedsLayout() calls
ensure the view hierarchy refreshes properly.
Comparison to PR fix: The PR uses InvalidateMeasure() which propagates through the full MAUI hierarchy (SetNeedsLayout + ancestors).
This approach is more targeted - it applies the transform immediately AND notifies the direct parent.
Potential concern: This approach calls UpdateIndicatorSize() immediately AND also calls SetNeedsLayout() which will call LayoutSubviews()
again async (which will call UpdateIndicatorSize() a second time). Minor redundancy. The PR fix is cleaner.
❌ Fix 2
Approach 2: Fix in MauiPageControl.LayoutSubviews() - Notify superview after transform change
Instead of fixing the extension method, make MauiPageControl.LayoutSubviews() notify the MAUI layout system
after applying the transform. When the transform changes, call this.InvalidateMeasure() on the superview
to propagate the measure invalidation up.
Different from PR fix and Attempt 1: This puts the fix inside MauiPageControl itself,
making the control self-aware of when it changes size and automatically notifying the layout system.
The extension method stays as-is (with LayoutSubviews() call).
diff --git a/src/Core/src/Platform/iOS/MauiPageControl.cs b/src/Core/src/Platform/iOS/MauiPageControl.cs
index 66dfa2f845..8fd35408c3 100644
--- a/src/Core/src/Platform/iOS/MauiPageControl.cs
+++ b/src/Core/src/Platform/iOS/MauiPageControl.cs
@@ -70,6 +70,7 @@ namespace Microsoft.Maui.Platform
var newTransform = CGAffineTransform.MakeScale(scale, scale);
Transform = newTransform;
+ Superview?.InvalidateMeasure();
}
public void UpdatePosition()
Analysis: (2.54% visual difference in screenshot)FAILED
Calling Superview?.InvalidateMeasure() from within MauiPageControl.UpdateIndicatorSize() causes a different visual result.
The screenshot was 2.54% different from the baseline. This suggests the layout settles differently.
Why it failed: Calling InvalidateMeasure() on the superview after applying the transform doesn't go through the same
MAUI layout propagation as calling pageControl.InvalidateMeasure() on the page control itself.
The PR fix calls InvalidateMeasure() ON the page control, which then calls SetNeedsLayout() + propagates to ancestors.
This approach calls InvalidateMeasure() on the SUPERVIEW, which skips the self-invalidation step.
The visual result differs slightly, possibly due to different layout settle timing.
Learning: The fix needs to be applied to the page control itself (not just the superview) to ensure proper propagation.
❌ Fix 3
Approach 3: SetNeedsLayout only (no MAUI propagation)
Replace LayoutSubviews() with just SetNeedsLayout() in UpdateIndicatorSize extension.
This schedules the layout in the next run loop without the overhead of propagating through the MAUI hierarchy.
Different from PR fix: PR uses InvalidateMeasure() which propagates to ancestors.
This approach only calls SetNeedsLayout() on the control itself - minimal change.
Different from Attempt 1: No direct UpdateIndicatorSize() call.
Different from Attempt 2: No ancestor invalidation.
diff --git a/src/Core/src/Platform/iOS/UIPageControlExtensions.cs b/src/Core/src/Platform/iOS/UIPageControlExtensions.cs
index a89bdf1d92..3d246b4131 100644
--- a/src/Core/src/Platform/iOS/UIPageControlExtensions.cs
+++ b/src/Core/src/Platform/iOS/UIPageControlExtensions.cs
@@ -19,7 +19,7 @@ namespace Microsoft.Maui.Platform
public static void UpdateIndicatorSize(this MauiPageControl pageControl, IIndicatorView indicatorView)
{
pageControl.IndicatorSize = indicatorView.IndicatorSize;
- pageControl.LayoutSubviews();
+ pageControl.SetNeedsLayout();
}
public static void UpdateHideSingle(this UIPageControl pageControl, IIndicatorView indicatorView)
Analysis: (2.54% visual difference)FAILED
SetNeedsLayout() alone is insufficient. The visual output differs by 2.54% from the snapshot.
Why it failed: SetNeedsLayout() schedules an async layout pass that applies the Transform,
but it does NOT propagate to MAUI ancestors. The parent view doesn't know the child changed size
and doesn't remeasure, so the indicator view doesn't get proper space allocation.
Learning: The fix MUST include ancestor propagation. SetNeedsLayout() on the control alone is not enough.
The MAUI ancestor remeasure (InvalidateAncestorsMeasures()) is critical to get the right layout.
❌ Fix 4
Approach 4: Explicit LayoutSubviews on superview after SetNeedsLayout
Call SetNeedsLayout() on the page control AND call LayoutIfNeeded() to force the layout synchronously,
then call SetNeedsLayout() on the superview to trigger parent re-layout.
This is different from the PR fix (which uses InvalidateMeasure with ancestor propagation)
and different from attempt 1 (which calls UpdateIndicatorSize directly).
The key idea: force a synchronous layout pass using LayoutIfNeeded(),
then trigger the parent to also update via Superview.SetNeedsLayout().
diff --git a/src/Core/src/Platform/iOS/UIPageControlExtensions.cs b/src/Core/src/Platform/iOS/UIPageControlExtensions.cs
index a89bdf1d92..f19f2009c5 100644
--- a/src/Core/src/Platform/iOS/UIPageControlExtensions.cs
+++ b/src/Core/src/Platform/iOS/UIPageControlExtensions.cs
@@ -19,7 +19,9 @@ namespace Microsoft.Maui.Platform
public static void UpdateIndicatorSize(this MauiPageControl pageControl, IIndicatorView indicatorView)
{
pageControl.IndicatorSize = indicatorView.IndicatorSize;
- pageControl.LayoutSubviews();
+ pageControl.SetNeedsLayout();
+ pageControl.LayoutIfNeeded();
+ pageControl.Superview?.SetNeedsLayout();
}
public static void UpdateHideSingle(this UIPageControl pageControl, IIndicatorView indicatorView)
Analysis: (2.54% visual difference)FAILED
SetNeedsLayout() + LayoutIfNeeded() + Superview.SetNeedsLayout() still produces 2.54% difference.
Why it failed: Even with synchronous layout (LayoutIfNeeded()), the MAUI ancestor propagation is missing.
The superview's SetNeedsLayout() schedules layout but since it's not going through InvalidateMeasure,
the MAUI layout engine doesn't do a full remeasure of the parent.
Key insight: The 2.54% difference seems to be a consistent visual artifact when MAUI ancestor
propagation is missing. The only approaches that work are those that go through InvalidateMeasure()
which calls InvalidateAncestorsMeasures().
✅ Fix 5
Approach 5: Apply transform directly then call InvalidateMeasure (two-phase approach)
Apply the Transform directly in the extension method (bypassing LayoutSubviews cycle for immediate effect),
then call InvalidateMeasure() to propagate to ancestors - but this time do it with proper coding.
Key difference from PR fix: We call MauiPageControl.UpdateIndicatorSize() (to apply Transform immediately)
before calling InvalidateMeasure(). The PR just calls InvalidateMeasure() and lets the transform be
applied asynchronously in the next LayoutSubviews cycle.
This eliminates any visual delay and makes the layout more predictable.
Different from Attempt 1: No Superview?.SetNeedsLayout() extra call.
diff --git a/src/Core/src/Platform/iOS/UIPageControlExtensions.cs b/src/Core/src/Platform/iOS/UIPageControlExtensions.cs
index a89bdf1d92..2f240029d1 100644
--- a/src/Core/src/Platform/iOS/UIPageControlExtensions.cs
+++ b/src/Core/src/Platform/iOS/UIPageControlExtensions.cs
@@ -19,7 +19,8 @@ namespace Microsoft.Maui.Platform
public static void UpdateIndicatorSize(this MauiPageControl pageControl, IIndicatorView indicatorView)
{
pageControl.IndicatorSize = indicatorView.IndicatorSize;
- pageControl.LayoutSubviews();
+ pageControl.UpdateIndicatorSize();
+ pageControl.InvalidateMeasure();
}
public static void UpdateHideSingle(this UIPageControl pageControl, IIndicatorView indicatorView)
Analysis: PASSED
Applying UpdateIndicatorSize() directly (for immediate transform) + InvalidateMeasure() for ancestor propagation works.
Why it works: The approach has two phases:
- Apply the Transform immediately via UpdateIndicatorSize() - ensures visual update is instant
- Call InvalidateMeasure() which calls SetNeedsLayout() + propagates to MAUI ancestors
Comparison to PR fix: The PR fix only calls InvalidateMeasure() (1 line).
This approach calls UpdateIndicatorSize() first (applies transform immediately) then InvalidateMeasure()
(applies it again asynchronously + propagates ancestors). The transform is applied twice but idempotent.
Conclusion: More complex than the PR fix with no real benefit - the extra UpdateIndicatorSize() call
is redundant since InvalidateMeasure()->SetNeedsLayout()->LayoutSubviews() will call it again anyway.
PR fix is simpler and equally correct.
📋 Expand PR Finalization Review
Title: ✅ Good
Current: [iOS & Mac] Fixed IndicatorView Size doesnt update dynamically
Description: ⚠️ Needs Update
- Grammar error: "doesnt" → "doesn't"
- Tense: "Fixed" should be "Fix" (imperative is standard for commit messages)
- Platform tag:
[iOS & Mac]is non-standard — use[iOS]since the fix is inPlatform/iOS/(which covers both iOS and MacCatalyst by convention)
✨ Suggested PR Description
[!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!
Root Cause
When IndicatorSize changes at runtime on iOS/MacCatalyst, UpdateIndicatorSize was calling pageControl.LayoutSubviews() directly. This is a UIKit-level layout pass that only affects the native view's internal subview layout — it does not propagate the size change through the MAUI layout system. As a result, the parent view was never told the IndicatorView's desired size had changed, so the visual size remained stale.
Description of Change
In UIPageControlExtensions.cs, replaced the LayoutSubviews() call in UpdateIndicatorSize with InvalidateMeasure(). This correctly signals the MAUI layout engine to re-measure and re-arrange the indicator view, ensuring the updated IndicatorSize is reflected in the UI.
File changed: src/Core/src/Platform/iOS/UIPageControlExtensions.cs
// Before
pageControl.LayoutSubviews();
// After
pageControl.InvalidateMeasure();This is an iOS/MacCatalyst-only change — the same code path handles both platforms since .iOS.cs files compile for both.
Issues Fixed
Fixes #31064
Platforms Tested
The fix applies to iOS and MacCatalyst. UI tests run cross-platform and snapshot baselines have been added for all platforms.
- iOS
- Mac Catalyst
- Android (not affected by this change)
- Windows (not affected by this change)
Code Review: ✅ Passed
Code Review — PR #31129
🟡 Suggestions
1. UpdateIndicatorShape still uses LayoutSubviews() — potential inconsistency
File: src/Core/src/Platform/iOS/UIPageControlExtensions.cs
The same UIPageControlExtensions class has an UpdateIndicatorShape method that still calls LayoutSubviews() directly:
public static void UpdateIndicatorShape(this MauiPageControl pageControl, IIndicatorView indicatorView)
{
pageControl.IsSquare = !indicatorView.IsCircleShape();
pageControl.LayoutSubviews(); // ← same pattern as the bug being fixed
}If the fix for UpdateIndicatorSize is to use InvalidateMeasure() because LayoutSubviews() bypasses the MAUI layout system, then UpdateIndicatorShape likely has the same issue when shape changes at runtime. Consider updating it for consistency, or confirm it isn't affected (e.g., if shape changes don't alter the measured size).
2. [Issue] attribute marks PlatformAffected.iOS only — issue also affects MacCatalyst
File: src/Controls/tests/TestCases.HostApp/Issues/Issue31064.cs (line 3)
[Issue(IssueTracker.Github, "31064", "Indicator view size should update dynamically", PlatformAffected.iOS)]The issue title and description in GitHub (#31064) explicitly mention both iOS and Catalyst. Marking only PlatformAffected.iOS may cause the test to be skipped on MacCatalyst. Consider using PlatformAffected.iOS | PlatformAffected.macOS (or the appropriate MacCatalyst constant) if the test should also validate the fix on Mac.
3. Missing newline at end of test files
Files:
src/Controls/tests/TestCases.HostApp/Issues/Issue31064.cssrc/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31064.cs
Both files are missing a trailing newline (\ No newline at end of file in the diff). This is a minor style issue but common in the repo — add a newline to the last line of each file.
✅ Looks Good
- Core fix is minimal and correct. Replacing
LayoutSubviews()withInvalidateMeasure()is the right approach — it routes the size change through the MAUI measure/arrange cycle instead of bypassing it at the UIKit layer. - UI test covers the scenario. The test taps the button to trigger a runtime
IndicatorSizechange and verifies via screenshot — this directly validates the fix. - Snapshot images added for all platforms. iOS, Mac, Android, and Windows baselines are committed, which prevents future snapshot test failures.
- Review comment about duplicate calls was addressed. The reviewer's concern about checking if size changed before calling
InvalidateMeasure()was correctly dismissed — the mapper only fires on value changes, so redundant calls aren't a problem.
<!-- 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. !!!!!!! --> ### Root Cause: When the IndicatorSize changed, the layout wasn’t updated, so the new size didn’t appears ### Description of Change Changed the UpdateIndicatorSize method in UIPageControlExtensions.cs to call InvalidateMeasure(), ensuring that the indicator view is properly remeasured and updated when its size changes. <!-- Enter description of the fix in this section --> ### Issues Fixed <!-- Please make sure that there is a bug logged for the issue being fixed. The bug should describe the problem and how to reproduce it. --> Fixes #31064 ### Tested the behaviour in the following platforms : - [x] Windows - [x] Android - [x] iOS - [x] mac ### Screenshot | Before Issue Fix | After Issue Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/891d33a7-6221-470f-9b9e-066512784296"> | <video src="https://github.com/user-attachments/assets/526f4058-85b9-4585-89ed-f665eba3b7d9"> | <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. -->
<!-- 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. !!!!!!! --> ### Root Cause: When the IndicatorSize changed, the layout wasn’t updated, so the new size didn’t appears ### Description of Change Changed the UpdateIndicatorSize method in UIPageControlExtensions.cs to call InvalidateMeasure(), ensuring that the indicator view is properly remeasured and updated when its size changes. <!-- Enter description of the fix in this section --> ### Issues Fixed <!-- Please make sure that there is a bug logged for the issue being fixed. The bug should describe the problem and how to reproduce it. --> Fixes #31064 ### Tested the behaviour in the following platforms : - [x] Windows - [x] Android - [x] iOS - [x] mac ### Screenshot | Before Issue Fix | After Issue Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/891d33a7-6221-470f-9b9e-066512784296"> | <video src="https://github.com/user-attachments/assets/526f4058-85b9-4585-89ed-f665eba3b7d9"> | <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. -->
<!-- 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. !!!!!!! --> ### Root Cause: When the IndicatorSize changed, the layout wasn’t updated, so the new size didn’t appears ### Description of Change Changed the UpdateIndicatorSize method in UIPageControlExtensions.cs to call InvalidateMeasure(), ensuring that the indicator view is properly remeasured and updated when its size changes. <!-- Enter description of the fix in this section --> ### Issues Fixed <!-- Please make sure that there is a bug logged for the issue being fixed. The bug should describe the problem and how to reproduce it. --> Fixes #31064 ### Tested the behaviour in the following platforms : - [x] Windows - [x] Android - [x] iOS - [x] mac ### Screenshot | Before Issue Fix | After Issue Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/891d33a7-6221-470f-9b9e-066512784296"> | <video src="https://github.com/user-attachments/assets/526f4058-85b9-4585-89ed-f665eba3b7d9"> | <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. -->
<!-- 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. !!!!!!! --> ### Root Cause: When the IndicatorSize changed, the layout wasn’t updated, so the new size didn’t appears ### Description of Change Changed the UpdateIndicatorSize method in UIPageControlExtensions.cs to call InvalidateMeasure(), ensuring that the indicator view is properly remeasured and updated when its size changes. <!-- Enter description of the fix in this section --> ### Issues Fixed <!-- Please make sure that there is a bug logged for the issue being fixed. The bug should describe the problem and how to reproduce it. --> Fixes #31064 ### Tested the behaviour in the following platforms : - [x] Windows - [x] Android - [x] iOS - [x] mac ### Screenshot | Before Issue Fix | After Issue Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/891d33a7-6221-470f-9b9e-066512784296"> | <video src="https://github.com/user-attachments/assets/526f4058-85b9-4585-89ed-f665eba3b7d9"> | <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. -->
…t#31129) <!-- 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. !!!!!!! --> ### Root Cause: When the IndicatorSize changed, the layout wasn’t updated, so the new size didn’t appears ### Description of Change Changed the UpdateIndicatorSize method in UIPageControlExtensions.cs to call InvalidateMeasure(), ensuring that the indicator view is properly remeasured and updated when its size changes. <!-- Enter description of the fix in this section --> ### Issues Fixed <!-- Please make sure that there is a bug logged for the issue being fixed. The bug should describe the problem and how to reproduce it. --> Fixes dotnet#31064 ### Tested the behaviour in the following platforms : - [x] Windows - [x] Android - [x] iOS - [x] mac ### Screenshot | Before Issue Fix | After Issue Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/891d33a7-6221-470f-9b9e-066512784296"> | <video src="https://github.com/user-attachments/assets/526f4058-85b9-4585-89ed-f665eba3b7d9"> | <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. -->
<!-- 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. !!!!!!! --> ### Root Cause: When the IndicatorSize changed, the layout wasn’t updated, so the new size didn’t appears ### Description of Change Changed the UpdateIndicatorSize method in UIPageControlExtensions.cs to call InvalidateMeasure(), ensuring that the indicator view is properly remeasured and updated when its size changes. <!-- Enter description of the fix in this section --> ### Issues Fixed <!-- Please make sure that there is a bug logged for the issue being fixed. The bug should describe the problem and how to reproduce it. --> Fixes #31064 ### Tested the behaviour in the following platforms : - [x] Windows - [x] Android - [x] iOS - [x] mac ### Screenshot | Before Issue Fix | After Issue Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/891d33a7-6221-470f-9b9e-066512784296"> | <video src="https://github.com/user-attachments/assets/526f4058-85b9-4585-89ed-f665eba3b7d9"> | <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. -->
## What's Coming .NET MAUI inflight/candidate introduces significant improvements across all platforms with focus on quality, performance, and developer experience. This release includes 46 commits with various improvements, bug fixes, and enhancements. ## Button - [Android] Implemented material3 support for Button by @Dhivya-SF4094 in #33173 <details> <summary>🔧 Fixes</summary> - [Implement Material3 support for Button](#33172) </details> ## CollectionView - [Android] Fix RemainingItemsThresholdReachedCommand not firing when CollectionView has Header and Footer both defined by @SuthiYuvaraj in #29618 <details> <summary>🔧 Fixes</summary> - [Android : RemainingItemsThresholdReachedCommand not firing when CollectionVew has Header and Footer both defined](#29588) </details> - [iOS/MacCatalyst] Fix CollectionView ScrollTo for horizontal layouts by @Shalini-Ashokan in #33853 <details> <summary>🔧 Fixes</summary> - [[iOS/MacCatalyst] CollectionView ScrollTo does not work with horizontal Layout](#33852) </details> - [iOS & Mac] Fixed IndicatorView Size doesnt update dynamically by @SubhikshaSf4851 in #31129 <details> <summary>🔧 Fixes</summary> - [[iOS, Catalyst] IndicatorView.IndicatorSize does not update dynamically at runtime](#31064) </details> - [Android] Fix for CollectionView Scrolled event is triggered on the initial app load. by @BagavathiPerumal in #33558 <details> <summary>🔧 Fixes</summary> - [[Android] CollectionView Scrolled event is triggered on the initial app load.](#33333) </details> - [iOS, Android] Fix for CollectionView IsEnabled=false allows touch interactions by @praveenkumarkarunanithi in #31403 <details> <summary>🔧 Fixes</summary> - [More issues with CollectionView IsEnabled, InputTransparent, Opacity via Styles and code behind](#19771) </details> - [iOS] Fix VerticalOffset Update When Modifying CollectionView.ItemsSource While Scrolled by @devanathan-vaithiyanathan in #34153 <details> <summary>🔧 Fixes</summary> - [[iOS]VerticalOffset Not Reset to Zero After Clearing ItemSource in CollectionView](#26798) </details> ## DateTimePicker - [Android] Fix DatePicker MinimumDate/MaximumDate not updating dynamically by @HarishwaranVijayakumar in #33687 <details> <summary>🔧 Fixes</summary> - [[regression/8.0.3] [Android] DatePicker control minimum date issue](#19256) - [[Android] DatePicker does not update MinimumDate / MaximumDate in the Popup when set in the viewmodel after first opening](#33583) </details> ## Drawing - Android drawable perf by @albyrock87 in #31567 ## Editor - [Android] Implemented material3 support for Editor by @SyedAbdulAzeemSF4852 in #33478 <details> <summary>🔧 Fixes</summary> - [Implement Material3 Support for Editor](#33476) </details> ## Entry - [iOS, Mac] Fix for CursorPosition not updating when typing into Entry control by @SyedAbdulAzeemSF4852 in #30505 <details> <summary>🔧 Fixes</summary> - [Entry control CursorPosition does not update on TextChanged event [iOS Maui 8.0.7] ](#20911) - [CursorPosition not calculated correctly on behaviors events for iOS devices](#32483) </details> ## Flyoutpage - [Android, Windows] Fix for FlyoutPage toolbar button not updating on orientation change by @praveenkumarkarunanithi in #31962 <details> <summary>🔧 Fixes</summary> - [Flyout page in Android does not show flyout button (burger) consistently](#24468) </details> - Fix for First Item in CollectionView Overlaps in FlyoutPage.Flyout on iOS by @praveenkumarkarunanithi in #29265 <details> <summary>🔧 Fixes</summary> - [[iOS] CollectionView not rendering first item correctly in FlyoutPage.Flyout](#29170) </details> ## Image - [Android] Fix excessive memory usage for stream and resource-based image loading by @Shalini-Ashokan in #33590 <details> <summary>🔧 Fixes</summary> - [[Android] Unexpected high Bitmap.ByteCount when loading image via ImageSource.FromResource() or ImageSource.FromStream() in .NET MAUI](#33239) </details> - [Android] Fix for Resize method returns an image that has already been disposed by @SyedAbdulAzeemSF4852 in #29964 <details> <summary>🔧 Fixes</summary> - [In GraphicsView, the Resize method returns an image that has already been disposed](#29961) - [IIMage.Resize bugged behaviour](#31103) </details> ## Label - Fixed Label Span font property inheritance when applied via Style by @SubhikshaSf4851 in #34110 <details> <summary>🔧 Fixes</summary> - [`Span` does not inherit text styling from `Label` if that styling is applied using `Style` ](#21326) </details> - [Android] Implemented material3 support for Label by @SyedAbdulAzeemSF4852 in #33599 <details> <summary>🔧 Fixes</summary> - [Implement Material3 Support for Label](#33598) </details> ## Map - [Android] Fix Circle Stroke color is incorrectly updated as Fill color. by @NirmalKumarYuvaraj in #33643 <details> <summary>🔧 Fixes</summary> - [[Android] Circle Stroke color is incorrectly updated as Fill color.](#33642) </details> ## Mediapicker - [iOS] Fix: invoke MediaPicker completion handler after DismissViewController by @yuriikyry4enko in #34250 <details> <summary>🔧 Fixes</summary> - [[iOS] Media Picker UIImagePickerController closing issue](#21996) </details> ## Navigation - Fix ContentPage memory leak on Android when using NavigationPage modally (fixes #33918) by @brunck in #34117 <details> <summary>🔧 Fixes</summary> - [[Android] Modal TabbedPage whose tabs are NavigationPage(ContentPage) is retained after PopModalAsync()](#33918) </details> ## Picker - [Android] Implement material3 support for TimePicker by @HarishwaranVijayakumar in #33646 <details> <summary>🔧 Fixes</summary> - [Implement Material3 support for TimePicker](#33645) </details> - [Android] Implemented Material3 support for Picker by @SyedAbdulAzeemSF4852 in #33668 <details> <summary>🔧 Fixes</summary> - [Implement Material3 support for Picker](#33665) </details> ## RadioButton - [Android] Implemented material3 support for RadioButton by @SyedAbdulAzeemSF4852 in #33468 <details> <summary>🔧 Fixes</summary> - [Implement Material3 Support for RadioButton](#33467) </details> ## Setup - Clarify MA003 error message by @jeremy-visionaid in #34067 <details> <summary>🔧 Fixes</summary> - [MA003 false positive with 9.0.21](#26599) </details> ## Shell - [Android] Fix TabBar FlowDirection not updating dynamically by @SubhikshaSf4851 in #33091 <details> <summary>🔧 Fixes</summary> - [[Android, iOS] FlowDirection RTL is not updated dynamically on Shell TabBar](#32993) </details> - [Android] Fix page not disposed on Shell replace navigation by @Vignesh-SF3580 in #33426 <details> <summary>🔧 Fixes</summary> - [[Android] [Shell] replace navigation leaks current page](#25134) </details> - [Android] Fixed Shell flyout does not disable scrolling when FlyoutVerticalScrollMode is set to Disabled by @NanthiniMahalingam in #32734 <details> <summary>🔧 Fixes</summary> - [[Android] Shell.FlyoutVerticalScrollMode="Disabled" does not disable scrolling](#32477) </details> ## Single Project - Fix: Throw a clear error when an SVG lacks dimensions instead of a NullReferenceException by @Shalini-Ashokan in #33194 <details> <summary>🔧 Fixes</summary> - [MAUI Fails To Convert Valid SVG Files Into PNG Files (Object reference not set to an instance of an object)](#32460) </details> ## SwipeView - [iOS] Fix SwipeView stays open on iOS after updating content by @devanathan-vaithiyanathan in #31248 <details> <summary>🔧 Fixes</summary> - [[iOS] - Swipeview with collectionview issue](#19541) </details> ## TabbedPage - [Windows] Fixed IsEnabled Property not works on Tabs by @NirmalKumarYuvaraj in #26728 <details> <summary>🔧 Fixes</summary> - [ShellContent IsEnabledProperty does not work](#5161) - [[Windows] Shell Tab IsEnabled Not Working](#32996) </details> - [Android] Fix NavigationBar overlapping StatusBar when NavigationBar visibility changes by @Vignesh-SF3580 in #33359 <details> <summary>🔧 Fixes</summary> - [[Android] NavigationBar overlaps with StatusBar when mixing HasNavigationBar=true/false in TabbedPage on Android 15 (API 35)](#33340) </details> ## Templates - Fix for unable to open task using keyboard navigation on windows platform by @SuthiYuvaraj in #33647 <details> <summary>🔧 Fixes</summary> - [Unable to open task using keyboard: A11y_.NET maui_User can get all the insights of Dashboard_Keyboard](#30787) </details> ## TitleView - Fix for NavigationPage.TitleView does not expand with host window in iPadOS 26+ by @SuthiYuvaraj in #33088 ## Toolbar - [iOS] Fix toolbar items ignoring BarTextColor on iOS/MacCatalyst 26+ by @Shalini-Ashokan in #34036 <details> <summary>🔧 Fixes</summary> - [[iOS 26] ToolbarItem color with custom BarTextColor not working](#33970) </details> - [Android] Fix for ToolbarItem retaining the icon from the previous page on Android when using NavigationPage. by @BagavathiPerumal in #32311 <details> <summary>🔧 Fixes</summary> - [Toolbaritem keeps the icon of the previous page on Android, using NavigationPage (not shell)](#31727) </details> ## WebView - [Android] Fix WebView in a grid expands beyond it's cell by @devanathan-vaithiyanathan in #32145 <details> <summary>🔧 Fixes</summary> - [Android - WebView in a grid expands beyond it's cell](#32030) </details> ## Xaml - ContentPresenter: Propagate binding context to children with explicit TemplateBinding by @HarishwaranVijayakumar in #30880 <details> <summary>🔧 Fixes</summary> - [Binding context in ContentPresenter](#23797) </details> <details> <summary>🔧 Infrastructure (1)</summary> - [Revert] ContentPresenter: Propagate binding context to children with explicit TemplateBinding by @Ahamed-Ali in #34332 </details> <details> <summary>🧪 Testing (6)</summary> - [Testing] Feature Matrix UITest Cases for Shell Flyout Page by @NafeelaNazhir in #32525 - [Testing] Feature Matrix UITest Cases for Brushes by @LogishaSelvarajSF4525 in #31833 - [Testing] Feature Matrix UITest Cases for BindableLayout by @LogishaSelvarajSF4525 in #33108 - [Android] Add UI tests for Material 3 CheckBox by @HarishwaranVijayakumar in #34126 <details> <summary>🔧 Fixes</summary> - [[Android] Add UI tests for Material 3 CheckBox](#34125) </details> - [Testing] Feature Matrix UITest Cases for Shell Tabbed Page by @NafeelaNazhir in #33159 - [Testing] Fixed Test case failure in PR 34294 - [03/2/2026] Candidate - 1 by @TamilarasanSF4853 in #34334 </details> <details> <summary>📦 Other (2)</summary> - Bumps Syncfusion.Maui.Toolkit dependency to version 1.0.9 by @PaulAndersonS in #34178 - Fix crash when closing Windows based app when using TitleBar by @MFinkBK in #34032 <details> <summary>🔧 Fixes</summary> - [Unhandled exception "Value does not fall within the expected range" when closing Windows app](#32194) </details> </details> **Full Changelog**: main...inflight/candidate

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!
Root Cause:
When the IndicatorSize changed, the layout wasn’t updated, so the new size didn’t appears
Description of Change
Changed the UpdateIndicatorSize method in UIPageControlExtensions.cs to call InvalidateMeasure(), ensuring that the indicator view is properly remeasured and updated when its size changes.
Issues Fixed
Fixes #31064
Tested the behaviour in the following platforms :
Screenshot
BeforeFix31064.mov
AfterFIx31064.mov