[iOS] Fix ScrollView with RTL FlowDirection and Horizontal Orientation scrolls in the wrong direction#32529
Conversation
There was a problem hiding this comment.
Pull Request Overview
This PR fixes an iOS-specific issue where ScrollView with RTL (Right-To-Left) FlowDirection fails to scroll correctly when the Orientation property is changed at runtime from Vertical to Horizontal. The fix adds orientation change tracking to ensure RTL layout adjustments are properly recalculated.
Key changes:
- Added
OnOrientationChanged()method toMauiScrollViewto reset RTL layout state - Updated
MapOrientationhandler to callOnOrientationChanged()when orientation changes at runtime - Added UI test coverage with Issue32271 test case
Reviewed Changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/Core/src/Platform/iOS/MauiScrollView.cs | Adds OnOrientationChanged() method to reset cached RTL layout direction and trigger re-layout when orientation changes |
| src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs | Calls OnOrientationChanged() in MapOrientation when the platform view is loaded to handle runtime orientation changes |
| src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32271.cs | Adds NUnit test that toggles orientation and verifies scrolling behavior via screenshot |
| src/Controls/tests/TestCases.HostApp/Issues/Issue32271.cs | Adds test host page with RTL ScrollView that supports runtime orientation toggling and scroll-to commands |
🤖 AI Summary📊 Expand Full Review🔍 Pre-Flight — Context & Validation📝 Review Session — Update Issue32271.cs ·
|
| File:Line | Reviewer Says | Status |
|---|---|---|
MauiScrollView.cs:113 |
Check EffectiveUserInterfaceLayoutDirection == RTL instead of _previousEffectiveUserInterfaceLayoutDirection == RTL |
🔍 ANALYZED - NOT A REAL BUG (see Fix phase) |
TestCases.HostApp/Issues/Issue32271.cs |
Orientation mismatch between ScrollView declaration and ViewModel | ✅ FIXED (commit dc92906, thread is Outdated) |
Files Changed
Implementation (Fix):
src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs(+8) — CallsmauiScrollView.OnOrientationChanged()inMapOrientationwhen view is loadedsrc/Core/src/Platform/iOS/MauiScrollView.cs(+14) — AddsOnOrientationChanged()method that resets_previousto null and forces layout
Tests:
src/Controls/tests/TestCases.HostApp/Issues/Issue32271.cs(+146) — UI test host page with RTL ScrollView + Toggle Orientation buttonsrc/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32271.cs(+20) — NUnit test: tap Toggle → tap Scroll → VerifyScreenshot
Test Type: UI Tests (Appium-based, VerifyScreenshot)
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #32529 | Reset _previousEffectiveUserInterfaceLayoutDirection to null in OnOrientationChanged(), force layout |
⏳ PENDING (Gate) | ScrollViewHandler.iOS.cs (+8), MauiScrollView.cs (+14) |
Logic analyzed as correct |
🚦 Gate — Test Verification
📝 Review Session — Update Issue32271.cs · dc92906
Result: ❌ FAILED
Platform: ios
Mode: Full Verification
- Tests FAIL without fix ✅ (expected - bug detected)
- Tests PASS with fix ❌ (unexpected - fix doesn't resolve test)
Root Cause of Gate Failure
The test VerifyScrollViewDirection() calls VerifyScreenshot() which requires a committed baseline snapshot file. No baseline exists in the repository:
src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollViewDirection.png
(or similar path — file does not exist)
Both test runs (with and without fix) fail because the snapshot infrastructure cannot find a baseline to compare against. This is NOT a bug in the fix — the build succeeds and the fix is logically correct. The test cannot pass until the PR author:
- Runs the test locally on iOS to generate the baseline snapshot
- Commits the baseline snapshot PNG to the repository
Note
This is the SECOND review on the same commit dc92906. The prior agent review (prior run, same commit) also failed Gate for this same reason. The PR author has not yet addressed the missing baseline.
🔧 Fix — Analysis & Comparison
📝 Review Session — Update Issue32271.cs · dc92906
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #32529 | Reset _previousEffectiveUserInterfaceLayoutDirection to null via OnOrientationChanged() when orientation changes at runtime; force layout via SetNeedsLayout() + LayoutIfNeeded() |
❌ NOT VERIFIED (Gate baseline missing) | ScrollViewHandler.iOS.cs (+8), MauiScrollView.cs (+14) |
Code analysis confirms logic is correct (see below) |
Exhausted: No (Fix phase skipped — Gate did not pass)
Selected Fix: PR's fix — Code analysis (below) confirms the approach is logically sound.
Code Analysis of PR's Fix
Root cause: In MauiScrollView.CrossPlatformArrange() (line 486), the RTL content offset logic is gated on:
if (_previousEffectiveUserInterfaceLayoutDirection != EffectiveUserInterfaceLayoutDirection)After the initial layout pass, _previous = RightToLeft. On subsequent layout passes (even after orientation change), RTL != RTL evaluates to false — the RTL positioning is never re-applied for the new orientation dimensions.
Fix logic (OnOrientationChanged):
if (_previousEffectiveUserInterfaceLayoutDirection == UIUserInterfaceLayoutDirection.RightToLeft)
{
_previousEffectiveUserInterfaceLayoutDirection = null; // Reset to force re-evaluation
SetNeedsLayout();
LayoutIfNeeded(); // Synchronous layout pass
}Setting _previous = null makes the condition null != RTL → true, forcing the RTL layout block to execute for the new orientation dimensions.
Correctness trace (repeated toggles):
- Initial (RTL, Horizontal):
null != RTL→ RTL layout applied →_previous = RTL✅ - Toggle to Vertical:
OnOrientationChanged()→_previous = null→ layout pass →null != RTL→ RTL layout applied →_previous = RTL✅ - Toggle back to Horizontal:
OnOrientationChanged()→_previous = RTL(TRUE) →_previous = null→ layout pass →null != RTL→ RTL layout applied →_previous = RTL✅ - Repeated toggles: same pattern ✅
Copilot reviewer's suggestion analysis: The Copilot reviewer suggested changing the condition to EffectiveUserInterfaceLayoutDirection == RightToLeft. This concern is based on an incorrect assumption that _previous becomes null after step 2 and stays null. In reality, the LayoutIfNeeded() call in OnOrientationChanged() triggers ArrangeContent() which sets _previous = RTL before returning. So on step 3, _previous == RTL is TRUE and the fix works correctly. The Copilot suggestion is not needed.
Minor concerns:
- Double layout pass:
OnOrientationChanged()callsLayoutIfNeeded()synchronously, thenMapOrientationcallsInvalidateMeasure(). Minor performance cost for complex layouts. - No platform guard on NUnit test:
PlatformAffected.iOSin HostApp page but test has no[Ignore]for other platforms.
📋 Report — Final Recommendation
📝 Review Session — Update Issue32271.cs · dc92906
⚠️ Final Recommendation: REQUEST CHANGES
Summary
PR #32529 fixes a real and confirmed iOS bug where a ScrollView with FlowDirection = RightToLeft loses its RTL scrolling behavior after the Orientation is changed at runtime. The fix approach is logically sound and minimal. However:
- [Blocking] The
VerifyScrollViewDirectiontest callsVerifyScreenshot()but no baseline snapshot has been committed to the repository — the test will fail in CI on every run until this is added. - [Non-blocking] The Copilot reviewer's inline suggestion at
MauiScrollView.cs:113has NOT been addressed (still open/unresolved), though code analysis confirms the suggestion is actually not needed (see analysis below). - [Minor] The
Issue32271.csNUnit test file is missing a newline at end of file. - [Minor] The NUnit test has no platform guard, but the HostApp page declares
PlatformAffected.iOS.
This is the second agent review on the same commit (dc92906). The prior review also failed Gate for the identical reason. The PR author has not yet addressed the missing baseline.
Root Cause
In MauiScrollView.CrossPlatformArrange(), the RTL content offset logic is protected by:
if (_previousEffectiveUserInterfaceLayoutDirection != EffectiveUserInterfaceLayoutDirection)After the initial layout pass sets _previous = RightToLeft, this condition always evaluates to false on subsequent passes — the RTL positioning is never re-applied when orientation changes (which changes content dimensions, requiring a new offset calculation).
Fix Analysis
PR's Approach: Add OnOrientationChanged() to MauiScrollView that resets _previousEffectiveUserInterfaceLayoutDirection = null (when it was RTL), then calls SetNeedsLayout() + LayoutIfNeeded() to synchronously re-trigger the RTL layout block.
Is the fix correct? Yes. The logic handles repeated orientation toggles correctly:
- After each toggle,
LayoutIfNeeded()restores_previous = RTLbefore the method returns - So on the next toggle,
_previous == RTLis always true → the reset works for all subsequent toggles
Copilot reviewer's suggestion: The suggestion to change the condition to EffectiveUserInterfaceLayoutDirection == RightToLeft is based on an incorrect assumption that _previous stays null after a toggle. In reality, ArrangeContent() (called by LayoutIfNeeded()) sets _previous = RTL before returning. The original PR's condition is functionally equivalent for the bug scenario. The suggestion is NOT required.
Issues Requiring Changes
[Blocking] Missing baseline snapshot for VerifyScrollViewDirection test
Evidence from Gate: Both test runs (WITH and WITHOUT fix) failed because the baseline snapshot does not exist:
src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollViewDirection.png— MISSING
Required action:
- Run the test locally on iOS to generate the baseline screenshot
- Commit the baseline PNG to the PR branch
- See visual test workflow
[Minor] Missing newline at EOF
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32271.cs — no newline at end of file.
[Minor] NUnit test runs on all platforms but HostApp page is iOS-only
The NUnit test has no [Category] or [Ignore] to restrict it to iOS, while Issue32271 : ContentPage declares PlatformAffected.iOS. This may cause failures on other platforms if the test infrastructure can't find the page.
Phases Completed
| Phase | Status | Notes |
|---|---|---|
| Pre-Flight | ✅ COMPLETE | Context gathered; prior review imported |
| Gate | ❌ FAILED | Missing baseline snapshot (same as prior review) |
| Fix | ⏩ SKIPPED | Gate did not pass; code analysis performed manually |
| Report | ✅ COMPLETE | REQUEST CHANGES for baseline snapshot |
PR Quality Assessment
| Aspect | Assessment |
|---|---|
| Fix correctness | ✅ Logically correct |
| Fix scope | ✅ Minimal, iOS-only |
| Code quality | ✅ Well-commented, follows conventions |
| Test structure | ✅ Correct (orientation mismatch fixed in prior commit) |
| Test baseline | ❌ Missing — CI will fail every run |
| Title accuracy | ✅ Accurate |
📋 Expand PR Finalization Review
Title: ✅ Good
Current: [iOS] Fix ScrollView with RTL FlowDirection and Horizontal Orientation scrolls in the wrong direction
Description: ✅ Good
Description needs updates. See details below.
✨ Suggested PR Description
[!NOTE]
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Root Cause
When a ScrollView has FlowDirection=RightToLeft and its Orientation is changed at runtime, the RTL content-offset adjustment in MauiScrollView.CrossPlatformArrange is not re-triggered.
The RTL adjustment logic (which repositions content and sets ContentOffset for right-to-left scrolling) only runs when _previousEffectiveUserInterfaceLayoutDirection != EffectiveUserInterfaceLayoutDirection. On initial load this fires once (previous is null, current is RightToLeft), then _previousEffectiveUserInterfaceLayoutDirection is set to RightToLeft. On subsequent layout passes (including those triggered by an orientation change), both values are RightToLeft, so the condition is false and the RTL adjustment is skipped — leaving the content offset wrong for the new orientation.
Description of Change
MauiScrollView.cs (iOS platform layer)
Added internal void OnOrientationChanged(). When the current layout direction is RightToLeft, this method resets _previousEffectiveUserInterfaceLayoutDirection to null, then calls SetNeedsLayout() and LayoutIfNeeded(). This tricks the RTL adjustment logic into believing the layout direction "changed" on the next arrange pass, causing it to recalculate the horizontal content offset for the new orientation.
ScrollViewHandler.iOS.cs
In MapOrientation, after updating the enabled state and before calling InvalidateMeasure, the handler now calls mauiScrollView.OnOrientationChanged() when the platform view is a MauiScrollView and IsLoaded() is true. The IsLoaded() guard prevents interference during the initial load path, where the RTL adjustment already works correctly.
Test files
Added Issue32271.cs in TestCases.HostApp/Issues/ and TestCases.Shared.Tests/Tests/Issues/. The host app page sets up a ScrollView with FlowDirection=RightToLeft, binds Orientation to a viewmodel, and provides a "Toggle Orientation" button and a "Scroll" button. The NUnit test taps "Toggle Orientation" (Vertical → Horizontal), taps "Scroll", and verifies via screenshot.
Key Technical Details
Relevant field: _previousEffectiveUserInterfaceLayoutDirection (UIUserInterfaceLayoutDirection?) in MauiScrollView
- Tracks last-seen layout direction to gate the RTL one-time adjustment
- Setting it to
nullre-arms the RTL adjustment for the next layout pass
Affected scenario: RTL + runtime Orientation change only. Initial load (Vertical or Horizontal with RTL) is unaffected because the direction tracking fires normally on first arrange.
IsLoaded() guard: Prevents calling OnOrientationChanged() during initial mapper setup, where _previousEffectiveUserInterfaceLayoutDirection is already null and no reset is needed.
Issues Fixed
Fixes #32271
Platforms Tested
- Android
- Windows
- iOS
- Mac
Code Review: ✅ Passed
Code Review — PR #32529
🟡 Suggestions
1. LayoutIfNeeded() in OnOrientationChanged() may be redundant
File: src/Core/src/Platform/iOS/MauiScrollView.cs
internal void OnOrientationChanged()
{
if (_previousEffectiveUserInterfaceLayoutDirection == UIUserInterfaceLayoutDirection.RightToLeft)
{
_previousEffectiveUserInterfaceLayoutDirection = null;
SetNeedsLayout();
LayoutIfNeeded(); // <-- concern
}
}LayoutIfNeeded() forces a synchronous layout pass. At this point the orientation hasn't actually been applied to the cross-platform layout yet — InvalidateMeasure (the call that follows in MapOrientation) is what drives the full re-measure+arrange cycle. If the scroll view's frame hasn't changed, LayoutSubviews will see frameChanged = false and skip CrossPlatformArrange, meaning the _previousEffectiveUserInterfaceLayoutDirection = null reset will persist until the InvalidateMeasure-triggered pass anyway.
The LayoutIfNeeded() call is not harmful but its value is unclear. It could force an intermediate layout pass with stale dimensions. At minimum, a comment explaining why the immediate synchronous layout is necessary (vs. letting InvalidateMeasure handle it) would make this clearer.
2. VerifyScreenshot() without a stability wait after scroll
File: src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32271.cs
public void VerifyScrollViewDirection()
{
App.WaitForElement("ToggleOrientationButton");
App.Tap("ToggleOrientationButton");
App.Tap("ScrollToEndButton");
VerifyScreenshot(); // called immediately after tap
}The "Scroll" button triggers ScrollToAsync(..., animated: true). The animated scroll may not complete before VerifyScreenshot() is called, making the screenshot non-deterministic. Consider using VerifyScreenshot(retryTimeout: TimeSpan.FromSeconds(2)) to allow the animation to settle:
VerifyScreenshot(retryTimeout: TimeSpan.FromSeconds(2));3. Screenshot-only assertion does not verify scroll direction correctness
File: src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32271.cs
The test verifies visual appearance via screenshot but does not verify the actual scroll direction programmatically. If the scroll position is wrong (the original bug), the screenshot would differ from the baseline — but only after a baseline has been established with the fix applied. This is reasonable for a regression test, though a GetRect()-based position check would be more robust.
4. Missing newline at end of test file
File: src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32271.cs
The diff shows \ No newline at end of file. All source files should end with a newline.
5. Nullable annotation gap in test viewmodel
File: src/Controls/tests/TestCases.HostApp/Issues/Issue32271.cs
View _content; // not nullable, but initialized via constructor pathThe field _content is a reference type declared without ?. The compiler may emit a nullable warning since it's not initialized in the field declaration. It's initialized via the Content setter in the constructor (Content = new Label {...}), but the declaration should use View? _content; to match the nullable intent:
View? _content;Similarly, the PropertyChanged event:
public event PropertyChangedEventHandler PropertyChanged;
// should be:
public event PropertyChangedEventHandler? PropertyChanged;6. Dead code: ContentText property in test viewmodel
File: src/Controls/tests/TestCases.HostApp/Issues/Issue32271.cs
public string ContentText
{
get => _contentText;
set { ... Content = new Label { Text = _contentText }; ... }
}ContentText (and the backing field _contentText) is never bound or used in the test page. It appears to be carry-over from the issue reporter's original reproduction. It creates a dead Label on set and can be removed.
✅ Looks Good
IsLoaded()guard inMapOrientationcorrectly prevents the orientation-change path from firing during initial mapper setup — good defensive coding.OnOrientationChanged()scope — correctly scoped tointernal, not public API.- RTL-only guard —
if (_previousEffectiveUserInterfaceLayoutDirection == UIUserInterfaceLayoutDirection.RightToLeft)correctly limits the reset to RTL views, avoiding interference with LTR scrollviews. - Test page structure — uses viewmodel + binding for orientation, matching real-world usage patterns that trigger the bug.
[Category(UITestCategories.ScrollView)]— correct category for the test.platformView is MauiScrollViewtype check — safe cast before calling the new method.
6e563dc to
00535e4
Compare
kubaflo
left a comment
There was a problem hiding this comment.
Could you please add snapshot?
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
…colors when Material3 is enabled (dotnet#35129) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Details: - When Material 3 is enabled (`<UseMaterial3>true</UseMaterial3>`), the Shell/TabbedPage "More" overflow `BottomSheetDialog` on Android still uses Material 2 styling — square corners, hard-coded black text, gray icons, and white backgrounds — instead of M3-styled rounded corners, theme-aware colors, and proper ripple effects. ### Root Cause of the issue: - The `CreateMoreBottomSheet()` method in both `BottomNavigationViewUtils.cs` and `ShellItemRenderer.cs` hard-codes M2 colors for item rows (white background, black text, gray icons) without checking `RuntimeFeature.IsMaterial3Enabled`. ### Description of Change <!-- Enter description of the fix in this section --> **Material 3 theming improvements:** * Updated the background drawable for BottomSheet items to use M3 ripple and background colors when Material 3 is enabled by introducing a new `CreateItemBackgroundDrawable(Context)` method and using it conditionally. (`ShellItemRenderer.cs`, `BottomNavigationViewUtils.cs`) * Changed icon tint and text color assignments in the BottomSheet to use M3 theme attributes (`colorOnSurfaceVariant` for icons, `colorOnSurface` for text) when Material 3 is enabled, falling back to previous hard-coded values otherwise. (`ShellItemRenderer.cs`, `BottomNavigationViewUtils.cs`) **Testing enhancements:** * Added a new sample page (`Issue35127`) to demonstrate and manually verify the correct theming of the "More" BottomSheet with six tabs. (`Issue35127.cs`) * Introduced a corresponding automated UI test to ensure the BottomSheet uses M3 themed colors when Material 3 is enabled. (`Issue35127.cs` in test project) ### Issues Fixed <!-- Please make sure that there is a bug logged for the issue being fixed. The bug should describe the problem and how to reproduce it. --> Fixes dotnet#35127 ### Tested the behaviour in the following platforms - [ ] - Windows - [x] - Android - [ ] - iOS - [ ] - Mac | Before | After | |----------|----------| | <img src="https://github.com/user-attachments/assets/7aee800f-c08d-479b-90db-204ffaee3af9">| <img src="https://github.com/user-attachments/assets/6699f69d-7fc7-4afb-b8af-6ef031a3fc72"> | <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. -->
… light theme (dotnet#35128) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ### Issue Details - When Material 3 is enabled via `<UseMaterial3>true</UseMaterial3>`, unselected tab labels in Android Shell top tabs appear too faint in light theme, making only the selected tab clearly visible. With Material 2 (`<UseMaterial3>false</UseMaterial3>`), unselected tab labels are clearly visible under the same. ### Root Cause - The top-tab unselected fallback color was still an old M2-style semi-transparent white, which had poor contrast on light Material 3 surfaces. ### Description of Change **Theme-aware color updates:** * Changed `DefaultUnselectedColor` in `ShellRenderer` from a static readonly field to a property that resolves to different colors depending on whether Material 3 is enabled and the current theme, improving visibility for unselected tabs in Material 3 light mode. * Updated the public API surface to reflect that `DefaultUnselectedColor` is now a property rather than a static readonly field. **Test additions:** * Added a new sample test case (`Issue35125`) in `TestCases.HostApp` that sets up a Shell with two top tabs and verifies that unselected tabs remain visible. * Introduced a corresponding UI test in `TestCases.Shared.Tests` to automate checking that the unselected tab text is visible when switching tabs on Android. ### Issues Fixed Fixes dotnet#35125 ### Validated the behaviour in the following platforms - [ ] Windows - [x] Android - [ ] iOS - [ ] Mac ### Output | Before | After | |----------|----------| | <img src="https://github.com/user-attachments/assets/97e6fb7c-0a88-429a-bf14-bf5fa0d77f56"> | <img src="https://github.com/user-attachments/assets/e4c9fcdb-851c-4729-b4b3-e1ed0fe55eae"> | --------- Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…yboard open (dotnet#35083) <!-- 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! ### Root Cause When `SafeAreaEdges` changes from a mode that includes `SoftInput` (e.g., `All`) to one that does not (e.g., `Container`), the view unsubscribes from iOS keyboard notifications. However, the keyboard state (`_isKeyboardShowing`, `_keyboardFrame`) was not cleared. If the keyboard was open during unsubscription and later dismissed, the `OnKeyboardWillHide` event is never received. When switching back to a `SoftInput` mode, the view re-subscribes with stale state, applying incorrect bottom padding equal to the old keyboard height instead of the home indicator inset. ### Description of Change Added state cleanup in `UnsubscribeFromKeyboardNotifications` in `MauiView.cs`. When unsubscribing, if the keyboard was previously visible, the method now clears the keyboard state, invalidates the safe area, and triggers a layout update. This prevents stale keyboard state from persisting across subscribe/unsubscribe cycles while preserving existing behavior in all other scenarios. Also added a test in `Issue28986_ContentPage` that reproduces the exact sequence: switch to All, open keyboard, change to Container, dismiss keyboard, switch back to All, and verify the layout returns to baseline without phantom padding. ### Issues Fixed Fixes dotnet#34846 Tested the behaviour in the following platforms - [x] Android - [ ] Windows - [x] iOS - [x] Mac ### Screenshots | Before Issue Fix | After Issue Fix | |------------------|-----------------| | <video width="350" alt="withoutfix" src="https://github.com/user-attachments/assets/48e5afe8-4b6c-4c5a-b223-91312bf212e3" /> | <video width="350" alt="withfix" src="https://github.com/user-attachments/assets/a7fa1af6-8927-4fed-a7f2-f8011bdbef8f" /> |
…changes (dotnet#34488) <!-- 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 - On iOS, when using CollectionView with the Scrolled event, the VerticalOffset value doesn't reset to 0 when changing the ItemsSource. While the collection view displays the new items correctly, the reported scroll position remains at the previous offset value instead of resetting to the top position. ### Root Cause - On iOS, UICollectionView.ReloadData() does not reset ContentOffset. ContentOffset retains its previous value, meaning any subsequent Scrolled callback—which derives VerticalOffset and delta values directly from ContentOffset—reports the stale, non-zero offset. As a result, MAUI's VerticalOffset never resets to 0 after the ItemsSource changes. ### Description of Change - Added logic in ItemsViewController.cs and ItemsViewController2.cs to reset CollectionView.ContentOffset to zero only when it’s not already at the origin. This ensures the scroll position resets correctly when the ItemsSource changes on iOS/MacCatalyst. - Before resetting the offset, ResetScrollTracking() is invoked via the internal IScrollTrackingDelegator interface so that the scrollViewDidScroll callback computes the delta from zero instead of using a stale offset. **Test and UI improvements:** - Updated the test page (Issue7993.xaml) to add a "ScrollToEnd" button for reliably scrolling the list, and updated label and button identifiers for improved test automation. - Added the ScrollToEndClicked handler in Issue7993.xaml.cs to programmatically scroll to the end of the list, supporting the new test flow. - Removed platform-specific test skips and refactored the test (Issue7993.cs) to use the new "ScrollToEnd" and "NewItemsSource" buttons, improving reliability and making the test applicable to iOS/MacCatalyst. ### Issues Fixed Fixes dotnet#26366 Fixes dotnet#33500 ### Validated the behaviour in the following platforms - [ ] Windows - [ ] Android - [x] iOS - [x] Mac ### Output | Before | After | |----------|----------| | <video src="https://github.com/user-attachments/assets/a374859f-25ce-445f-a53f-6354c2d3f201"> | <video src="https://github.com/user-attachments/assets/2bdc2dd4-43be-41ef-810c-e4098cba791f"> |
…n removal (dotnet#34898) (dotnet#35031) <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Details Clearing the Shell's root items causes a memory leak. On iOS, the old page is cleaned up but its child elements (labels, buttons, etc.) are not. On Android, neither the page nor its children are cleaned up. Leaked native views accumulate with each root page swap, eventually degrading performance or crashing the app. ### Root Cause When items are removed from Shell, the system only removes the old page from its internal list — it does not tell the native platform to destroy the actual screen elements. On Android, the fragment cleanup recycled the page without disconnecting its child handlers. On iOS, only the page-level handler was disconnected but the children inside it were skipped. So every time items were removed, the old native views stayed alive in memory, invisible but never freed. ### Description of Change On Android, when the old page's view is destroyed, we now also disconnect all its children (labels, buttons, entries, etc.) — not just recycle the page. On iOS, when the renderer is disposed, we now disconnect the children inside the page — not just the page itself. This ensures all native views are properly freed when Shell items are removed. Validated the behavior in the following platforms - [x] Android - [ ] Windows - [x] iOS - [x ] Mac ### Issues Fixed Fixes dotnet#34898 ### Output ScreenShot |Before|After| |--|--| | <video src="https://github.com/user-attachments/assets/db75c53e-3fdf-4b28-ab16-aa48a6ea9cd7" >| <video src="https://github.com/user-attachments/assets/38ffe4ae-e6c8-4673-867c-61ade57a0b96">|
…net#34527) <!-- 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: Horizontalspacing / Verticalspacing is not not applied to the first column in GridItemLayout using CollectionView on Android platform. ### Root Cause: The grid spacing was not being distributed symmetrically across the active layout implementations, so edge items did not fully participate when spacing changed at runtime. ### Description of Change: - On Android, the fix in MauiRecyclerView.cs changes how RecyclerView padding is handled for GridItemsLayout. Android was already using SpacingItemDecoration, which applies half-spacing on all four sides of each item. Previously, negative RecyclerView padding canceled that spacing at the control edges. The branch keeps that negative-padding behavior for non-grid layouts, but disables it for GridItemsLayout, allowing the grid’s half-spacing to remain visible at the outer perimeter. This makes the first row and first column visually respond when spacing changes, but it also changes the grid behavior from spacing only between items to spacing around the outside edges as well. **Tested the behavior in the following platforms:** - [x] Android - [x] Windows - [ ] iOS - [ ] Mac ### Reference: N/A ### Issues Fixed: Fixes dotnet#34257 ### Screenshots | Before | After | |---------|--------| | <Video src="https://github.com/user-attachments/assets/578dda69-1d60-474c-a6d8-23b3f9d29a50" Width="300" Height="600"> | <Video src="https://github.com/user-attachments/assets/7f3826e6-5922-4b6f-a6b9-de581b7db6c3" Width="300" Height="600"> |
…crollView (dotnet#34352) > [!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! This PR enhances the event handling support for multiple MAUI controls by adding comprehensive implementation and validation for control-specific events, along with corresponding test coverage. The update includes the addition of events for Slider and ScrollView controls, ensuring proper event triggering and argument handling across different platforms.
c34d4d2 to
b701c11
Compare
@kubaflo , Based on AI suggestion, I have modified the fix |
|
/review -b feature/enhanced-reviewer |
1f8dd5f to
84345dc
Compare
16dc4f5 to
7c04ed7
Compare
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Issue Details
When a ScrollView is set to RightToLeft (RTL) flow direction and its Orientation is changed at runtime, the scrolling behavior is not working on iOS.
Description of Change
Added orientation tracking and enabled the logic to perform RTL layout handling during cross-platform arrange.
Issues Fixed
Fixes #32271
Tested the behavior in the following platforms.
Before.mov
After.mov