Skip to content

[iOS] Fix Span TapGestureRecognizer hitbox misalignment in FormattedString when font contains a STAT table - #36564

Merged
kubaflo merged 5 commits into
dotnet:inflight/currentfrom
devanathan-vaithiyanathan:fix-36505
Jul 15, 2026
Merged

[iOS] Fix Span TapGestureRecognizer hitbox misalignment in FormattedString when font contains a STAT table#36564
kubaflo merged 5 commits into
dotnet:inflight/currentfrom
devanathan-vaithiyanathan:fix-36505

Conversation

@devanathan-vaithiyanathan

Copy link
Copy Markdown
Contributor

Note

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

Issue Details

When a Label uses FormattedText with a custom font that has an OpenType STAT table, tapping a Span with a TapGestureRecognizer doesn't work correctly on iOS. The tap hitbox is vertically offset from where the text is actually drawn — tapping the visible text does nothing, tapping below it fires the event.

Description of Change

NSLayoutManager.UsesFontLeading is now set to false on iOS 16+ so the tap hitbox calculation uses the same line metrics that CoreText uses to render the text, eliminating the vertical offset. The container size is also updated to use finalSize (MAUI's layout size) instead of control.Bounds, which can be zero/stale during layout on Mac Catalyst.

Issues Fixed

Fixes #36505

Tested the behavior in the following platforms.

  • Android
  • Windows
  • iOS
  • Mac
Before After
iOS
Before.mov
iOS
After.mov

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

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

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

Or

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

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service dotnet-policy-service Bot added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Jul 14, 2026
@devanathan-vaithiyanathan devanathan-vaithiyanathan added the community ✨ Community Contribution label Jul 14, 2026
@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jul 14, 2026

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Expert Review — 1 findings

See inline comments for details.

@MauiBot MauiBot added s/agent-gate-passed AI verified tests catch the bug (fail without fix, pass with fix) s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Jul 14, 2026
MauiBot

This comment was marked as outdated.

@MauiBot MauiBot removed the s/agent-review-in-progress AI review is currently running for this PR label Jul 14, 2026
kubaflo pushed a commit that referenced this pull request Jul 14, 2026
…pe stageDependencies)

Regression from 4917a9e. The UpdateComment job gated its deep-artifact
download on a JOB-level variable:

  runDeepUITestsResult: $[ dependencies.RunDeepUITests.result ]

At JOB scope, `dependencies.<Name>` resolves SAME-STAGE jobs, not sibling
stages, so `dependencies.RunDeepUITests.result` silently resolved to EMPTY.
The download condition in(<empty>, 'Succeeded','SucceededWithIssues','Failed')
was therefore always false -> the deep artifact was NEVER downloaded -> the
aggregator saw no categories -> the summary posted the false warning
'No UI test results were produced for the detected categories' even when deep
ran fine and published its artifact.

Observed on build 14651727 / PR #36564: the Label deep category was detected,
ran 18 min, captured diagnostics, and published drop-ios_ui_tests-controls-Label
-- yet the summary claimed no results. (The sibling vars aiSummaryReviewId and
trustedGateResult in the same block already correctly use stageDependencies.)

Fixes:
1. Root cause: use stageDependencies.RunDeepUITests.result (correct cross-stage
   context at job scope).
2. Defense-in-depth: gate the download on ne(result,'Skipped') instead of an
   in()-allowlist, so we skip ONLY when deep is DEFINITIVELY Skipped and err
   toward SHOWING results for any other/unknown value. Hiding real results is
   far worse than a cosmetic yellow; continueOnError absorbs a missing artifact.

The stage-level references (UpdateAISummaryComment stage condition and the
AnalyzeCopilotTokenUsage stage variable) correctly keep `dependencies` -- that
is the right context at STAGE scope.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 15d2af20-e4ab-4e88-9011-cfbd83513bc0
@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jul 14, 2026

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Expert Review — 2 findings

See inline comments for details.

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

AI Review Summary

@devanathan-vaithiyanathan — new AI review results are available based on this last commit: e247aef. To request a fresh review after new comments or commits, comment /review rerun.

Gate Passed Confidence Low Platform iOS


🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix

Gate Result: ✅ PASSED

