Fix MauiView leaks detached platform views when SafeAreaEdges includes SoftInput - #35586
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35586Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35586" |
|
Hey there @@KarthikRajaKalaimani! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
|
/review -b feature/refactor-copilot-yml |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 2 findings
See inline comments for details.
| { | ||
| App.WaitForElement("statusLabel"); | ||
| Assert.That( | ||
| App.WaitForTextToBePresentInElement("statusLabel", "Suspect SafeAreaEdges.SoftInput: virtual=0/12, handler=0/12, platform=0/12", timeout: TimeSpan.FromSeconds(25)), |
There was a problem hiding this comment.
[moderate] Test Quality — The 25s timeout is tight for this page: it runs two 12-cycle scenarios with fixed waits plus repeated forced GC/finalizer cycles. On iOS CI/device runs, GC and UI scheduling can easily push this past 25s even when the fix is correct. Increase the timeout or signal completion more deterministically to avoid flaking unrelated to the leak.
This comment has been minimized.
This comment has been minimized.
|
/review -b feature/refactor-copilot-yml |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 1 findings
See inline comments for details.
kubaflo
left a comment
There was a problem hiding this comment.
Could you please check the ai's suggestions?
|
/review -b feature/refactor-copilot-yml |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 1 findings
See inline comments for details.
kubaflo
left a comment
There was a problem hiding this comment.
Could you please check the ai's suggestions?
… it) (dotnet#35714) <!-- 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! Follow-up to @kubaflo's review on dotnet#35687. ## What In `eng/pipelines/ci-copilot.yml`, when `parameters.Platform == 'android'`, the Android AVD was being created twice: 1. **First**, via `common/provision.yml` running the `ProvisionAndroidSdkAvdCreateAvds` MSBuild target — because `skipAndroidCreateAvds: ${{ ne(parameters.Platform, 'android') }}` evaluated to `false` for Android. That target invokes `dotnet android avd create --name "Emulator_30" … --force`. 2. **Then**, the inline `Create AVD and boot Android Emulator` script ran `avdmanager create avd -n Emulator_30 -k "system-images;android-30;google_apis_playstore;x86_64" --device "Nexus 5X" --force`. Both create the same AVD name with `--force`, so the second silently overwrites the first — no error, just ~30–60s wasted on every Copilot review pipeline run for Android. The inline script is the canonical source of truth: it pins the `google_apis_playstore` image variant, the `Nexus 5X` device profile, the `disk.dataPartition.size=2048m` shrink, and ADB key pre-auth. None of those are applied by `ProvisionAndroidSdkAvdCreateAvds`. So the right fix is to skip the provision step entirely and let the inline script own AVD creation. ## Change Pinned `skipAndroidCreateAvds: true` (with an explanatory comment) at both call sites of `common/provision.yml` in `ci-copilot.yml` (the ReviewPR stage and the Deep stage). The inline `avdmanager` blocks are untouched. This is the AVD-creation portion of dotnet#35376 being reverted — the inline script that same PR added already handles AVD creation, so the provision-step AVD creation was redundant. ## Scope This change is scoped to **`ci-copilot.yml`** only — the Copilot review pipeline. It does **not** touch the required gating pipelines: - `maui-pr` - `maui-pr-devicetests` - `maui-pr-uitests` ## Follow-up Needs to be ported to `net11.0` afterward via the automated `merge/main-to-net11.0` flow. Co-authored-by: bot <bot@test> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
17f55fc to
7e43412
Compare
addressed Ai summary concerns |
|
/review -b feature/enhanced-reviewer |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 1 findings
See inline comments for details.
| } | ||
| else | ||
| { | ||
| UnsubscribeFromKeyboardNotifications(); |
There was a problem hiding this comment.
[major] Safe Area and Window Insets - This unsubscribe path now handles both detached views and attached runtime transitions away from SoftInput, but UnsubscribeFromKeyboardNotifications() clears _isKeyboardShowing without scheduling layout. In the attached case, for example when keyboard is open and SafeAreaEdges changes from SoftInput or All to Container or None, the old keyboard safe-area padding can remain applied until some unrelated layout pass, regressing the stale-padding fix from issue 34846 and PR 35083. Please distinguish the detach case, where Window is null and clearing without layout avoids retaining detached views, from the attached no-SoftInput case, which should clear through ClearKeyboardState()/SetNeedsLayout().
MauiBot
left a comment
There was a problem hiding this comment.
AI Review Summary
@KarthikRajaKalaimani — new AI review results are available based on this last commit:
7e43412.
Improved fix and test case To request a fresh review after new comments or commits, comment/review rerun.
Review Sessions — click to expand
Gate — Test Before & After Fix
Gate Result: ✅ PASSED
Platform: IOS · Base: main · Merge base: e904e900
| Test | Without Fix (expect FAIL) | With Fix (expect PASS) |
|---|---|---|
🖥️ Issue35386 Issue35386 |
✅ FAIL — 352s | ✅ PASS — 132s |
🔴 Without fix — 🖥️ Issue35386: FAIL ✅ · 352s
Determining projects to restore...
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 533 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 623 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 6.06 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/Foldable/src/Controls.Foldable.csproj (in 6.22 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Core/maps/src/Maps.csproj (in 6.23 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj (in 6.24 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/Maps/src/Controls.Maps.csproj (in 6.24 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 6.24 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Xaml/Controls.Xaml.csproj (in 6.24 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/BlazorWebView/src/Maui/Microsoft.AspNetCore.Components.WebView.Maui.csproj (in 6.26 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 6.27 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.80-ci+azdo.14270477
Graphics -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Graphics/Debug/net10.0-ios26.0/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14270477
Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0-ios26.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14270477
Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Core/Debug/net10.0-ios26.0/Microsoft.Maui.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14270477
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.80-ci+azdo.14270477
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.80-ci+azdo.14270477
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14270477
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14270477
Controls.Maps -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Maps.dll
Controls.Foldable -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Foldable.dll
Controls.Xaml -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Xaml.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14270477
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:38.84
Determining projects to restore...
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 733 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/tests/CustomAttributes/Controls.CustomAttributes.csproj (in 733 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.Core/UITest.Core.csproj (in 733 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 758 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 754 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/VisualTestUtils/VisualTestUtils.csproj (in 3 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 829 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 857 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.NUnit/UITest.NUnit.csproj (in 1.48 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.Appium/UITest.Appium.csproj (in 1.95 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.Analyzers/UITest.Analyzers.csproj (in 2.3 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/VisualTestUtils.MagickNet/VisualTestUtils.MagickNet.csproj (in 3.75 sec).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.iOS.Tests/Controls.TestCases.iOS.Tests.csproj (in 3.78 sec).
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14270477
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.80-ci+azdo.14270477
Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14270477
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.80-ci+azdo.14270477
Controls.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
UITest.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
VisualTestUtils -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
VisualTestUtils.MagickNet -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
UITest.NUnit -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
UITest.Appium -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
UITest.Analyzers -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
Controls.TestCases.iOS.Tests -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
Test run for /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (arm64)
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[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.07] Discovering: Controls.TestCases.iOS.Tests
[xUnit.net 00:00:00.19] Discovered: Controls.TestCases.iOS.Tests
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 6/3/2026 5:01:50 AM FixtureSetup for Issue35386(iOS)
>>>>> 6/3/2026 5:01:54 AM SoftInputSafeArea_DetachedPlatformViews_DoNotLeak Start
>>>>> 6/3/2026 5:02:55 AM SoftInputSafeArea_DetachedPlatformViews_DoNotLeak Stop
>>>>> 6/3/2026 5:02:55 AM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
Failed SoftInputSafeArea_DetachedPlatformViews_DoNotLeak [1 m 1 s]
Error Message:
The status label did not reach the expected SoftInput summary text.
Assert.That(App.WaitForTextToBePresentInElement("statusLabel", "Suspect SafeAreaEdges.SoftInput: virtual=0/12, handler=0/12, platform=0/12", timeout: TimeSpan.FromSeconds(60)), Is.True)
Expected: True
But was: False
Stack Trace:
at Microsoft.Maui.TestCases.Tests.Issues.Issue35386.SoftInputSafeArea_DetachedPlatformViews_DoNotLeak() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35386.cs:line 19
1) at Microsoft.Maui.TestCases.Tests.Issues.Issue35386.SoftInputSafeArea_DetachedPlatformViews_DoNotLeak() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35386.cs:line 19
NUnit Adapter 4.5.0.0: Test execution complete
Results File: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue35386.trx
Total tests: 1
Test Run Failed.
Failed: 1
Total time: 2.4051 Minutes
>>> TRX_RESULT_FILE: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue35386.trx
🟢 With fix — 🖥️ Issue35386: PASS ✅ · 132s
Determining projects to restore...
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 376 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 399 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 404 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 448 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 463 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.80-ci+azdo.14270477
Graphics -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Graphics/Debug/net10.0-ios26.0/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14270477
Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0-ios26.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14270477
Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Core/Debug/net10.0-ios26.0/Microsoft.Maui.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14270477
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.80-ci+azdo.14270477
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.80-ci+azdo.14270477
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14270477
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14270477
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
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14270477
Controls.Xaml -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Xaml.dll
Controls.Maps -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Maps.dll
Detected signing identity:
Code Signing Key: "" (-)
Provisioning Profile: "" () - no entitlements
Bundle Id: com.microsoft.maui.uitests
App Id: com.microsoft.maui.uitests
Controls.TestCases.HostApp -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-ios/iossimulator-arm64/Controls.TestCases.HostApp.dll
Optimizing assemblies for size may change the behavior of the app. Be sure to test after publishing. See: https://aka.ms/dotnet-illink
Optimizing assemblies for size. This process might take a while.
Build succeeded.
/Users/cloudtest/vss/_work/1/s/.dotnet/packs/Microsoft.iOS.Sdk.net10.0_26.0/26.0.11017/targets/Xamarin.Shared.Sdk.targets(309,3): warning : RuntimeIdentifier was set on the command line, and will override the value for RuntimeIdentifiers set in the project file. [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-ios]
1 Warning(s)
0 Error(s)
Time Elapsed 00:00:55.81
Determining projects to restore...
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 455 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 464 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 471 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 498 ms).
Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 463 ms).
8 of 13 projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14270477
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.80-ci+azdo.14270477
Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14270477
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.80-ci+azdo.14270477
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
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.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.17] 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
>>>>> 6/3/2026 5:04:39 AM FixtureSetup for Issue35386(iOS)
>>>>> 6/3/2026 5:04:44 AM SoftInputSafeArea_DetachedPlatformViews_DoNotLeak Start
>>>>> 6/3/2026 5:05:08 AM SoftInputSafeArea_DetachedPlatformViews_DoNotLeak Stop
Passed SoftInputSafeArea_DetachedPlatformViews_DoNotLeak [24 s]
NUnit Adapter 4.5.0.0: Test execution complete
Results File: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue35386.trx
Test Run Successful.
Total tests: 1
Passed: 1
Total time: 45.3598 Seconds
>>> TRX_RESULT_FILE: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue35386.trx
📁 Fix files reverted (2 files)
eng/pipelines/ci-copilot.ymlsrc/Core/src/Platform/iOS/MauiView.cs
UI Tests — SafeAreaEdges,ViewBaseTests
Detected UI test categories: SafeAreaEdges,ViewBaseTests
❌ Deep UI tests — 77 passed, 103 failed across 2 categories on platform-pool agent (replaces in-process counts above).
🧪 UI Test Execution Results (deep, platform pool)
| Category | Tests | Snapshot diffs |
|---|---|---|
SafeAreaEdges |
64/68 (4 ❌) | 4 diff PNGs |
ViewBaseTests |
13/112 (99 ❌) | 99 diff PNGs |
❌ SafeAreaEdges — 4 failed tests
LayoutShouldBeCorrectOnFirstNavigation
System.InvalidOperationException :
Snapshot different than baseline: LayoutShouldBeCorrectOnFirstNavigation.png (size differs - baseline is 1124x2286 pixels, actual is 1206x2472 pixels)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
iOS 26 visual tests require an iPhone 11 Pro simulator for correct screen resolution.
To create the simulator, run:
xcrun simctl create "iPhone 11 Pro" com.apple.CoreSimulator.SimDeviceType.iPhone-11-Pro com.apple.CoreSimulator.SimRuntime.iOS-26-0
Then run the tests targeting the new simulator.
----> VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: LayoutShouldBeCorrectOnFirstNavigation.png (size differs - baseline is 1124x2286 pixels, actual is 1206x2472 pixels)
If the correct baseline has changed (this isn't a a bug), then update the baseline image
...
ToolbarExtendsAllTheWayLeftAndRight_FlyoutPage
System.InvalidOperationException :
Snapshot different than baseline: ToolbarExtendsAllTheWayLeftAndRight_FlyoutPage.png (size differs - baseline is 2436x974 pixels, actual is 2622x1056 pixels)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
iOS 26 visual tests require an iPhone 11 Pro simulator for correct screen resolution.
To create the simulator, run:
xcrun simctl create "iPhone 11 Pro" com.apple.CoreSimulator.SimDeviceType.iPhone-11-Pro com.apple.CoreSimulator.SimRuntime.iOS-26-0
Then run the tests targeting the new simulator.
----> VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: ToolbarExtendsAllTheWayLeftAndRight_FlyoutPage.png (size differs - baseline is 2436x974 pixels, actual is 2622x1056 pixels)
If the correct baseline has changed (this isn't a a bug), then update the
...
ToolbarExtendsAllTheWayLeftAndRight_NavigationPage
System.InvalidOperationException :
Snapshot different than baseline: ToolbarExtendsAllTheWayLeftAndRight_NavigationPage.png (size differs - baseline is 2436x974 pixels, actual is 2622x1056 pixels)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
iOS 26 visual tests require an iPhone 11 Pro simulator for correct screen resolution.
To create the simulator, run:
xcrun simctl create "iPhone 11 Pro" com.apple.CoreSimulator.SimDeviceType.iPhone-11-Pro com.apple.CoreSimulator.SimRuntime.iOS-26-0
Then run the tests targeting the new simulator.
----> VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: ToolbarExtendsAllTheWayLeftAndRight_NavigationPage.png (size differs - baseline is 2436x974 pixels, actual is 2622x1056 pixels)
If the correct baseline has changed (this isn't a a bug), then upd
...
ToolbarExtendsAllTheWayLeftAndRight_Shell
System.InvalidOperationException :
Snapshot different than baseline: ToolbarExtendsAllTheWayLeftAndRight_Shell.png (size differs - baseline is 2436x974 pixels, actual is 2622x1056 pixels)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
iOS 26 visual tests require an iPhone 11 Pro simulator for correct screen resolution.
To create the simulator, run:
xcrun simctl create "iPhone 11 Pro" com.apple.CoreSimulator.SimDeviceType.iPhone-11-Pro com.apple.CoreSimulator.SimRuntime.iOS-26-0
Then run the tests targeting the new simulator.
----> VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: ToolbarExtendsAllTheWayLeftAndRight_Shell.png (size differs - baseline is 2436x974 pixels, actual is 2622x1056 pixels)
If the correct baseline has changed (this isn't a a bug), then update the baseline i
...
❌ ViewBaseTests — 99 failed tests
Image_ClipWithComplexPolyLineGeometry
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.ClipFeatureTests.Image_ClipWithComplexPolyLineGeometry() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ClipFeatureTests.cs:line 1026
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Ref
...
Image_ClipWithRectangleGeometry
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.ClipFeatureTests.Image_ClipWithRectangleGeometry() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ClipFeatureTests.cs:line 287
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection
...
BoxView_ClipWithCornerRadius
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.ClipFeatureTests.BoxView_ClipWithCornerRadius() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ClipFeatureTests.cs:line 176
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.Ru
...
Border_ClipWithStrokeShapeRoundRectangle
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.ClipFeatureTests.Border_ClipWithStrokeShapeRoundRectangle() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ClipFeatureTests.cs:line 109
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.R
...
DefaultContentWithFlowDirection
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.ContentViewFeatureTests.DefaultContentWithFlowDirection() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ContentViewFeatureTests.cs:line 172
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at Sys
...
ImageButton_ClipWithShadow
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.ClipFeatureTests.ImageButton_ClipWithShadow() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ClipFeatureTests.cs:line 662
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.Runt
...
LightTheme_EditorAndPlaceholderColor_VerifyVisualState
System.InvalidOperationException :
Snapshot different than baseline: LightTheme_EditorAndPlaceholderColor_VerifyVisualState.png (size differs - baseline is 1124x1126 pixels, actual is 1206x1312 pixels)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
iOS 26 visual tests require an iPhone 11 Pro simulator for correct screen resolution.
To create the simulator, run:
xcrun simctl create "iPhone 11 Pro" com.apple.CoreSimulator.SimDeviceType.iPhone-11-Pro com.apple.CoreSimulator.SimRuntime.iOS-26-0
Then run the tests targeting the new simulator.
----> VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: LightTheme_EditorAndPlaceholderColor_VerifyVisualState.png (size differs - baseline is 1124x1126 pixels, actual is 1206x1312 pixels)
If the correct baseline has changed (this isn't a a bug)
...
SecondCustomPageWithFlowDirectionChanged
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.ContentViewFeatureTests.SecondCustomPageWithFlowDirectionChanged() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ContentViewFeatureTests.cs:line 333
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
...
VisualTransform_AnchorX_ScaleYWithRotation
System.InvalidOperationException :
Snapshot different than baseline: VisualTransform_AnchorX_ScaleYWithRotation.png (size differs - baseline is 1124x2286 pixels, actual is 1206x2472 pixels)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
iOS 26 visual tests require an iPhone 11 Pro simulator for correct screen resolution.
To create the simulator, run:
xcrun simctl create "iPhone 11 Pro" com.apple.CoreSimulator.SimDeviceType.iPhone-11-Pro com.apple.CoreSimulator.SimRuntime.iOS-26-0
Then run the tests targeting the new simulator.
----> VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: VisualTransform_AnchorX_ScaleYWithRotation.png (size differs - baseline is 1124x2286 pixels, actual is 1206x2472 pixels)
If the correct baseline has changed (this isn't a a bug), then update the baseli
...
VisualTransform_ScaleWithAnchorXAndRotationY
System.InvalidOperationException :
Snapshot different than baseline: VisualTransform_ScaleWithAnchorXAndRotationY.png (size differs - baseline is 1124x2286 pixels, actual is 1206x2472 pixels)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
iOS 26 visual tests require an iPhone 11 Pro simulator for correct screen resolution.
To create the simulator, run:
xcrun simctl create "iPhone 11 Pro" com.apple.CoreSimulator.SimDeviceType.iPhone-11-Pro com.apple.CoreSimulator.SimRuntime.iOS-26-0
Then run the tests targeting the new simulator.
----> VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: VisualTransform_ScaleWithAnchorXAndRotationY.png (size differs - baseline is 1124x2286 pixels, actual is 1206x2472 pixels)
If the correct baseline has changed (this isn't a a bug), then update the ba
...
VisualTransform_RotationXWithScaleX
System.InvalidOperationException :
Snapshot different than baseline: VisualTransform_RotationXWithScaleX.png (size differs - baseline is 1124x2286 pixels, actual is 1206x2472 pixels)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
iOS 26 visual tests require an iPhone 11 Pro simulator for correct screen resolution.
To create the simulator, run:
xcrun simctl create "iPhone 11 Pro" com.apple.CoreSimulator.SimDeviceType.iPhone-11-Pro com.apple.CoreSimulator.SimRuntime.iOS-26-0
Then run the tests targeting the new simulator.
----> VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: VisualTransform_RotationXWithScaleX.png (size differs - baseline is 1124x2286 pixels, actual is 1206x2472 pixels)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See
...
VisualTransform_AnchorY_ScaleWithRotationY
System.InvalidOperationException :
Snapshot different than baseline: VisualTransform_AnchorY_ScaleWithRotationY.png (size differs - baseline is 1124x2286 pixels, actual is 1206x2472 pixels)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
iOS 26 visual tests require an iPhone 11 Pro simulator for correct screen resolution.
To create the simulator, run:
xcrun simctl create "iPhone 11 Pro" com.apple.CoreSimulator.SimDeviceType.iPhone-11-Pro com.apple.CoreSimulator.SimRuntime.iOS-26-0
Then run the tests targeting the new simulator.
----> VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: VisualTransform_AnchorY_ScaleWithRotationY.png (size differs - baseline is 1124x2286 pixels, actual is 1206x2472 pixels)
If the correct baseline has changed (this isn't a a bug), then update the baseli
...
ContentView_ClipNull_NoCrash
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.ClipFeatureTests.ContentView_ClipNull_NoCrash() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ClipFeatureTests.cs:line 785
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.Ru
...
Border_ClipWithRotationAndScale
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.ClipFeatureTests.Border_ClipWithRotationAndScale() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ClipFeatureTests.cs:line 1086
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflectio
...
Border_ClipNull_NoCrash
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.ClipFeatureTests.Border_ClipNull_NoCrash() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ClipFeatureTests.cs:line 695
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.Runtime
...
Image_ClipWithBezierSegmentPath
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.ClipFeatureTests.Image_ClipWithBezierSegmentPath() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ClipFeatureTests.cs:line 383
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection
...
ImageButton_ClipWithRoundRectangleGeometry
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.ClipFeatureTests.ImageButton_ClipWithRoundRectangleGeometry() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ClipFeatureTests.cs:line 641
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System
...
DarkTheme_EditorAndPlaceholderColor_VerifyVisualState
System.InvalidOperationException :
Snapshot different than baseline: DarkTheme_EditorAndPlaceholderColor_VerifyVisualState.png (size differs - baseline is 1124x1126 pixels, actual is 1206x1312 pixels)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
iOS 26 visual tests require an iPhone 11 Pro simulator for correct screen resolution.
To create the simulator, run:
xcrun simctl create "iPhone 11 Pro" com.apple.CoreSimulator.SimDeviceType.iPhone-11-Pro com.apple.CoreSimulator.SimRuntime.iOS-26-0
Then run the tests targeting the new simulator.
----> VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: DarkTheme_EditorAndPlaceholderColor_VerifyVisualState.png (size differs - baseline is 1124x1126 pixels, actual is 1206x1312 pixels)
If the correct baseline has changed (this isn't a a bug),
...
VisualTransform_RotationWithRotationX
System.InvalidOperationException :
Snapshot different than baseline: VisualTransform_RotationWithRotationX.png (size differs - baseline is 1124x2286 pixels, actual is 1206x2472 pixels)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
iOS 26 visual tests require an iPhone 11 Pro simulator for correct screen resolution.
To create the simulator, run:
xcrun simctl create "iPhone 11 Pro" com.apple.CoreSimulator.SimDeviceType.iPhone-11-Pro com.apple.CoreSimulator.SimRuntime.iOS-26-0
Then run the tests targeting the new simulator.
----> VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: VisualTransform_RotationWithRotationX.png (size differs - baseline is 1124x2286 pixels, actual is 1206x2472 pixels)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
...
LightTheme_Editor_VerifyVisualState
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.AppThemeFeatureTests.LightTheme_Editor_VerifyVisualState() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/AppThemeFeatureTests.cs:line 276
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at Syste
...
LightTheme_VerifyVisualState
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.AppThemeFeatureTests.LightTheme_VerifyVisualState() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/AppThemeFeatureTests.cs:line 31
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
Image_ClipWithLineSegmentPath
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.ClipFeatureTests.Image_ClipWithLineSegmentPath() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ClipFeatureTests.cs:line 351
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.R
...
Button_ClipWithText
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.ClipFeatureTests.Button_ClipWithText() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ClipFeatureTests.cs:line 243
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.RuntimeMeth
...
FirstCustomPageWithFlowDirection
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.ContentViewFeatureTests.FirstCustomPageWithFlowDirection() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ContentViewFeatureTests.cs:line 245
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at Sy
...
DarkTheme_CheckBox_VerifyVisualState
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.AppThemeFeatureTests.DarkTheme_CheckBox_VerifyVisualState() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/AppThemeFeatureTests.cs:line 67
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
VisualTransform_IsVisible
System.InvalidOperationException :
Snapshot different than baseline: VisualTransform_IsVisible.png (size differs - baseline is 1124x2286 pixels, actual is 1206x2472 pixels)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
iOS 26 visual tests require an iPhone 11 Pro simulator for correct screen resolution.
To create the simulator, run:
xcrun simctl create "iPhone 11 Pro" com.apple.CoreSimulator.SimDeviceType.iPhone-11-Pro com.apple.CoreSimulator.SimRuntime.iOS-26-0
Then run the tests targeting the new simulator.
----> VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: VisualTransform_IsVisible.png (size differs - baseline is 1124x2286 pixels, actual is 1206x2472 pixels)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or d
...
DarkTheme_Slider_VerifyVisualState
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.AppThemeFeatureTests.DarkTheme_Slider_VerifyVisualState() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/AppThemeFeatureTests.cs:line 174
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System
...
Image_ClipWithPolyQuadraticBezierSegmentPath
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.ClipFeatureTests.Image_ClipWithPolyQuadraticBezierSegmentPath() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ClipFeatureTests.cs:line 447
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at Syst
...
Border_ClipWithShadow
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.ClipFeatureTests.Border_ClipWithShadow() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ClipFeatureTests.cs:line 130
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.RuntimeMe
...
DefaultContentWithHeightRequest
System.InvalidOperationException : Unable to extract difference percentage from exception message.
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.ContentViewFeatureTests.DefaultContentWithHeightRequest() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ContentViewFeatureTests.cs:line 119
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at Sys
...
(+69 more — see TRX in artifact)
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)
Regression Cross-Reference
🔍 Regression Cross-Reference
🟡 Overlaps with prior bug-fix PRs — same files modified, but no exact line revert detected.
| File | Fix PR | Fixed issue(s) |
|---|---|---|
src/Core/src/Platform/iOS/MauiView.cs |
#34024 | #32586, #33934, #33595, #34042 |
🧪 Regression Tests to Verify
These tests were added by the overlapping fix PRs. Running them to verify no side-effect regressions:
| Fix PR | Type | Test | Filter |
|---|---|---|---|
| #34024 | UITest | Issue28986_ParentChildTest | Issue28986_ParentChildTest |
| #34024 | UITest | Issue32586 | Issue32586 |
| #34024 | UITest | Issue33595 | Issue33595 |
| #34024 | UITest | Issue33934 | Issue33934 |
🧪 Regression Test Results
❌ FAILED — 0 passed, 4 failed, 0 skipped
| Fix PR | Test | Type | Result |
|---|---|---|---|
| #34024 | Issue28986_ParentChildTest | UITest | ❌ FAILED |
| #34024 | Issue32586 | UITest | ❌ FAILED |
| #34024 | Issue33595 | UITest | ❌ FAILED |
| #34024 | Issue33934 | UITest | ❌ FAILED |
Pre-Flight — Context & Validation
Issue: #35386 - MauiView leaks detached platform views when SafeAreaEdges includes SoftInput
PR: #35586 - Fix MauiView leaks detached platform views when SafeAreaEdges includes SoftInput
Platforms Affected: iOS, MacCatalyst
Files Changed: 1 implementation, 2 test
Key Findings
MauiViewsubscribes toUIKeyboard.WillShowNotificationandUIKeyboard.WillHideNotificationwhenSafeAreaEdgesincludesSoftInput; observer callbacks can retain detachedMauiViewinstances if tokens are not removed.- The PR fix unsubscribes when
Window == null, which breaks the notification-center retain chain on detach. - The PR also clears keyboard state without calling
SetNeedsLayout(), which is correct for detached views but less precise for attached runtime transitions away fromSoftInput. - GitHub CLI authentication was unavailable, so PR metadata/comments were gathered from public PR HTML and local branch diff instead of authenticated
gh pr view/review-comment APIs.
Code Review Summary
Verdict: NEEDS_DISCUSSION
Confidence: medium
Errors: 0 | Warnings: 1 | Suggestions: 0
Key code review findings:
src/Core/src/Platform/iOS/MauiView.cs:329- attached unsubscribe no longer requests layout when clearing visible-keyboard state; detach and attached runtime changes should use different cleanup semantics.367 -
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #35586 | Flatten so unsubscribes keyboard observers; clear stale keyboard state without layout. | PASSED (Gate pre-run) | src/Core/src/Platform/iOS/MauiView.cs |
Fixes leak, but uses the same no-layout cleanup for detached and attached unsubscribe paths. |
Code Review — Deep Analysis
Code PR #35586Review
Independent Assessment
What this changes: MauiView removes iOS/MacCatalyst keyboard notification observers when a view using SafeAreaEdges.SoftInput is no longer eligible for keyboard-aware safe-area adjustment, and the PR adds a UI leak regression test.
Inferred motivation: NSNotificationCenter observer tokens can retain callbacks that retain the MauiView; if detach does not remove those tokens, detached platform views stay alive.
Reconciliation with PR Narrative
Author claims: Detached MauiView instances leak because UpdateKeyboardSubscription() previously skipped all subscription management when Window == null; flattening the condition makes detach unsubscribe. The PR also clears keyboard state without scheduling layout while detached.
Agreement/disagreement: The leak root cause matches the code and the PR fix addresses it. The main tradeoff is that the PR's unconditional unsubscribe path suppresses SetNeedsLayout() for attached runtime transitions away from SoftInput, which can leave attached views with stale keyboard padding until another layout occurs.
Findings
Attached unsubscribe no longer requests layout when keyboard state is clearedWarning ####
src/Core/src/Platform/iOS/MauiView.cs:329-367
The PR changed UnsubscribeFromKeyboardNotifications() so clearing _isKeyboardShowing only sets _safeAreaInvalidated and no longer calls SetNeedsLayout(). That is appropriate for detached views, but for an attached view whose SafeAreaEdges changes away from SoftInput while the keyboard is visible, the old ClearKeyboardState() behavior requested layout immediately. A reason-aware detach path would preserve no-layout cleanup for detached views while keeping layout invalidation for attached runtime changes.
Devil's Advocate
The PR's existing tests and gate pass, and detached views should not schedule layout. The warning matters only for attached runtime SafeAreaEdges transitions while the keyboard is visible; however, this code already supports runtime subscription updates, and preserving attached layout invalidation is safer with minimal extra complexity.
Verdict: NEEDS_DISCUSSION
Confidence: medium
Summary: The PR fix resolves the leak, but a slightly more explicit attached-vs-detached unsubscribe model is more robust and avoids changing attached runtime layout behavior. No GitHub comments were posted because this run only writes local PR-agent artifacts.
Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix-1 | Reason-aware unsubscribe: detached cleanup removes keyboard observers and clears stale state without layout; attached cleanup preserves and . | PASS | 1 file | Better than PR fix because it preserves attached runtime layout behavior while still fixing the detach leak. |
| PR | PR #35586 | Flatten so any non-subscribed state, including detach, unsubscribes keyboard observers. | PASSED (Gate) | 1 implementation file, 2 test files | Original PR fix is correct for the leak but less precise about detached vs attached cleanup semantics. |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| maui-expert-reviewer | 1 | Yes | Recommended reason-aware unsubscribe as the best surgical alternative; other options included explicit MovedToWindow() cleanup, weak notification callbacks, and a subscription state machine. |
Exhausted: stopped because Candidate #1 passed the primary test and all mandatory regressions and is demonstrably better than the PR fix.No
Selected Fix: Candidate # preserves the PR's leak fix while retaining correct attached-view layout invalidation semantics.1
Report — Final Recommendation
Comparative Report — PR #35586
Candidate Ranking
| Rank | Candidate | Result | Assessment |
|---|---|---|---|
| 1 | pr-plus-reviewer |
PASS by equivalence to try-fix-1 evidence |
Best candidate. It keeps the PR's observer-unsubscribe leak fix and applies expert feedback to distinguish detached cleanup from attached runtime SoftInput cleanup. |
| 2 | try-fix-1 |
PASS | Functionally equivalent to pr-plus-reviewer and passed the primary Issue35386 test plus mandatory iOS safe-area regressions. Ranked below pr-plus-reviewer only because the requested expert-review phase applies this improvement as a PR-fix candidate. |
| 3 | pr |
Gate PASS, regression FAIL | Fixes the detached observer leak, but failed saved overlapping regression checks and has an expert finding: attached runtime unsubscribe clears keyboard state without SetNeedsLayout(), risking stale safe-area padding regressions. |
Analysis
pr correctly identifies the leak root cause: MauiView subscribes to UIKeyboard.WillShowNotification and WillHideNotification for SafeAreaEdges.SoftInput, and detached views must remove those notification observers to break the retain chain. The gate result confirms the raw PR makes the new Issue35386 leak test pass after failing without the fix.
The problem with pr is that it flattens all unsubscribe reasons into the same cleanup behavior. That is safe for detached views, where layout should not be scheduled, but less safe for attached views that stop using SoftInput while the keyboard is visible. Those attached views still need ClearKeyboardState() to request layout and remove keyboard-derived safe-area padding promptly. The saved regression-check result ranks pr lower because overlapping safe-area regression tests failed.
try-fix-1 implements the expert recommendation: reason-aware unsubscribe. Detached cleanup clears _keyboardFrame, _isKeyboardShowing, and _safeAreaInvalidated without layout; attached cleanup calls ClearKeyboardState(). Its saved result is PASS, including Issue35386, Issue28986_ParentChildTest, Issue32586, Issue33595, and Issue33934.
pr-plus-reviewer is the same reason-aware improvement applied as expert-review feedback to the PR fix. It is the single winning candidate because it preserves the submitted PR's intent, addresses the expert review finding, and inherits the passing regression evidence from the equivalent try-fix-1 candidate.
Winner
pr-plus-reviewer
Future Action — review latest findings
No alternative fix was selected for this run. Review the session findings and CI results before merging.
…s SoftInput (#35586) <!-- 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: Every MauiView that uses SafeAreaEdges.SoftInput stays alive in memory after being removed from the screen. Users saw platform=12/12 views never getting released in iOS and Mac platform. ### Root Cause: When a MAUI view is configured with SafeAreaEdges.SoftInput on iOS or MacCatalyst, MauiView.SubscribeToKeyboardNotifications() registers two observers with NSNotificationCenter.DefaultCenter — one for UIKeyboard.WillShowNotification and one for UIKeyboard.WillHideNotification. Each call to AddObserver returns an NSObject token that the notification center holds strongly. Because the callbacks (OnKeyboardWillShow, OnKeyboardWillHide) are instance methods, the delegate objects capture a strong reference to this — the MauiView instance. This creates an unbroken retain chain: NSNotificationCenter → NSObject token → delegate → MauiView. As long as those observer tokens are registered, the notification center transitively keeps every subscribed MauiView alive, regardless of whether the view has been removed from the visual tree, its handler disconnected, and all MAUI-side references dropped. The reason the tokens were never removed on detach comes down to a single guard condition in UpdateKeyboardSubscription(). The method was structured as if (Window != null) { ... }, meaning all subscription management — both subscribe and unsubscribe — was gated on the view being attached to a window. When MovedToWindow() fires with Window == null (the standard UIKit signal that a view has been removed from the hierarchy), UpdateKeyboardSubscription() was called but immediately fell through without doing anything. UnsubscribeFromKeyboardNotifications() was never reached, leaving the observer tokens live in the notification center indefinitely. The result, confirmed by the sandbox repro, was platform=12/12 alive after 12 forced GC cycles for views using SafeAreaEdges.SoftInput, versus platform=0/12 for the control case. ### Description of Change: The fix flattens the conditional in UpdateKeyboardSubscription() so that the else branch — which calls UnsubscribeFromKeyboardNotifications() — fires for all cases where the view should not be actively subscribed, including detach. The original nested structure if (Window != null) { if (ShouldSubscribe) subscribe; else unsubscribe; } is replaced with if (Window != null && ShouldSubscribeToKeyboardNotifications()) { SubscribeToKeyboardNotifications(); } else { UnsubscribeFromKeyboardNotifications(); }. Now when MovedToWindow() fires with Window == null, the condition evaluates to false and UnsubscribeFromKeyboardNotifications() is called, issuing NSNotificationCenter.DefaultCenter.RemoveObserver(token) for both keyboard observers. This severs the retain chain and allows the MauiView to be collected normally by the GC. The change is 9 lines, touches a single method, and introduces no new state or lifecycle hooks. **Tested the behavior in the following platforms:** - [x] Android - [ ] Windows - [x] iOS - [ ] Mac ### Reference: N/A ### Issues Fixed: Fixes #35386 ### Screenshots | Before | After | |---------|--------| | <img src="https://github.com/user-attachments/assets/cbbd452a-6565-4e0b-a17d-12c83204bfe5" Width="400" Height="600"/> | <img src="https://github.com/user-attachments/assets/3ebbeba2-9fd1-42ac-a4d5-06d5184f2cc7" Width="400" Height="600"/> | ---------
…s SoftInput (#35586) <!-- 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: Every MauiView that uses SafeAreaEdges.SoftInput stays alive in memory after being removed from the screen. Users saw platform=12/12 views never getting released in iOS and Mac platform. ### Root Cause: When a MAUI view is configured with SafeAreaEdges.SoftInput on iOS or MacCatalyst, MauiView.SubscribeToKeyboardNotifications() registers two observers with NSNotificationCenter.DefaultCenter — one for UIKeyboard.WillShowNotification and one for UIKeyboard.WillHideNotification. Each call to AddObserver returns an NSObject token that the notification center holds strongly. Because the callbacks (OnKeyboardWillShow, OnKeyboardWillHide) are instance methods, the delegate objects capture a strong reference to this — the MauiView instance. This creates an unbroken retain chain: NSNotificationCenter → NSObject token → delegate → MauiView. As long as those observer tokens are registered, the notification center transitively keeps every subscribed MauiView alive, regardless of whether the view has been removed from the visual tree, its handler disconnected, and all MAUI-side references dropped. The reason the tokens were never removed on detach comes down to a single guard condition in UpdateKeyboardSubscription(). The method was structured as if (Window != null) { ... }, meaning all subscription management — both subscribe and unsubscribe — was gated on the view being attached to a window. When MovedToWindow() fires with Window == null (the standard UIKit signal that a view has been removed from the hierarchy), UpdateKeyboardSubscription() was called but immediately fell through without doing anything. UnsubscribeFromKeyboardNotifications() was never reached, leaving the observer tokens live in the notification center indefinitely. The result, confirmed by the sandbox repro, was platform=12/12 alive after 12 forced GC cycles for views using SafeAreaEdges.SoftInput, versus platform=0/12 for the control case. ### Description of Change: The fix flattens the conditional in UpdateKeyboardSubscription() so that the else branch — which calls UnsubscribeFromKeyboardNotifications() — fires for all cases where the view should not be actively subscribed, including detach. The original nested structure if (Window != null) { if (ShouldSubscribe) subscribe; else unsubscribe; } is replaced with if (Window != null && ShouldSubscribeToKeyboardNotifications()) { SubscribeToKeyboardNotifications(); } else { UnsubscribeFromKeyboardNotifications(); }. Now when MovedToWindow() fires with Window == null, the condition evaluates to false and UnsubscribeFromKeyboardNotifications() is called, issuing NSNotificationCenter.DefaultCenter.RemoveObserver(token) for both keyboard observers. This severs the retain chain and allows the MauiView to be collected normally by the GC. The change is 9 lines, touches a single method, and introduces no new state or lifecycle hooks. **Tested the behavior in the following platforms:** - [x] Android - [ ] Windows - [x] iOS - [ ] Mac ### Reference: N/A ### Issues Fixed: Fixes #35386 ### Screenshots | Before | After | |---------|--------| | <img src="https://github.com/user-attachments/assets/cbbd452a-6565-4e0b-a17d-12c83204bfe5" Width="400" Height="600"/> | <img src="https://github.com/user-attachments/assets/3ebbeba2-9fd1-42ac-a4d5-06d5184f2cc7" Width="400" Height="600"/> | ---------
…s SoftInput (#35586) <!-- 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: Every MauiView that uses SafeAreaEdges.SoftInput stays alive in memory after being removed from the screen. Users saw platform=12/12 views never getting released in iOS and Mac platform. ### Root Cause: When a MAUI view is configured with SafeAreaEdges.SoftInput on iOS or MacCatalyst, MauiView.SubscribeToKeyboardNotifications() registers two observers with NSNotificationCenter.DefaultCenter — one for UIKeyboard.WillShowNotification and one for UIKeyboard.WillHideNotification. Each call to AddObserver returns an NSObject token that the notification center holds strongly. Because the callbacks (OnKeyboardWillShow, OnKeyboardWillHide) are instance methods, the delegate objects capture a strong reference to this — the MauiView instance. This creates an unbroken retain chain: NSNotificationCenter → NSObject token → delegate → MauiView. As long as those observer tokens are registered, the notification center transitively keeps every subscribed MauiView alive, regardless of whether the view has been removed from the visual tree, its handler disconnected, and all MAUI-side references dropped. The reason the tokens were never removed on detach comes down to a single guard condition in UpdateKeyboardSubscription(). The method was structured as if (Window != null) { ... }, meaning all subscription management — both subscribe and unsubscribe — was gated on the view being attached to a window. When MovedToWindow() fires with Window == null (the standard UIKit signal that a view has been removed from the hierarchy), UpdateKeyboardSubscription() was called but immediately fell through without doing anything. UnsubscribeFromKeyboardNotifications() was never reached, leaving the observer tokens live in the notification center indefinitely. The result, confirmed by the sandbox repro, was platform=12/12 alive after 12 forced GC cycles for views using SafeAreaEdges.SoftInput, versus platform=0/12 for the control case. ### Description of Change: The fix flattens the conditional in UpdateKeyboardSubscription() so that the else branch — which calls UnsubscribeFromKeyboardNotifications() — fires for all cases where the view should not be actively subscribed, including detach. The original nested structure if (Window != null) { if (ShouldSubscribe) subscribe; else unsubscribe; } is replaced with if (Window != null && ShouldSubscribeToKeyboardNotifications()) { SubscribeToKeyboardNotifications(); } else { UnsubscribeFromKeyboardNotifications(); }. Now when MovedToWindow() fires with Window == null, the condition evaluates to false and UnsubscribeFromKeyboardNotifications() is called, issuing NSNotificationCenter.DefaultCenter.RemoveObserver(token) for both keyboard observers. This severs the retain chain and allows the MauiView to be collected normally by the GC. The change is 9 lines, touches a single method, and introduces no new state or lifecycle hooks. **Tested the behavior in the following platforms:** - [x] Android - [ ] Windows - [x] iOS - [ ] Mac ### Reference: N/A ### Issues Fixed: Fixes #35386 ### Screenshots | Before | After | |---------|--------| | <img src="https://github.com/user-attachments/assets/cbbd452a-6565-4e0b-a17d-12c83204bfe5" Width="400" Height="600"/> | <img src="https://github.com/user-attachments/assets/3ebbeba2-9fd1-42ac-a4d5-06d5184f2cc7" Width="400" Height="600"/> | ---------
…s SoftInput (#35586) <!-- 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: Every MauiView that uses SafeAreaEdges.SoftInput stays alive in memory after being removed from the screen. Users saw platform=12/12 views never getting released in iOS and Mac platform. ### Root Cause: When a MAUI view is configured with SafeAreaEdges.SoftInput on iOS or MacCatalyst, MauiView.SubscribeToKeyboardNotifications() registers two observers with NSNotificationCenter.DefaultCenter — one for UIKeyboard.WillShowNotification and one for UIKeyboard.WillHideNotification. Each call to AddObserver returns an NSObject token that the notification center holds strongly. Because the callbacks (OnKeyboardWillShow, OnKeyboardWillHide) are instance methods, the delegate objects capture a strong reference to this — the MauiView instance. This creates an unbroken retain chain: NSNotificationCenter → NSObject token → delegate → MauiView. As long as those observer tokens are registered, the notification center transitively keeps every subscribed MauiView alive, regardless of whether the view has been removed from the visual tree, its handler disconnected, and all MAUI-side references dropped. The reason the tokens were never removed on detach comes down to a single guard condition in UpdateKeyboardSubscription(). The method was structured as if (Window != null) { ... }, meaning all subscription management — both subscribe and unsubscribe — was gated on the view being attached to a window. When MovedToWindow() fires with Window == null (the standard UIKit signal that a view has been removed from the hierarchy), UpdateKeyboardSubscription() was called but immediately fell through without doing anything. UnsubscribeFromKeyboardNotifications() was never reached, leaving the observer tokens live in the notification center indefinitely. The result, confirmed by the sandbox repro, was platform=12/12 alive after 12 forced GC cycles for views using SafeAreaEdges.SoftInput, versus platform=0/12 for the control case. ### Description of Change: The fix flattens the conditional in UpdateKeyboardSubscription() so that the else branch — which calls UnsubscribeFromKeyboardNotifications() — fires for all cases where the view should not be actively subscribed, including detach. The original nested structure if (Window != null) { if (ShouldSubscribe) subscribe; else unsubscribe; } is replaced with if (Window != null && ShouldSubscribeToKeyboardNotifications()) { SubscribeToKeyboardNotifications(); } else { UnsubscribeFromKeyboardNotifications(); }. Now when MovedToWindow() fires with Window == null, the condition evaluates to false and UnsubscribeFromKeyboardNotifications() is called, issuing NSNotificationCenter.DefaultCenter.RemoveObserver(token) for both keyboard observers. This severs the retain chain and allows the MauiView to be collected normally by the GC. The change is 9 lines, touches a single method, and introduces no new state or lifecycle hooks. **Tested the behavior in the following platforms:** - [x] Android - [ ] Windows - [x] iOS - [ ] Mac ### Reference: N/A ### Issues Fixed: Fixes #35386 ### Screenshots | Before | After | |---------|--------| | <img src="https://github.com/user-attachments/assets/cbbd452a-6565-4e0b-a17d-12c83204bfe5" Width="400" Height="600"/> | <img src="https://github.com/user-attachments/assets/3ebbeba2-9fd1-42ac-a4d5-06d5184f2cc7" Width="400" Height="600"/> | ---------
…s SoftInput (#35586) <!-- 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: Every MauiView that uses SafeAreaEdges.SoftInput stays alive in memory after being removed from the screen. Users saw platform=12/12 views never getting released in iOS and Mac platform. ### Root Cause: When a MAUI view is configured with SafeAreaEdges.SoftInput on iOS or MacCatalyst, MauiView.SubscribeToKeyboardNotifications() registers two observers with NSNotificationCenter.DefaultCenter — one for UIKeyboard.WillShowNotification and one for UIKeyboard.WillHideNotification. Each call to AddObserver returns an NSObject token that the notification center holds strongly. Because the callbacks (OnKeyboardWillShow, OnKeyboardWillHide) are instance methods, the delegate objects capture a strong reference to this — the MauiView instance. This creates an unbroken retain chain: NSNotificationCenter → NSObject token → delegate → MauiView. As long as those observer tokens are registered, the notification center transitively keeps every subscribed MauiView alive, regardless of whether the view has been removed from the visual tree, its handler disconnected, and all MAUI-side references dropped. The reason the tokens were never removed on detach comes down to a single guard condition in UpdateKeyboardSubscription(). The method was structured as if (Window != null) { ... }, meaning all subscription management — both subscribe and unsubscribe — was gated on the view being attached to a window. When MovedToWindow() fires with Window == null (the standard UIKit signal that a view has been removed from the hierarchy), UpdateKeyboardSubscription() was called but immediately fell through without doing anything. UnsubscribeFromKeyboardNotifications() was never reached, leaving the observer tokens live in the notification center indefinitely. The result, confirmed by the sandbox repro, was platform=12/12 alive after 12 forced GC cycles for views using SafeAreaEdges.SoftInput, versus platform=0/12 for the control case. ### Description of Change: The fix flattens the conditional in UpdateKeyboardSubscription() so that the else branch — which calls UnsubscribeFromKeyboardNotifications() — fires for all cases where the view should not be actively subscribed, including detach. The original nested structure if (Window != null) { if (ShouldSubscribe) subscribe; else unsubscribe; } is replaced with if (Window != null && ShouldSubscribeToKeyboardNotifications()) { SubscribeToKeyboardNotifications(); } else { UnsubscribeFromKeyboardNotifications(); }. Now when MovedToWindow() fires with Window == null, the condition evaluates to false and UnsubscribeFromKeyboardNotifications() is called, issuing NSNotificationCenter.DefaultCenter.RemoveObserver(token) for both keyboard observers. This severs the retain chain and allows the MauiView to be collected normally by the GC. The change is 9 lines, touches a single method, and introduces no new state or lifecycle hooks. **Tested the behavior in the following platforms:** - [x] Android - [ ] Windows - [x] iOS - [ ] Mac ### Reference: N/A ### Issues Fixed: Fixes #35386 ### Screenshots | Before | After | |---------|--------| | <img src="https://github.com/user-attachments/assets/cbbd452a-6565-4e0b-a17d-12c83204bfe5" Width="400" Height="600"/> | <img src="https://github.com/user-attachments/assets/3ebbeba2-9fd1-42ac-a4d5-06d5184f2cc7" Width="400" Height="600"/> | ---------
…s SoftInput (#35586) <!-- 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: Every MauiView that uses SafeAreaEdges.SoftInput stays alive in memory after being removed from the screen. Users saw platform=12/12 views never getting released in iOS and Mac platform. ### Root Cause: When a MAUI view is configured with SafeAreaEdges.SoftInput on iOS or MacCatalyst, MauiView.SubscribeToKeyboardNotifications() registers two observers with NSNotificationCenter.DefaultCenter — one for UIKeyboard.WillShowNotification and one for UIKeyboard.WillHideNotification. Each call to AddObserver returns an NSObject token that the notification center holds strongly. Because the callbacks (OnKeyboardWillShow, OnKeyboardWillHide) are instance methods, the delegate objects capture a strong reference to this — the MauiView instance. This creates an unbroken retain chain: NSNotificationCenter → NSObject token → delegate → MauiView. As long as those observer tokens are registered, the notification center transitively keeps every subscribed MauiView alive, regardless of whether the view has been removed from the visual tree, its handler disconnected, and all MAUI-side references dropped. The reason the tokens were never removed on detach comes down to a single guard condition in UpdateKeyboardSubscription(). The method was structured as if (Window != null) { ... }, meaning all subscription management — both subscribe and unsubscribe — was gated on the view being attached to a window. When MovedToWindow() fires with Window == null (the standard UIKit signal that a view has been removed from the hierarchy), UpdateKeyboardSubscription() was called but immediately fell through without doing anything. UnsubscribeFromKeyboardNotifications() was never reached, leaving the observer tokens live in the notification center indefinitely. The result, confirmed by the sandbox repro, was platform=12/12 alive after 12 forced GC cycles for views using SafeAreaEdges.SoftInput, versus platform=0/12 for the control case. ### Description of Change: The fix flattens the conditional in UpdateKeyboardSubscription() so that the else branch — which calls UnsubscribeFromKeyboardNotifications() — fires for all cases where the view should not be actively subscribed, including detach. The original nested structure if (Window != null) { if (ShouldSubscribe) subscribe; else unsubscribe; } is replaced with if (Window != null && ShouldSubscribeToKeyboardNotifications()) { SubscribeToKeyboardNotifications(); } else { UnsubscribeFromKeyboardNotifications(); }. Now when MovedToWindow() fires with Window == null, the condition evaluates to false and UnsubscribeFromKeyboardNotifications() is called, issuing NSNotificationCenter.DefaultCenter.RemoveObserver(token) for both keyboard observers. This severs the retain chain and allows the MauiView to be collected normally by the GC. The change is 9 lines, touches a single method, and introduces no new state or lifecycle hooks. **Tested the behavior in the following platforms:** - [x] Android - [ ] Windows - [x] iOS - [ ] Mac ### Reference: N/A ### Issues Fixed: Fixes #35386 ### Screenshots | Before | After | |---------|--------| | <img src="https://github.com/user-attachments/assets/cbbd452a-6565-4e0b-a17d-12c83204bfe5" Width="400" Height="600"/> | <img src="https://github.com/user-attachments/assets/3ebbeba2-9fd1-42ac-a4d5-06d5184f2cc7" Width="400" Height="600"/> | ---------
…s SoftInput (#35586) <!-- 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: Every MauiView that uses SafeAreaEdges.SoftInput stays alive in memory after being removed from the screen. Users saw platform=12/12 views never getting released in iOS and Mac platform. ### Root Cause: When a MAUI view is configured with SafeAreaEdges.SoftInput on iOS or MacCatalyst, MauiView.SubscribeToKeyboardNotifications() registers two observers with NSNotificationCenter.DefaultCenter — one for UIKeyboard.WillShowNotification and one for UIKeyboard.WillHideNotification. Each call to AddObserver returns an NSObject token that the notification center holds strongly. Because the callbacks (OnKeyboardWillShow, OnKeyboardWillHide) are instance methods, the delegate objects capture a strong reference to this — the MauiView instance. This creates an unbroken retain chain: NSNotificationCenter → NSObject token → delegate → MauiView. As long as those observer tokens are registered, the notification center transitively keeps every subscribed MauiView alive, regardless of whether the view has been removed from the visual tree, its handler disconnected, and all MAUI-side references dropped. The reason the tokens were never removed on detach comes down to a single guard condition in UpdateKeyboardSubscription(). The method was structured as if (Window != null) { ... }, meaning all subscription management — both subscribe and unsubscribe — was gated on the view being attached to a window. When MovedToWindow() fires with Window == null (the standard UIKit signal that a view has been removed from the hierarchy), UpdateKeyboardSubscription() was called but immediately fell through without doing anything. UnsubscribeFromKeyboardNotifications() was never reached, leaving the observer tokens live in the notification center indefinitely. The result, confirmed by the sandbox repro, was platform=12/12 alive after 12 forced GC cycles for views using SafeAreaEdges.SoftInput, versus platform=0/12 for the control case. ### Description of Change: The fix flattens the conditional in UpdateKeyboardSubscription() so that the else branch — which calls UnsubscribeFromKeyboardNotifications() — fires for all cases where the view should not be actively subscribed, including detach. The original nested structure if (Window != null) { if (ShouldSubscribe) subscribe; else unsubscribe; } is replaced with if (Window != null && ShouldSubscribeToKeyboardNotifications()) { SubscribeToKeyboardNotifications(); } else { UnsubscribeFromKeyboardNotifications(); }. Now when MovedToWindow() fires with Window == null, the condition evaluates to false and UnsubscribeFromKeyboardNotifications() is called, issuing NSNotificationCenter.DefaultCenter.RemoveObserver(token) for both keyboard observers. This severs the retain chain and allows the MauiView to be collected normally by the GC. The change is 9 lines, touches a single method, and introduces no new state or lifecycle hooks. **Tested the behavior in the following platforms:** - [x] Android - [ ] Windows - [x] iOS - [ ] Mac ### Reference: N/A ### Issues Fixed: Fixes #35386 ### Screenshots | Before | After | |---------|--------| | <img src="https://github.com/user-attachments/assets/cbbd452a-6565-4e0b-a17d-12c83204bfe5" Width="400" Height="600"/> | <img src="https://github.com/user-attachments/assets/3ebbeba2-9fd1-42ac-a4d5-06d5184f2cc7" Width="400" Height="600"/> | ---------
…s SoftInput (#35586) <!-- 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: Every MauiView that uses SafeAreaEdges.SoftInput stays alive in memory after being removed from the screen. Users saw platform=12/12 views never getting released in iOS and Mac platform. ### Root Cause: When a MAUI view is configured with SafeAreaEdges.SoftInput on iOS or MacCatalyst, MauiView.SubscribeToKeyboardNotifications() registers two observers with NSNotificationCenter.DefaultCenter — one for UIKeyboard.WillShowNotification and one for UIKeyboard.WillHideNotification. Each call to AddObserver returns an NSObject token that the notification center holds strongly. Because the callbacks (OnKeyboardWillShow, OnKeyboardWillHide) are instance methods, the delegate objects capture a strong reference to this — the MauiView instance. This creates an unbroken retain chain: NSNotificationCenter → NSObject token → delegate → MauiView. As long as those observer tokens are registered, the notification center transitively keeps every subscribed MauiView alive, regardless of whether the view has been removed from the visual tree, its handler disconnected, and all MAUI-side references dropped. The reason the tokens were never removed on detach comes down to a single guard condition in UpdateKeyboardSubscription(). The method was structured as if (Window != null) { ... }, meaning all subscription management — both subscribe and unsubscribe — was gated on the view being attached to a window. When MovedToWindow() fires with Window == null (the standard UIKit signal that a view has been removed from the hierarchy), UpdateKeyboardSubscription() was called but immediately fell through without doing anything. UnsubscribeFromKeyboardNotifications() was never reached, leaving the observer tokens live in the notification center indefinitely. The result, confirmed by the sandbox repro, was platform=12/12 alive after 12 forced GC cycles for views using SafeAreaEdges.SoftInput, versus platform=0/12 for the control case. ### Description of Change: The fix flattens the conditional in UpdateKeyboardSubscription() so that the else branch — which calls UnsubscribeFromKeyboardNotifications() — fires for all cases where the view should not be actively subscribed, including detach. The original nested structure if (Window != null) { if (ShouldSubscribe) subscribe; else unsubscribe; } is replaced with if (Window != null && ShouldSubscribeToKeyboardNotifications()) { SubscribeToKeyboardNotifications(); } else { UnsubscribeFromKeyboardNotifications(); }. Now when MovedToWindow() fires with Window == null, the condition evaluates to false and UnsubscribeFromKeyboardNotifications() is called, issuing NSNotificationCenter.DefaultCenter.RemoveObserver(token) for both keyboard observers. This severs the retain chain and allows the MauiView to be collected normally by the GC. The change is 9 lines, touches a single method, and introduces no new state or lifecycle hooks. **Tested the behavior in the following platforms:** - [x] Android - [ ] Windows - [x] iOS - [ ] Mac ### Reference: N/A ### Issues Fixed: Fixes #35386 ### Screenshots | Before | After | |---------|--------| | <img src="https://github.com/user-attachments/assets/cbbd452a-6565-4e0b-a17d-12c83204bfe5" Width="400" Height="600"/> | <img src="https://github.com/user-attachments/assets/3ebbeba2-9fd1-42ac-a4d5-06d5184f2cc7" Width="400" Height="600"/> | ---------
…s SoftInput (#35586) <!-- 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: Every MauiView that uses SafeAreaEdges.SoftInput stays alive in memory after being removed from the screen. Users saw platform=12/12 views never getting released in iOS and Mac platform. ### Root Cause: When a MAUI view is configured with SafeAreaEdges.SoftInput on iOS or MacCatalyst, MauiView.SubscribeToKeyboardNotifications() registers two observers with NSNotificationCenter.DefaultCenter — one for UIKeyboard.WillShowNotification and one for UIKeyboard.WillHideNotification. Each call to AddObserver returns an NSObject token that the notification center holds strongly. Because the callbacks (OnKeyboardWillShow, OnKeyboardWillHide) are instance methods, the delegate objects capture a strong reference to this — the MauiView instance. This creates an unbroken retain chain: NSNotificationCenter → NSObject token → delegate → MauiView. As long as those observer tokens are registered, the notification center transitively keeps every subscribed MauiView alive, regardless of whether the view has been removed from the visual tree, its handler disconnected, and all MAUI-side references dropped. The reason the tokens were never removed on detach comes down to a single guard condition in UpdateKeyboardSubscription(). The method was structured as if (Window != null) { ... }, meaning all subscription management — both subscribe and unsubscribe — was gated on the view being attached to a window. When MovedToWindow() fires with Window == null (the standard UIKit signal that a view has been removed from the hierarchy), UpdateKeyboardSubscription() was called but immediately fell through without doing anything. UnsubscribeFromKeyboardNotifications() was never reached, leaving the observer tokens live in the notification center indefinitely. The result, confirmed by the sandbox repro, was platform=12/12 alive after 12 forced GC cycles for views using SafeAreaEdges.SoftInput, versus platform=0/12 for the control case. ### Description of Change: The fix flattens the conditional in UpdateKeyboardSubscription() so that the else branch — which calls UnsubscribeFromKeyboardNotifications() — fires for all cases where the view should not be actively subscribed, including detach. The original nested structure if (Window != null) { if (ShouldSubscribe) subscribe; else unsubscribe; } is replaced with if (Window != null && ShouldSubscribeToKeyboardNotifications()) { SubscribeToKeyboardNotifications(); } else { UnsubscribeFromKeyboardNotifications(); }. Now when MovedToWindow() fires with Window == null, the condition evaluates to false and UnsubscribeFromKeyboardNotifications() is called, issuing NSNotificationCenter.DefaultCenter.RemoveObserver(token) for both keyboard observers. This severs the retain chain and allows the MauiView to be collected normally by the GC. The change is 9 lines, touches a single method, and introduces no new state or lifecycle hooks. **Tested the behavior in the following platforms:** - [x] Android - [ ] Windows - [x] iOS - [ ] Mac ### Reference: N/A ### Issues Fixed: Fixes #35386 ### Screenshots | Before | After | |---------|--------| | <img src="https://github.com/user-attachments/assets/cbbd452a-6565-4e0b-a17d-12c83204bfe5" Width="400" Height="600"/> | <img src="https://github.com/user-attachments/assets/3ebbeba2-9fd1-42ac-a4d5-06d5184f2cc7" Width="400" Height="600"/> | ---------
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:
Every MauiView that uses SafeAreaEdges.SoftInput stays alive in memory after being removed from the screen. Users saw platform=12/12 views never getting released in iOS and Mac platform.
Root Cause:
When a MAUI view is configured with SafeAreaEdges.SoftInput on iOS or MacCatalyst, MauiView.SubscribeToKeyboardNotifications() registers two observers with NSNotificationCenter.DefaultCenter — one for UIKeyboard.WillShowNotification and one for UIKeyboard.WillHideNotification. Each call to AddObserver returns an NSObject token that the notification center holds strongly. Because the callbacks (OnKeyboardWillShow, OnKeyboardWillHide) are instance methods, the delegate objects capture a strong reference to this — the MauiView instance. This creates an unbroken retain chain: NSNotificationCenter → NSObject token → delegate → MauiView. As long as those observer tokens are registered, the notification center transitively keeps every subscribed MauiView alive, regardless of whether the view has been removed from the visual tree, its handler disconnected, and all MAUI-side references dropped.
The reason the tokens were never removed on detach comes down to a single guard condition in UpdateKeyboardSubscription(). The method was structured as if (Window != null) { ... }, meaning all subscription management — both subscribe and unsubscribe — was gated on the view being attached to a window. When MovedToWindow() fires with Window == null (the standard UIKit signal that a view has been removed from the hierarchy), UpdateKeyboardSubscription() was called but immediately fell through without doing anything. UnsubscribeFromKeyboardNotifications() was never reached, leaving the observer tokens live in the notification center indefinitely. The result, confirmed by the sandbox repro, was platform=12/12 alive after 12 forced GC cycles for views using SafeAreaEdges.SoftInput, versus platform=0/12 for the control case.
Description of Change:
The fix flattens the conditional in UpdateKeyboardSubscription() so that the else branch — which calls UnsubscribeFromKeyboardNotifications() — fires for all cases where the view should not be actively subscribed, including detach. The original nested structure if (Window != null) { if (ShouldSubscribe) subscribe; else unsubscribe; } is replaced with if (Window != null && ShouldSubscribeToKeyboardNotifications()) { SubscribeToKeyboardNotifications(); } else { UnsubscribeFromKeyboardNotifications(); }. Now when MovedToWindow() fires with Window == null, the condition evaluates to false and UnsubscribeFromKeyboardNotifications() is called, issuing NSNotificationCenter.DefaultCenter.RemoveObserver(token) for both keyboard observers. This severs the retain chain and allows the MauiView to be collected normally by the GC. The change is 9 lines, touches a single method, and introduces no new state or lifecycle hooks.
Tested the behavior in the following platforms:
Reference:
N/A
Issues Fixed:
Fixes #35386
Screenshots