[iOS] Fix span Tap gesture on wrapped Label lines in iOS 26+#34640
[iOS] Fix span Tap gesture on wrapped Label lines in iOS 26+#34640SubhikshaSf4851 wants to merge 4 commits intodotnet:mainfrom
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34640Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34640" |
There was a problem hiding this comment.
Pull request overview
Fixes incorrect hit-testing for Span TapGestureRecognizer on wrapped Label lines on iOS 26+ by deferring span-region recalculation until the native UILabel bounds have been finalized, and adds a HostApp repro + automated UI test for issue #34504.
Changes:
- Update iOS
Label.ArrangeOverrideto deferRecalculateSpanPositionswhenUILabel.Boundsis still unset on iOS/MacCatalyst 26+. - Add a new HostApp issue page (
Issue34504) reproducing the NavigationPage + wrapped-span tap scenario. - Add a new Appium/NUnit UI test (
Issue34504) validating taps on wrapped span lines after navigation.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| src/Controls/src/Core/Label/Label.iOS.cs | Defers span-region recalculation on iOS/MacCatalyst 26+ when UILabel.Bounds isn’t finalized yet. |
| src/Controls/tests/TestCases.HostApp/Issues/Issue34504.cs | Adds a two-page NavigationPage-based repro with formatted spans and a success indicator. |
| src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34504.cs | Adds an automated UI test that navigates and taps within the label to trigger a span tap. |
🚦 Gate - Test Before and After Fix📊 Expand Full Gate —
|
| Test | Without Fix (expect FAIL) | With Fix (expect PASS) |
|---|---|---|
🖥️ Issue34504 Issue34504 |
❌ PASS — 201s | ✅ PASS — 82s |
🔴 Without fix — 🖥️ Issue34504: PASS ❌ · 201s
Determining projects to restore...
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 1.93 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 2.39 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 8.95 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 13.02 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 13.02 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Xaml/Controls.Xaml.csproj (in 13.04 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj (in 13.08 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/Foldable/src/Controls.Foldable.csproj (in 13.08 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/BlazorWebView/src/Maui/Microsoft.AspNetCore.Components.WebView.Maui.csproj (in 13.09 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/Maps/src/Controls.Maps.csproj (in 13.12 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Core/maps/src/Maps.csproj (in 13.14 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.60-ci+azdo.13700268
Graphics -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Graphics/Debug/net10.0-ios26.0/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13700268
Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0-ios26.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13700268
Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Core/Debug/net10.0-ios26.0/Microsoft.Maui.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13700268
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.60-ci+azdo.13700268
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.60-ci+azdo.13700268
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13700268
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13700268
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13700268
Controls.Maps -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Maps.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
Controls.Foldable -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Foldable.dll
Controls.Xaml -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Xaml.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:35.75
Determining projects to restore...
Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.Core/UITest.Core.csproj (in 762 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 762 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/VisualTestUtils/VisualTestUtils.csproj (in 762 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/tests/CustomAttributes/Controls.CustomAttributes.csproj (in 762 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 762 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 776 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 842 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 851 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.NUnit/UITest.NUnit.csproj (in 2.08 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.Appium/UITest.Appium.csproj (in 1.34 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/VisualTestUtils.MagickNet/VisualTestUtils.MagickNet.csproj (in 2.46 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.iOS.Tests/Controls.TestCases.iOS.Tests.csproj (in 3.27 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.Analyzers/UITest.Analyzers.csproj (in 3.56 sec).
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13700268
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.60-ci+azdo.13700268
Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13700268
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.60-ci+azdo.13700268
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.04] Discovering: Controls.TestCases.iOS.Tests
[xUnit.net 00:00:00.12] 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
>>>>> 3/31/2026 8:34:07 AM FixtureSetup for Issue34504(iOS)
>>>>> 3/31/2026 8:34:12 AM SpanTapGestureOnSecondLineShouldWork Start
>>>>> 3/31/2026 8:34:13 AM SpanTapGestureOnSecondLineShouldWork Stop
Passed SpanTapGestureOnSecondLineShouldWork [1 s]
NUnit Adapter 4.5.0.0: Test execution complete
Test Run Successful.
Total tests: 1
Passed: 1
Total time: 57.0925 Seconds
🟢 With fix — 🖥️ Issue34504: PASS ✅ · 82s
Determining projects to restore...
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 477 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 501 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 506 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 533 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 547 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.60-ci+azdo.13700268
Graphics -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Graphics/Debug/net10.0-ios26.0/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13700268
Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0-ios26.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13700268
Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Core/Debug/net10.0-ios26.0/Microsoft.Maui.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13700268
Controls.BindingSourceGen -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
Maps -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Maps/Debug/net10.0-ios26.0/Microsoft.Maui.Maps.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13700268
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.60-ci+azdo.13700268
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13700268
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13700268
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
Controls.Foldable -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Foldable.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13700268
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:40.29
Determining projects to restore...
Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 457 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 486 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 489 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 513 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 526 ms).
8 of 13 projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13700268
Graphics -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
Controls.CustomAttributes -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13700268
Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13700268
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.60-ci+azdo.13700268
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.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 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 3/31/2026 8:35:29 AM FixtureSetup for Issue34504(iOS)
>>>>> 3/31/2026 8:35:34 AM SpanTapGestureOnSecondLineShouldWork Start
>>>>> 3/31/2026 8:35:36 AM SpanTapGestureOnSecondLineShouldWork Stop
Passed SpanTapGestureOnSecondLineShouldWork [1 s]
NUnit Adapter 4.5.0.0: Test execution complete
Test Run Successful.
Total tests: 1
Passed: 1
Total time: 18.2028 Seconds
⚠️ Issues found
- ❌ Issue34504 PASSED without fix (should fail) — tests don't catch the bug
📁 Fix files reverted (2 files)
eng/pipelines/ci-copilot.ymlsrc/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cs
🤖 AI Summary📊 Expand Full Review —
|
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #34640 | Fallback to finalSize when control.Bounds is zero in RecalculateSpanPositions |
❌ GATE FAILED (test passes w/o fix) | FormattedStringExtensions.cs |
Original PR |
🔧 Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix (claude-opus-4.6) | Deferred RecalculateSpanPositions via BeginInvokeOnMainThread in Label.iOS.cs when bounds is 0 |
✅ PASS | 1 file | Uses real UIKit geometry after layout settles |
| 2 | try-fix (claude-sonnet-4.6) | LayoutSubviews hook in MauiLabel (mirrors Android LayoutChanged pattern) |
✅ PASS | 4 files (incl. PublicAPI) | More invasive; touches Core platform layer |
| 3 | try-fix (gpt-5.3-codex) | SizeChanged-triggered span region recalculation in Label.iOS.cs |
✅ PASS | 1 file | No bounds fallback, no deferral, no LayoutSubviews |
| 4 | try-fix (gpt-5.4) | Always use finalSize unconditionally in FormattedStringExtensions.cs (removes UILabel.Bounds dependence entirely) |
✅ PASS | 1 file (2 lines) | Simplest fix; strictly cleaner than PR's conditional |
| 5 | try-fix (claude-opus-4.6) | Lazy/on-demand recalculation at hit-test time in GetChildElements — self-healing, skip when data bad |
✅ PASS | 3 files | Most architecturally distinct; adds state complexity |
| PR | PR #34640 | Conditional fallback to finalSize in FormattedStringExtensions.cs when control.Bounds is zero |
❌ GATE FAILED | 1 file | Fix logic is sound; test doesn't catch regression |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| claude-opus-4.6 | 2 | Yes | Lazy recalculation at hit-test time → ran as Attempt 5 |
| gpt-5.3-codex | 2 | Yes | Use TextKit's NSLayoutManager.UsedRectForTextContainer from measured size (variant of PR approach) |
| claude-sonnet-4.6 | 3 | Yes | DidMoveToWindow override in MauiLabel (variant of Attempt 2) |
| gpt-5.4 | 3 | Yes | SetNeedsLayout/LayoutIfNeeded flush before span calc (risky — could cause layout cycles) |
Exhausted: Yes — all remaining ideas are variants of existing approaches or risky anti-patterns
Selected Fix: Attempt 4 (always use finalSize) — smallest change, strictly cleaner logic than PR's conditional. Alternatively PR's fix is fine and more conservative.
Why not PR: Gate FAILED (test passes without fix). Fix logic is correct but unconfirmed empirically.
Key concern: All 5 try-fix attempts AND the PR's fix all pass the same test — because the test doesn't actually reproduce the iOS 26+ timing bug reliably.
📋 Report — Final Recommendation
⚠️ Final Recommendation: REQUEST CHANGES
Phase Status
| Phase | Status | Notes |
|---|---|---|
| Pre-Flight | ✅ COMPLETE | Issue #34504, iOS 26+ span gesture timing bug |
| Gate | ❌ FAILED | iOS — test passes both with and without fix |
| Try-Fix | ✅ COMPLETE | 5 attempts, all 5 passing (but same test weakness applies) |
| Report | ✅ COMPLETE |
Summary
PR #34640 fixes a real iOS 26+ bug where UILabel.Bounds is {0,0,0,0} during ArrangeOverride after navigation, causing span gesture hit-areas to be computed with zero dimensions. The fix in FormattedStringExtensions.cs is logically sound. However, the gate failed: the test passes both with and without the fix, meaning the test does not reliably reproduce the iOS 26+ timing regression. The fix cannot be confirmed empirically from this review.
Root Cause
On iOS 26+ with NavigationPage, UIKit defers committing UILabel.Bounds until after the native layout pass completes. MAUI's ArrangeOverride calls RecalculateSpanPositions during this window when control.Bounds.Width/Height is still {0,0,0,0}. The NSTextContainer.Size is then set to zero, causing NSLayoutManager to compute all glyph rects collapsed at (0,0). Span gesture regions are stored with these wrong rects, so taps on wrapped lines (which have non-zero Y offsets) never match and no gesture fires.
Fix Quality
PR's fix (conditional fallback):
var containerWidth = control.Bounds.Width > 0 ? control.Bounds.Width : (nfloat)finalSize.Width;
var containerHeight = control.Bounds.Height > 0 ? control.Bounds.Height : (nfloat)finalSize.Height;
textContainer.Size = new(containerWidth, control.Lines == 0 ? nfloat.MaxValue : containerHeight);- ✅ Correct fix location (
FormattedStringExtensions.cs) - ✅ Conservative — only deviates from
Boundswhen it's zero - ✅ Minimal change (3 lines, 1 file)
⚠️ Slightly more code than necessary — Attempt 4 showsfinalSizecan be used unconditionally since it's already validated> 0earlier in the method- ❌ Gate FAILED — test does not catch the regression
Simpler alternative (Attempt 4, gpt-5.4):
textContainer.Size = new((nfloat)finalSize.Width,
control.Lines == 0 ? nfloat.MaxValue : (nfloat)finalSize.Height);- ✅ Simplest possible fix (same 1 file, 2 lines changed)
- ✅ Removes
UILabel.Boundsdependence entirely for this calculation - ✅
finalSizeis already validated> 0by the early-return guard above ⚠️ Changes semantics globally (not just iOS 26+): previouslyBoundswas preferred when non-zero. For span hit-area computation,finalSize(MAUI's layout measurement) is arguably more correct anyway.
Issues to Address
-
Test reliability (blocking): The test
SpanTapGestureOnSecondLineShouldWorkpasses without the fix on the iOS 26 simulator. This means the test exercises the UI only after layout has settled — by which time any timing-window bug has self-resolved. A reliable test would need to catch the in-layout state (e.g., checking span regions directly, or using a mock that records whetherRecalculateSpanPositionsproduced non-zero rects). If a reliable automated test is not feasible, the PR description should document this limitation explicitly. -
PR description mismatch: The description mentions deferring via
BeginInvokeOnMainThreadinLabel.iOS.cs, but the actual diff only changesFormattedStringExtensions.cs. The description should be updated to reflect the current implementation. -
Minor improvement: Consider using
finalSizeunconditionally (Attempt 4's approach) instead of the conditional fallback — it's simpler and equally correct.
kubaflo
left a comment
There was a problem hiding this comment.
Looks like the test couldn't catch a bug before fix
|
@kubaflo The test was executed locally and failed on iOS 26 as expected without the fix. The test results for iOS 18 and iOS 26, both with and without the fix, have been shared below. This issue is specific to iOS 26 and does not reproduce on iOS 18, which is why the test passes on iOS 18 with and without the fix. iOS 18 version test results :
iOS 26 version test results :
|




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 whether this change resolves your issue. Thank you!
Root Cause :
On iOS 26+, UILabel layout isn’t finalized when span positions are calculated, so gesture hit areas are computed too early—leading to incorrect tap detection, especially for wrapped lines after navigation.
Description of Change :
Label.iOS.csto detect when the nativeUILabelbounds are not yet finalized duringArrangeOverride(on iOS/MacCatalyst 26+), and defer span position recalculation to the next main run loop iteration to ensure correct gesture hit-testing. [1] [2]Testing and Reproduction:
Issue34504) to the test cases app, which sets up navigation and span labels to exercise the layout and gesture recognition scenario.Issue34504.csin shared tests) that navigates to the test page and verifies that tapping on a wrapped span line successfully triggers the gesture.Issues Fixed
Fixes #34504
Tested the behavior in the following platforms
BeforeFixiOS34054.mov
AfterFix34054.mov