Platform: IOS · Base: main · Merge base: 0395a53b

Test Without Fix (expect FAIL) With Fix (expect PASS)
🖥️ Issue36505 Issue36505 ✅ FAIL — 270s ✅ PASS — 104s
🔴 Without fix — 🖥️ Issue36505: FAIL ✅ · 270s
  Determining projects to restore...
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 433 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 441 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 4.95 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/Foldable/src/Controls.Foldable.csproj (in 5.13 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Xaml/Controls.Xaml.csproj (in 5.14 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 5.14 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/BlazorWebView/src/Maui/Microsoft.AspNetCore.Components.WebView.Maui.csproj (in 5.14 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj (in 5.14 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/Maps/src/Controls.Maps.csproj (in 5.14 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 5.16 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Core/maps/src/Maps.csproj (in 5.17 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.100-ci+azdo.14653021
  Graphics -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Graphics/Debug/net10.0-ios26.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14653021
  Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0-ios26.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14653021
  Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Core/Debug/net10.0-ios26.0/Microsoft.Maui.dll
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14653021
  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.100-ci+azdo.14653021
  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.100-ci+azdo.14653021
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14653021
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14653021
  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.100-ci+azdo.14653021
  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:02:17.18
  Determining projects to restore...
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/tests/CustomAttributes/Controls.CustomAttributes.csproj (in 678 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/VisualTestUtils/VisualTestUtils.csproj (in 678 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 659 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.Core/UITest.Core.csproj (in 670 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 762 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 76 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 780 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 801 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.NUnit/UITest.NUnit.csproj (in 1.63 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.Appium/UITest.Appium.csproj (in 2.09 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.Analyzers/UITest.Analyzers.csproj (in 3.02 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/VisualTestUtils.MagickNet/VisualTestUtils.MagickNet.csproj (in 3.08 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.iOS.Tests/Controls.TestCases.iOS.Tests.csproj (in 3.81 sec).
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14653021
  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.100-ci+azdo.14653021
  Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14653021
  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.100-ci+azdo.14653021
  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.
[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
>>>>> 7/14/2026 10:46:01 AM FixtureSetup for Issue36505(iOS)
>>>>> 7/14/2026 10:46:06 AM SpanTapHitboxShouldAlignWithRenderedTextWhenFontHasStatTable Start
>>>>> 7/14/2026 10:46:10 AM SpanTapHitboxShouldAlignWithRenderedTextWhenFontHasStatTable Stop
>>>>> 7/14/2026 10:46:10 AM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
  Failed SpanTapHitboxShouldAlignWithRenderedTextWhenFontHasStatTable [4 s]
  Error Message:
     Tapping the span at its visual position should trigger the TapGestureRecognizer
Assert.That(App.WaitForTextToBePresentInElement("StatusLabel", "Success", timeout: TimeSpan.FromSeconds(3)), Is.True)
  Expected: True
  But was:  False

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.Issues.Issue36505.SpanTapHitboxShouldAlignWithRenderedTextWhenFontHasStatTable() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36505.cs:line 33

1)    at Microsoft.Maui.TestCases.Tests.Issues.Issue36505.SpanTapHitboxShouldAlignWithRenderedTextWhenFontHasStatTable() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36505.cs:line 33


NUnit Adapter 4.5.0.0: Test execution complete
Results File: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue36505.trx

Total tests: 1
     Failed: 1
Test Run Failed.
 Total time: 1.3219 Minutes
>>> TRX_RESULT_FILE: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue36505.trx

🟢 With fix — 🖥️ Issue36505: PASS ✅ · 104s
  Determining projects to restore...
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 344 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 350 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 352 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 387 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 399 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.100-ci+azdo.14653021
  Graphics -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Graphics/Debug/net10.0-ios26.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14653021
  Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0-ios26.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14653021
  Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Core/Debug/net10.0-ios26.0/Microsoft.Maui.dll
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14653021
  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.100-ci+azdo.14653021
  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.100-ci+azdo.14653021
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14653021
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14653021
  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.100-ci+azdo.14653021
  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:49.53
  Determining projects to restore...
  Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 370 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 370 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 376 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 353 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 410 ms).
  8 of 13 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14653021
  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.100-ci+azdo.14653021
  Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14653021
  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.100-ci+azdo.14653021
  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.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
  VisualTestUtils.MagickNet -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.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.
[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.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
>>>>> 7/14/2026 10:47:49 AM FixtureSetup for Issue36505(iOS)
>>>>> 7/14/2026 10:47:54 AM SpanTapHitboxShouldAlignWithRenderedTextWhenFontHasStatTable Start
>>>>> 7/14/2026 10:47:55 AM SpanTapHitboxShouldAlignWithRenderedTextWhenFontHasStatTable Stop
  Passed SpanTapHitboxShouldAlignWithRenderedTextWhenFontHasStatTable [537 ms]
NUnit Adapter 4.5.0.0: Test execution complete
Results File: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue36505.trx

Test Run Successful.
Total tests: 1
     Passed: 1
 Total time: 22.3133 Seconds
>>> TRX_RESULT_FILE: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue36505.trx

📁 Fix files reverted (1 files)
  • src/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cs

📱 UI Tests — Label

Detected UI test categories: Label

Deep UI tests — 90 passed, 0 failed across 1 category on platform-pool agent (replaces in-process counts above).

🧪 UI Test Execution Results (deep, platform pool)

Category Tests Snapshot diffs
Label 90/92 ✓
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)

📋 Pre-Flight — Context & Validation

Issue: #36505 - [iOS] Span TapGestureRecognizer hitbox is mispositioned inside FormattedString when the font contains a STAT table (line-height/leading mismatch)
PR: #36564 - [WIP][iOS] Fix Span TapGestureRecognizer hitbox misalignment in FormattedString when font contains a STAT table
Platforms Affected: iOS; MacCatalyst is also affected by the changed .ios.cs implementation and PR test scope
Files Changed: 1 implementation, 4 test/support

Key Findings

  • Root issue: Label.FormattedText span hit regions are calculated with TextKit metrics that can diverge from visible UILabel/CoreText rendering when the custom font has an OpenType STAT table.
  • PR production fix disables NSLayoutManager.UsesFontLeading on iOS 16+ and uses MAUI finalSize for the text container size instead of stale/zero UILabel.Bounds.
  • Prior inline review found the PR's platform gate misses MacCatalyst because .ios.cs also compiles for MacCatalyst and OperatingSystem.IsIOSVersionAtLeast(16) is false there.
  • UI test type: Appium UI test, category Label, target platform for this loop: iOS.

Code Review Summary

Verdict: NEEDS_CHANGES
Confidence: low
Errors: 1 | Warnings: 1 | Suggestions: 0

Key code review findings:

  • Error: FormattedStringExtensions.cs:233 MacCatalyst keeps UsesFontLeading=true, so the fix does not apply to a platform the PR marks/tests.
  • Warning: Issue36505.cs:41 MacCatalyst test offset may compensate for the unfixed hitbox rather than tapping the rendered text position.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #36564 TextKit span-position calculation with UsesFontLeading=false on iOS 16+ and finalSize-based text container sizing PASSED (Gate, provided) FormattedStringExtensions.cs, UI test/support files Original PR; iOS gate passed, but MacCatalyst platform gate is incomplete

🔬 Code Review — Deep Analysis

Code Review — PR #36564

Independent Assessment

What this changes: Updates iOS/MacCatalyst formatted Label span hitbox calculation by disabling NSLayoutManager.UsesFontLeading on iOS 16+ and preferring MAUI finalSize over potentially stale UILabel.Bounds. Adds a UI test with a STAT-table custom font.

Inferred motivation: Align Span gesture hit regions with visually rendered text when custom font metrics differ between TextKit hitbox calculation and CoreText/UILabel rendering.

Reconciliation with PR Narrative

Author claims: Fixes #36505 by disabling font leading on iOS 16+ and using finalSize; tested iOS and Mac.

Agreement/disagreement: Matches the iOS intent, but the implementation does not apply the font-leading fix on MacCatalyst despite PR/test scope including MacCatalyst.

Prior Review Reconciliation

Prior Error Finding Source Status Evidence
MacCatalyst keeps UsesFontLeading=true because .ios.cs also compiles for MacCatalyst and OperatingSystem.IsIOSVersionAtLeast(16) is false there. MauiBot inline review, FormattedStringExtensions.cs:233 Unresolved Current code still uses UsesFontLeading = !OperatingSystem.IsIOSVersionAtLeast(16).

Blast Radius Assessment

  • Runs for all instances: No; only formatted-text Label span-position recalculation on iOS/MacCatalyst.
  • Startup impact: No.
  • Static/shared state: No.

CI Status

  • Required-check result: unavailable; gh pr checks 36564 --repo dotnet/maui --required could not run because gh is unauthenticated in this environment.
  • Classification: undetermined.
  • Action taken: Confidence capped low. Public PR metadata and patch were fetched through unauthenticated GitHub API/curl.

Findings

Error — MacCatalyst does not receive the font-leading fix

src/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cs:233

This code path also applies to MacCatalyst, but OperatingSystem.IsIOSVersionAtLeast(16) is false on MacCatalyst. MacCatalyst therefore keeps UsesFontLeading=true, so the STAT-table hitbox mismatch remains unfixed for a platform the PR explicitly marks/tests. Use OperatingSystem.IsMacCatalystVersionAtLeast(...) as well, or disable font leading for this TextKit hitbox calculation on both platforms.

Warning — MacCatalyst test offset can mask the unfixed behavior

src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36505.cs:41

The MacCatalyst-specific - visualLineHeight * 0.5f offset appears to compensate for the still-misaligned hitbox rather than tapping the rendered text position. Once the MacCatalyst production fix is applied, this offset should be revisited or the test may validate a workaround instead of the intended behavior.

Failure-Mode Probing

  • MacCatalyst with STAT-table font: current PR fix does not apply; hitboxes can remain vertically offset.
  • iOS 16+: UsesFontLeading=false should align TextKit hitboxes with rendered text.
  • iOS <16: behavior remains unchanged.
  • Stale/zero UILabel.Bounds: finalSize is already validated positive and now drives the text container size.
  • Handler reconnect/arrange repeats: no new subscriptions or static state.

Verdict: NEEDS_CHANGES

Confidence: low

Summary: The iOS approach is plausible, but MacCatalyst remains unfixed while the PR claims/tests Mac coverage. CI required-check status could not be verified with authenticated gh, further limiting confidence.


🛠️ Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix UILabel/TextKit draw-rect alignment offset FAIL 1 file Did not compile: NSLayoutManager.GetUsedRectForTextContainer is not available in the target binding.
2 try-fix CoreText CTFrame/CTLine span rectangle calculation FAIL 1 file Built and ran iOS UI test, but tap still did not trigger StatusLabel=Success.
3 try-fix Preserve TextKit fix but gate UsesFontLeading=false for iOS 16+ OR MacCatalyst 16+ PASS (iOS) 1 file iOS Issue36505 passed; MacCatalyst build succeeded but Appium launch was environment-blocked.
PR PR #36564 UsesFontLeading=false on iOS 16+ only plus finalSize text-container sizing PASSED (Gate) 5 files Original PR passes iOS gate but misses MacCatalyst in .ios.cs code path.

Cross-Pollination

Model/Reviewer Round New Ideas? Details
maui-expert-reviewer 1 Yes Suggested TextKit/UILabel offset approach; rejected after compile failure.
maui-expert-reviewer 2 Yes Suggested CoreText rewrite; rejected after iOS UI test failure.
maui-expert-reviewer 3 Yes Suggested correcting the runtime platform gate while keeping TextKit; passed requested iOS test and fixes reviewed MacCatalyst gap.

Exhausted: No - stopped because candidate #3 passed the requested iOS test and is demonstrably better than the PR's current fix for the identified MacCatalyst miss.
Selected Fix: Candidate #3 - it is the smallest passing candidate, preserves the PR's validated iOS behavior, and corrects the .ios.cs MacCatalyst gate by using OperatingSystem.IsMacCatalystVersionAtLeast(16) as well as the iOS check.


📝 Recommended PR Title & Description

Assessment: ✏️ Recommend updating — the winning fix extends the UsesFontLeading change to MacCatalyst and removes the MacCatalyst test workaround, so the current [WIP][iOS] title and iOS-only font-leading description are stale.

Recommended title

[iOS/MacCatalyst] Label: Fix FormattedString span tap hitboxes for STAT-table fonts

Recommended description

### Issue Details
When a Label uses FormattedText with a custom font that has an OpenType STAT table, tapping a Span with a TapGestureRecognizer doesn't work correctly on iOS/MacCatalyst. The tap hitbox is vertically offset from where the text is actually drawn — tapping the visible text does nothing, tapping below it fires the event.

### Description of Change
NSLayoutManager.UsesFontLeading is now set to false for span hitbox calculation on iOS 16+ and MacCatalyst 16+ so TextKit uses line metrics that align with the CoreText/UILabel-rendered text, eliminating the vertical offset.

The text container size is also updated to use finalSize (MAUI's layout size) instead of control.Bounds, which can be zero/stale during layout on Mac Catalyst.

The regression test adds a FormattedString Label that uses the STAT-table custom font and taps the rendered "Click me" Span at its visual center. The same visual-center tap formula is used for iOS and MacCatalyst so the test verifies the hitbox alignment instead of compensating for an offset.

### Issues Fixed
Fixes #36505

**Tested the behavior in the following platforms.**
- [ ] Android
- [ ] Windows
- [x] iOS
- [ ] Mac

| Before  | After  |
|---------|--------|
| **iOS**<br> <video src="https://github.com/user-attachments/assets/622bbc78-ea45-48a3-8433-53a6ed2b1ce9" width="300" height="600"> | **iOS**<br> <video src="https://github.com/user-attachments/assets/e42b373b-6d5f-4982-8106-fd4f66ea0aff" width="300" height="600"> |

🏁 Report — Final Recommendation

Comparative Report — PR #36564

Ranking

Rank Candidate Regression result Assessment
1 pr-plus-reviewer PASS-equivalent for requested iOS path; no iOS behavior change beyond raw PR, and applies reviewer fixes Best candidate. Preserves the passing PR/try-fix-3 TextKit approach, extends the production fix to MacCatalyst, and removes the MacCatalyst test workaround that could mask the bug.
2 try-fix-3 PASS on iOS; MacCatalyst build passed, runtime launch environment-blocked Strong production fix. It corrects the MacCatalyst UsesFontLeading gate while preserving iOS behavior, but it does not address the test's MacCatalyst half-line compensation.
3 pr PASS on iOS gate Good iOS fix, but incomplete for MacCatalyst because .ios.cs also compiles there and OperatingSystem.IsIOSVersionAtLeast(16) remains false on MacCatalyst.
4 try-fix-2 FAIL CoreText rewrite built and ran, but the iOS UI test still failed to trigger the span tap. It is also higher risk for truncation, multi-line ranges, RTL, and composed-character cases.
5 try-fix-1 FAIL UILabel/TextKit offset approach did not compile because the target binding lacks NSLayoutManager.GetUsedRectForTextContainer.

Candidate analysis

pr

The raw PR fix addresses the demonstrated iOS failure by disabling TextKit font leading on iOS 16+ and using MAUI finalSize for the text container. The provided gate confirms the new iOS test catches the issue and passes with the PR fix. However, the same implementation file participates in MacCatalyst builds; the iOS-only runtime check leaves MacCatalyst on the old TextKit metric behavior while the PR describes and marks Mac coverage.

pr-plus-reviewer

This candidate applies the expert reviewer's actionable feedback to the raw PR. It keeps the focused TextKit approach and finalSize sizing, adds OperatingSystem.IsMacCatalystVersionAtLeast(16) to the font-leading gate, and removes the MacCatalyst-specific test tap offset. This is the smallest candidate that addresses both the verified iOS regression and the reviewed MacCatalyst correctness gap.

try-fix-1

This candidate attempted to keep default TextKit font leading and adjust span rectangles by the difference between UILabel's drawn text rect and TextKit's used rect. It failed compilation because NSLayoutManager.GetUsedRectForTextContainer is not available in the target bindings, so it cannot be selected.

try-fix-2

This candidate replaced the TextKit hitbox calculation with a CoreText CTFrame/CTLine implementation. It compiled after iteration but failed the iOS regression test, so it must rank below every passing candidate. Its broader rewrite also has more regression surface than the PR's localized TextKit metric adjustment.

try-fix-3

This candidate keeps the PR's TextKit approach and corrects the runtime platform gate for both iOS 16+ and MacCatalyst 16+. It passed the requested iOS test and compiled for MacCatalyst, but the MacCatalyst Appium run was environment-blocked. It is better than the raw PR, but pr-plus-reviewer is stronger because it also removes the test compensation that could hide the MacCatalyst bug.

Winner

Winner: pr-plus-reviewer.

It is the single best candidate because it preserves the only passing production approach, fixes the MacCatalyst gap found by expert review, and aligns the UI test with the intended behavior. The failed try-fix candidates rank lower per the required rule, and the raw PR ranks below candidates that address the unresolved MacCatalyst issue.


🧭 Next Steps — review latest findings

No alternative fix was selected for this run. Review the session findings and CI results before merging.

@MauiBot MauiBot removed the s/agent-review-in-progress AI review is currently running for this PR label Jul 14, 2026
@devanathan-vaithiyanathan devanathan-vaithiyanathan changed the title [WIP][iOS] Fix Span TapGestureRecognizer hitbox misalignment in FormattedString when font contains a STAT table [iOS] Fix Span TapGestureRecognizer hitbox misalignment in FormattedString when font contains a STAT table Jul 15, 2026
@devanathan-vaithiyanathan
devanathan-vaithiyanathan marked this pull request as ready for review July 15, 2026 07:46
Copilot AI review requested due to automatic review settings July 15, 2026 07:46
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes iOS (and Mac Catalyst) Span TapGestureRecognizer hit-testing for Label.FormattedText when custom fonts contain an OpenType STAT table, by making the hitbox layout use the same line metrics as the text rendering path.

Changes:

  • Adjusts iOS NSLayoutManager configuration to avoid font-leading-driven line-metric mismatches and updates text-container sizing to prefer MAUI’s finalSize.
  • Adds a HostApp repro page (Issue36505) using a custom STAT font and a tappable Span.
  • Adds an Appium UI test to validate that tapping the visually rendered span triggers the recognizer.

Reviewed changes

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

File Description
src/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cs Updates span-rect calculation to better match rendered text metrics and uses MAUI layout size for container sizing.
src/Controls/tests/TestCases.HostApp/MauiProgram.cs Registers the custom STAT font with the test HostApp.
src/Controls/tests/TestCases.HostApp/Issues/Issue36505.cs Adds the repro UI for the formatted-string span tap hitbox issue.
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36505.cs Adds an Appium regression test that taps the expected span location and asserts success.

Comment on lines +223 to +234
// On iOS 16+, NSLayoutManager's default UsesFontLeading=true causes it to include
// font leading (extra line spacing) from the OS/2 typographic metrics that CoreText
// uses when a font has an OpenType STAT table. This makes the layout manager compute
// line heights that don't match what CoreText uses to draw the glyphs, resulting in
// span tap hitboxes being vertically offset from the rendered text.
// Disabling UsesFontLeading on iOS 16+ makes NSLayoutManager match CoreText's metrics
// so the calculated span rects align with the actual rendered text positions.
// See: https://github.com/dotnet/maui/issues/36505
using var layoutManager = new NSLayoutManager
{
UsesFontLeading = !OperatingSystem.IsIOSVersionAtLeast(16)
};
Comment on lines +38 to +45
void TapClickMe(float x, float y, float visualLineHeight, float lineCenterOffset)
{
#if MACCATALYST
App.TapCoordinates(x, y + (visualLineHeight * 15) + lineCenterOffset - visualLineHeight * 0.5f);
#else
App.TapCoordinates(x, y + (visualLineHeight * 15) + lineCenterOffset);
#endif
}
kubaflo pushed a commit that referenced this pull request Jul 15, 2026
The 'Download drop-deep-uitests' step in UpdateAISummaryComment gated on the deep
stage's RESULT (runDeepUITestsResult). That cross-stage value is unreliable and
has hidden real results twice:
  - empty via dependencies.RunDeepUITests.result (14651727/#36564), and
  - spuriously 'Skipped' via stageDependencies.RunDeepUITests.result
    (14652616/#35156: 9 categories ran and published drop-deep-uitests, but the
    summary posted 'No UI test results were produced' because the download was
    skipped -> artDir absent -> review-only summary).

Gate the download on the SAME signal the deep stage itself uses to decide whether
to run: ReviewPR's detectedCategories output (present and not NONE/ALL). ReviewPR
always runs, so that output is dependable across the stage boundary. Now the
artifact is downloaded exactly when the deep stage ran and published it, so real
results are always surfaced; a genuinely-skipped deep stage still skips the
download cleanly (no spurious yellow). continueOnError remains as defense-in-depth.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 15d2af20-e4ab-4e88-9011-cfbd83513bc0
@kubaflo
kubaflo changed the base branch from main to inflight/current July 15, 2026 14:56
@kubaflo
kubaflo force-pushed the inflight/current branch from 0651f27 to 84d8b21 Compare July 15, 2026 15:10
@kubaflo
kubaflo merged commit 67c9077 into dotnet:inflight/current Jul 15, 2026
34 of 44 checks passed
@github-actions github-actions Bot added this to the .NET 10 SR10 milestone Jul 15, 2026
kubaflo pushed a commit that referenced this pull request Jul 22, 2026
…tring when font contains a STAT table (#36564)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

### Issue Details
When a Label uses FormattedText with a custom font that has an OpenType
STAT table, tapping a Span with a TapGestureRecognizer doesn't work
correctly on iOS. The tap hitbox is vertically offset from where the
text is actually drawn — tapping the visible text does nothing, tapping
below it fires the event.


### Description of Change

<!-- Enter description of the fix in this section -->
NSLayoutManager.UsesFontLeading is now set to false on iOS 16+ so the
tap hitbox calculation uses the same line metrics that CoreText uses to
render the text, eliminating the vertical offset. The container size is
also updated to use finalSize (MAUI's layout size) instead of
control.Bounds, which can be zero/stale during layout on Mac Catalyst.

### 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 #36505 

<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->

**Tested the behavior in the following platforms.**
- [ ] Android
- [ ] Windows
- [x] iOS
- [x] Mac


| Before  | After  |
|---------|--------|
| **iOS**<br> <video
src="https://github.com/user-attachments/assets/622bbc78-ea45-48a3-8433-53a6ed2b1ce9"
width="300" height="600"> | **iOS**<br> <video
src="https://github.com/user-attachments/assets/e42b373b-6d5f-4982-8106-fd4f66ea0aff"
width="300" height="600"> |
kubaflo pushed a commit that referenced this pull request Jul 28, 2026
…tring when font contains a STAT table (#36564)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

### Issue Details
When a Label uses FormattedText with a custom font that has an OpenType
STAT table, tapping a Span with a TapGestureRecognizer doesn't work
correctly on iOS. The tap hitbox is vertically offset from where the
text is actually drawn — tapping the visible text does nothing, tapping
below it fires the event.


### Description of Change

<!-- Enter description of the fix in this section -->
NSLayoutManager.UsesFontLeading is now set to false on iOS 16+ so the
tap hitbox calculation uses the same line metrics that CoreText uses to
render the text, eliminating the vertical offset. The container size is
also updated to use finalSize (MAUI's layout size) instead of
control.Bounds, which can be zero/stale during layout on Mac Catalyst.

### 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 #36505 

<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->

**Tested the behavior in the following platforms.**
- [ ] Android
- [ ] Windows
- [x] iOS
- [x] Mac


| Before  | After  |
|---------|--------|
| **iOS**<br> <video
src="https://github.com/user-attachments/assets/622bbc78-ea45-48a3-8433-53a6ed2b1ce9"
width="300" height="600"> | **iOS**<br> <video
src="https://github.com/user-attachments/assets/e42b373b-6d5f-4982-8106-fd4f66ea0aff"
width="300" height="600"> |
kubaflo pushed a commit that referenced this pull request Jul 29, 2026
…tring when font contains a STAT table (#36564)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

### Issue Details
When a Label uses FormattedText with a custom font that has an OpenType
STAT table, tapping a Span with a TapGestureRecognizer doesn't work
correctly on iOS. The tap hitbox is vertically offset from where the
text is actually drawn — tapping the visible text does nothing, tapping
below it fires the event.


### Description of Change

<!-- Enter description of the fix in this section -->
NSLayoutManager.UsesFontLeading is now set to false on iOS 16+ so the
tap hitbox calculation uses the same line metrics that CoreText uses to
render the text, eliminating the vertical offset. The container size is
also updated to use finalSize (MAUI's layout size) instead of
control.Bounds, which can be zero/stale during layout on Mac Catalyst.

### 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 #36505 

<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->

**Tested the behavior in the following platforms.**
- [ ] Android
- [ ] Windows
- [x] iOS
- [x] Mac


| Before  | After  |
|---------|--------|
| **iOS**<br> <video
src="https://github.com/user-attachments/assets/622bbc78-ea45-48a3-8433-53a6ed2b1ce9"
width="300" height="600"> | **iOS**<br> <video
src="https://github.com/user-attachments/assets/e42b373b-6d5f-4982-8106-fd4f66ea0aff"
width="300" height="600"> |
kubaflo pushed a commit that referenced this pull request Aug 7, 2026
…tring when font contains a STAT table (#36564)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

### Issue Details
When a Label uses FormattedText with a custom font that has an OpenType
STAT table, tapping a Span with a TapGestureRecognizer doesn't work
correctly on iOS. The tap hitbox is vertically offset from where the
text is actually drawn — tapping the visible text does nothing, tapping
below it fires the event.


### Description of Change

<!-- Enter description of the fix in this section -->
NSLayoutManager.UsesFontLeading is now set to false on iOS 16+ so the
tap hitbox calculation uses the same line metrics that CoreText uses to
render the text, eliminating the vertical offset. The container size is
also updated to use finalSize (MAUI's layout size) instead of
control.Bounds, which can be zero/stale during layout on Mac Catalyst.

### 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 #36505 

<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->

**Tested the behavior in the following platforms.**
- [ ] Android
- [ ] Windows
- [x] iOS
- [x] Mac


| Before  | After  |
|---------|--------|
| **iOS**<br> <video
src="https://github.com/user-attachments/assets/622bbc78-ea45-48a3-8433-53a6ed2b1ce9"
width="300" height="600"> | **iOS**<br> <video
src="https://github.com/user-attachments/assets/e42b373b-6d5f-4982-8106-fd4f66ea0aff"
width="300" height="600"> |
kubaflo pushed a commit that referenced this pull request Aug 12, 2026
…tring when font contains a STAT table (#36564)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

### Issue Details
When a Label uses FormattedText with a custom font that has an OpenType
STAT table, tapping a Span with a TapGestureRecognizer doesn't work
correctly on iOS. The tap hitbox is vertically offset from where the
text is actually drawn — tapping the visible text does nothing, tapping
below it fires the event.


### Description of Change

<!-- Enter description of the fix in this section -->
NSLayoutManager.UsesFontLeading is now set to false on iOS 16+ so the
tap hitbox calculation uses the same line metrics that CoreText uses to
render the text, eliminating the vertical offset. The container size is
also updated to use finalSize (MAUI's layout size) instead of
control.Bounds, which can be zero/stale during layout on Mac Catalyst.

### 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 #36505 

<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->

**Tested the behavior in the following platforms.**
- [ ] Android
- [ ] Windows
- [x] iOS
- [x] Mac


| Before  | After  |
|---------|--------|
| **iOS**<br> <video
src="https://github.com/user-attachments/assets/622bbc78-ea45-48a3-8433-53a6ed2b1ce9"
width="300" height="600"> | **iOS**<br> <video
src="https://github.com/user-attachments/assets/e42b373b-6d5f-4982-8106-fd4f66ea0aff"
width="300" height="600"> |
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 15, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-controls-label Label, Span community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration platform/ios platform/macos macOS / Mac Catalyst s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-gate-passed AI verified tests catch the bug (fail without fix, pass with fix) s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[iOS] Span TapGestureRecognizer hitbox is mispositioned inside FormattedString when the font contains a STAT table (line-height/leading mismatch)

5 participants