Skip to content

[iOS] Fix Scrollbar does not align with FlowDirection=RightToLeft in WebView and HybridWebView#30653

Merged
kubaflo merged 10 commits intodotnet:inflight/currentfrom
devanathan-vaithiyanathan:fix-30605
Mar 14, 2026
Merged

[iOS] Fix Scrollbar does not align with FlowDirection=RightToLeft in WebView and HybridWebView#30653
kubaflo merged 10 commits intodotnet:inflight/currentfrom
devanathan-vaithiyanathan:fix-30605

Conversation

@devanathan-vaithiyanathan
Copy link
Contributor

Note

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

Issue Details

FlowDirection was only applied to the WebView and HybridWebview, not its internal ScrollView, which handles the actual content layout and scrolling.

Description of Change

FlowDirection is now applied to the internal ScrollView of WKWebView to ensure correct layout direction.

Issues Fixed

Fixes #30605

Regarding Test case

For this case, it's not possible to write a test because the scrollbar does not appear during initial loading. It only becomes visible when a scroll action is performed, and even then, it remains visible only for a few seconds.

Tested the behavior in the following platforms.

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

@dotnet-policy-service dotnet-policy-service bot added community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration labels Jul 16, 2025
@devanathan-vaithiyanathan devanathan-vaithiyanathan marked this pull request as ready for review July 18, 2025 04:50
Copilot AI review requested due to automatic review settings July 18, 2025 04:50
@devanathan-vaithiyanathan devanathan-vaithiyanathan requested a review from a team as a code owner July 18, 2025 04:50
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR fixes a visual bug where scrollbars in WebView and HybridWebView did not align correctly with the FlowDirection when set to RightToLeft on iOS. The issue was that FlowDirection was only applied to the WebView container but not to its internal ScrollView component that handles the actual scrolling behavior.

  • Adds FlowDirection mapping to both WebView and HybridWebView handlers for iOS
  • Creates a specialized method to update FlowDirection on the internal ScrollView
  • Includes special handling for macOS Catalyst to refresh scroll indicators when FlowDirection changes

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/Core/src/Platform/iOS/WebViewExtensions.cs Adds new UpdateFlowDirectionForScrollView method with macOS Catalyst-specific scroll indicator refresh logic
src/Core/src/Handlers/WebView/WebViewHandler.iOS.cs Implements MapFlowDirection method for WebView to apply FlowDirection to internal ScrollView
src/Core/src/Handlers/WebView/WebViewHandler.cs Adds FlowDirection property mapping to WebView handler for iOS platform
src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.iOS.cs Implements MapFlowDirection method for HybridWebView to apply FlowDirection to internal ScrollView
src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.cs Adds FlowDirection property mapping to HybridWebView handler for iOS platform
Comments suppressed due to low confidence (3)

src/Core/src/Platform/iOS/WebViewExtensions.cs:67

  • [nitpick] The method name 'UpdateFlowDirectionForScrollView' is redundant since the parameter is already a UIScrollView. Consider renaming to 'UpdateFlowDirection' or 'UpdateFlowDirectionWithScrollIndicatorRefresh' to better reflect its purpose.
		internal static void UpdateFlowDirectionForScrollView(this UIKit.UIScrollView scrollView, IView view)

src/Core/src/Handlers/WebView/WebViewHandler.iOS.cs:67

  • This new MapFlowDirection functionality lacks test coverage. According to the coding guidelines, all code changes should have relevant test cases in TestCases.HostApp and TestCases.Shared.Tests to validate the FlowDirection behavior.
		internal static void MapFlowDirection(IWebViewHandler handler, IWebView webView)

src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.iOS.cs:71

  • This new MapFlowDirection functionality for HybridWebView lacks test coverage. According to the coding guidelines, all code changes should have relevant test cases in TestCases.HostApp and TestCases.Shared.Tests to validate the FlowDirection behavior.
		internal static void MapFlowDirection(IHybridWebViewHandler handler, IHybridWebView hybridWebView)

scrollView.UpdateFlowDirection(view);

// On macOS, we need to refresh the scroll indicators when flow direction changes
// But only for runtime changes, not during initial load
Copy link

Copilot AI Jul 18, 2025

Choose a reason for hiding this comment

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

The condition 'view.IsLoadedOnPlatform()' lacks documentation explaining why this check is necessary only for runtime changes and not during initial load. Consider adding a comment explaining the timing requirement.

Suggested change
// But only for runtime changes, not during initial load
// But only for runtime changes, not during initial load
// The view.IsLoadedOnPlatform() check ensures that this code is executed
// only after the view has been loaded on the platform. During the initial load,
// the scroll indicators do not need to be refreshed as they are set up correctly
// by default. This avoids unnecessary operations during the initial load phase.

Copilot uses AI. Check for mistakes.
@jsuarezruiz
Copy link
Contributor

/azp run MAUI-UITests-public

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).


public static IPropertyMapper<IHybridWebView, IHybridWebViewHandler> Mapper = new PropertyMapper<IHybridWebView, IHybridWebViewHandler>(ViewHandler.ViewMapper)
{
#if IOS
Copy link
Contributor

Choose a reason for hiding this comment

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

Not required on Catalyst?

Copy link
Contributor

Choose a reason for hiding this comment

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

Can include a comment documenting why is required on iOS?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jsuarezruiz ,
Adding iOS alone works for Mac as well.
For safety, I’ve added the Mac condition too.

Copy link
Member

@PureWeen PureWeen left a comment

Choose a reason for hiding this comment

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

merge conflict please rebase

@devanathan-vaithiyanathan
Copy link
Contributor Author

merge conflict please rebase

@PureWeen , I've resolved the merge conflicts

@rmarinho
Copy link
Member

rmarinho commented Feb 16, 2026

🤖 AI Summary

📊 Expand Full Review
🔍 Pre-Flight — Context & Validation
📝 Review SessionUpdate WebViewExtensions.cs · d620b08

Issue: #30605 - [iOS] Scrollbar does not align with FlowDirection=RightToLeft in WebView and HybridWebView
PR: #30653
Platforms Affected: iOS, macOS/MacCatalyst
Files Changed: 5 implementation files, 0 test files

Issue Summary

When setting FlowDirection="RightToLeft" on a WebView or HybridWebView in .NET MAUI, the scrollbar remains on the right-hand side (LTR default) instead of moving to the left side for RTL layout. This was confirmed on iOS and macOS Catalyst in version 9.0.82 SR8.2.

Root Cause (from PR): FlowDirection was only applied to the WebView/HybridWebView containers, not to their internal UIScrollView components which handle actual scrolling.

Files Changed

Implementation Files:

  • src/Core/src/Handlers/WebView/WebViewHandler.cs (+2) - Adds FlowDirection mapping for iOS/MacCatalyst
  • src/Core/src/Handlers/WebView/WebViewHandler.iOS.cs (+9) - Implements MapFlowDirection method
  • src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.cs (+1/-1) - Changes #if WINDOWS to #if WINDOWS || IOS
  • src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.iOS.cs (+9) - Implements MapFlowDirection method
  • src/Core/src/Platform/iOS/WebViewExtensions.cs (+23) - New UpdateFlowDirectionForScrollView extension method

Test Files: None (PR explicitly states scrollbar animation cannot be tested)

Critical Bug Found: WebViewHandler.cs Dead Code###

The PR introduces a serious regression in WebViewHandler.cs. The preprocessor structure is:

#if __ANDROID__
    [android mappings]
#elif __IOS__ || MACCATALYST     // NEW: catches iOS first!
    [nameof(IWebView.FlowDirection)] = MapFlowDirection,
#elif __IOS__                    // DEAD CODE - never reached on iOS!
    [nameof(WKUIDelegate)] = MapWKUIDelegate,    // MISSING from iOS!
    [nameof(IWebView.Background)] = MapBackground,  // MISSING from iOS!
#endif

Impact: On iOS, MapWKUIDelegate (JavaScript dialogs) and MapBackground (background color) are no longer registered in the property mapper, causing a regression in existing WebView behavior.

Fix needed: The #elif __IOS__ || MACCATALYST block must include all three mappings, or the condition structure must be rearranged.

Missing MacCatalyst in HybridWebViewHandler.cs###

HybridWebViewHandler.cs uses #if WINDOWS || IOS but NOT MACCATALYST:

#if WINDOWS || IOS   // MACCATALYST is missing!
    [nameof(IView.FlowDirection)] = MapFlowDirection,
#endif

The IOS preprocessor symbol is NOT defined for MacCatalyst (confirmed by existing code patterns using IOS || MACCATALYST throughout the codebase). This means HybridWebView FlowDirection fix doesn't apply to MacCatalyst.

Existing Test Not Updated###

An existing FeatureMatrix test (VerifyHybridWebViewWithFlowDirection) validates this behavior via screenshot comparison. It's currently disabled on iOS/MacCatalyst:

#if TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS
public void VerifyHybridWebViewWithFlowDirection()

The PR should have enabled this test for iOS and added iOS snapshots.

Reviewer Feedback

File:Line Reviewer Feedback Status
WebViewExtensions.cs:72 Copilot Suggested adding comment for IsLoadedOnPlatform() check Comment added in PR
HybridWebViewHandler.cs:72 jsuarezruiz "Not required on Author says fixed but MACCATALYST still missing Catalyst?"

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #30653 Apply FlowDirection to internal UIScrollView of PENDING (Gate) 5 files Has critical regression bug (WKUIDelegate dead code) WKWebView

🚦 Gate — Test Verification
📝 Review SessionUpdate WebViewExtensions.cs · d620b08

** FAILEDResult:**
Platform: ios
Mode: Full Verification

Gate Failure Reason

The PR adds no test files for the iOS fix. The VerifyHybridWebViewWithFlowDirection test that covers this behavior is explicitly disabled on iOS via preprocessor conditional:

#if TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS // Issue Link: ...#30605
public void VerifyHybridWebViewWithFlowDirection()

This test runs only on Android; it's excluded from iOS builds. The PR should have:

  1. Enabled this test for iOS (removed TEST_FAILS_ON_IOS from the condition)
  2. Added iOS snapshots for visual comparison

What Was Found

Gate Status

  • Tests FAIL without Cannot verify (no iOS test exists)fix:
  • Tests PASS with Cannot verify (no iOS test exists)fix:

🔧 Fix — Analysis & Comparison
📝 Review SessionUpdate WebViewExtensions.cs · d620b08

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #30653 Apply FlowDirection to internal UIScrollView of GATE FAILED (no tests) 5 files Has critical regression bug WKWebView

Exhausted: N/A (Gate did not try-fix phase skipped)pass
Selected Fix: N/A
Notes: try-fix phase was skipped because Gate failed. No iOS tests exist to verify fix candidates.


📋 Report — Final Recommendation
📝 Review SessionUpdate WebViewExtensions.cs · d620b08

Final Recommendation: REQUEST CHANGES##

Summary

PR #30653 attempts to fix a valid iOS/MacCatalyst issue (scrollbar not moving to RTL position when FlowDirection=RightToLeft is set on WebView/HybridWebView). The root cause identification is correct. However, the implementation introduces a critical regression that breaks existing iOS WebView behavior, and the PR is missing tests for the iOS fix.


Root Cause

FlowDirection was applied only to the WKWebView container, not to its internal UIScrollView. Since the scrollbar is rendered by the internal UIScrollView, it did not respect the RTL direction. The fix applies UpdateFlowDirection() to handler.PlatformView.ScrollView instead.


Critical Issues (Must Fix)

1. Dead Code Regression in WebViewHandler.cs (Critical)

The PR's preprocessor directive structure in WebViewHandler.cs creates unreachable dead code that removes existing iOS WebView functionality:

#if __ANDROID__
    [android stuff]
 only FlowDirection is mapped
    [nameof(IWebView.FlowDirection)] = MapFlowDirection,
#elif __IOS__                  // DEAD CODE - never reached on iOS!
    [nameof(WKUIDelegate)] =  MISSING on iOSMapWKUIDelegate,   // 
    [nameof(IWebView.Background)] =  MISSING on iOSMapBackground,  // 
#endif

Since __IOS__ is a subset of __IOS__ || MACCATALYST, the #elif __IOS__ block is never executed on iOS. This removes the WKUIDelegate and Background property mappings from iOS, which breaks:

  • JavaScript dialog support (alert(), confirm(), prompt())
  • WebView background color handling

Required fix: Merge all iOS/MacCatalyst mappings into a single block:

#elif __IOS__ || MACCATALYST
    [nameof(IWebView.FlowDirection)] = MapFlowDirection,
    [nameof(WKUIDelegate)] = MapWKUIDelegate,
    [nameof(IWebView.Background)] = MapBackground,
#endif

2. Missing MacCatalyst in HybridWebViewHandler.cs

HybridWebViewHandler.cs uses #if WINDOWS || IOS which does NOT include MacCatalyst:

#if WINDOWS || IOS   // MACCATALYST is missing!
    [nameof(IView.FlowDirection)] = MapFlowDirection,
#endif

The IOS preprocessor symbol is NOT defined for MacCatalyst (confirmed by existing code patterns throughout the codebase using IOS || MACCATALYST). The fix must use #if WINDOWS || IOS || MACCATALYST.


The PR adds no test files. There is an existing VerifyHybridWebViewWithFlowDirection test in HybridWebViewFeatureTests.cs that covers this behavior, but it's disabled on iOS:

#if TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS // Issue #30605

The PR should enable this test for iOS by removing TEST_FAILS_ON_IOS from the condition and adding iOS snapshots.


What Works

  • Root cause identification is correct
  • UpdateFlowDirectionForScrollView() extension method approach is sound
  • MacCatalyst scroll indicator refresh workaround is properly guarded by IsLoadedOnPlatform() check
  • WebViewHandler.iOS.cs and HybridWebViewHandler.iOS.cs implementations are correct
  • WebViewExtensions.cs code and comments are clear and well-documented

Title & Description Review

Current Title: [iOS] Fix Scrollbar does not align with FlowDirection=RightToLeft in WebView and HybridWebView
Assessment: Acceptable but verbose. Recommended: [iOS] WebView/HybridWebView: Apply FlowDirection to internal UIScrollView for RTL scrollbar

Current Description: The NOTE block is present, issue reference is correct, platform testing checkboxes included. However, it's missing documentation of the critical dead code issue introduced.


Required Changes Before Merge

  1. Fix WebViewHandler.cs: Merge WKUIDelegate and MapBackground into the #elif __IOS__ || MACCATALYST block
  2. Fix HybridWebViewHandler.cs: Change #if WINDOWS || IOS to #if WINDOWS || IOS || MACCATALYST
  3. Enable iOS test: Remove TEST_FAILS_ON_IOS from HybridWebViewFeatureTests.cs condition and add iOS snapshots

📋 Expand PR Finalization Review
Title: ✅ Good

Current: [iOS] Fix Scrollbar does not align with FlowDirection=RightToLeft in WebView and HybridWebView

Description: ✅ Good

Description needs updates. See details below.

✨ Suggested PR Description

Recommended Description for PR #30653

Assessment

The existing PR description is good quality and matches the implementation. It includes all required elements (NOTE block, issue details, description of change, issues fixed, platform testing, visual evidence).

Recommendation: Keep the existing description and add only a "Technical Details" section for future maintenance.


Enhanced Description (Additions Only)

Add this section after "Description of Change" and before "Issues Fixed":

### Technical Details

**Implementation:**
- Added `MapFlowDirection` mapper to iOS/MacCatalyst WebView and HybridWebView handlers
- Created `UpdateFlowDirectionForScrollView` extension method in `WebViewExtensions.cs`
- Applies `UpdateFlowDirection` to WKWebView's internal `ScrollView` component

**MacCatalyst-Specific Workaround:**
On macOS, scrollbar indicators don't automatically update when flow direction changes at runtime. The fix toggles scrollbar visibility to force a refresh:

```csharp
// On macOS, we need to refresh the scroll indicators when flow direction changes
// But only for runtime changes, not during initial load
if (OperatingSystem.IsMacCatalyst() && view.IsLoadedOnPlatform())
{
    bool showsVertical = scrollView.ShowsVerticalScrollIndicator;
    bool showsHorizontal = scrollView.ShowsHorizontalScrollIndicator;

    scrollView.ShowsVerticalScrollIndicator = false;
    scrollView.ShowsHorizontalScrollIndicator = false;

    scrollView.ShowsVerticalScrollIndicator = showsVertical;
    scrollView.ShowsHorizontalScrollIndicator = showsHorizontal;
}

This only happens for runtime changes (not initial load) to avoid visible flicker.

Why This Fix Works:

  • Before: FlowDirection only applied to WKWebView container, not its internal ScrollView
  • After: FlowDirection propagates to the internal ScrollView that handles scrollbar rendering
  • Result: Scrollbar position matches the specified flow direction (LTR or RTL)

---

## Complete Enhanced Description (Full Text)

If you prefer to see the complete description with additions:

```markdown
<!-- 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
FlowDirection was only applied to the WebView and HybridWebview, not its internal ScrollView, which handles the actual content layout and scrolling.

### Description of Change

<!-- Enter description of the fix in this section -->
FlowDirection is now applied to the internal ScrollView of WKWebView to ensure correct layout direction.

### Technical Details

**Implementation:**
- Added `MapFlowDirection` mapper to iOS/MacCatalyst WebView and HybridWebView handlers
- Created `UpdateFlowDirectionForScrollView` extension method in `WebViewExtensions.cs`
- Applies `UpdateFlowDirection` to WKWebView's internal `ScrollView` component

**MacCatalyst-Specific Workaround:**
On macOS, scrollbar indicators don't automatically update when flow direction changes at runtime. The fix toggles scrollbar visibility to force a refresh:

```csharp
// On macOS, we need to refresh the scroll indicators when flow direction changes
// But only for runtime changes, not during initial load
if (OperatingSystem.IsMacCatalyst() && view.IsLoadedOnPlatform())
{
    bool showsVertical = scrollView.ShowsVerticalScrollIndicator;
    bool showsHorizontal = scrollView.ShowsHorizontalScrollIndicator;

    scrollView.ShowsVerticalScrollIndicator = false;
    scrollView.ShowsHorizontalScrollIndicator = false;

    scrollView.ShowsVerticalScrollIndicator = showsVertical;
    scrollView.ShowsHorizontalScrollIndicator = showsHorizontal;
}

This only happens for runtime changes (not initial load) to avoid visible flicker.

Why This Fix Works:

  • Before: FlowDirection only applied to WKWebView container, not its internal ScrollView
  • After: FlowDirection propagates to the internal ScrollView that handles scrollbar rendering
  • Result: Scrollbar position matches the specified flow direction (LTR or RTL)

Issues Fixed

Fixes #30605

Regarding Test case

For this case, it's not possible to write a test because the scrollbar does not appear during initial loading. It only becomes visible when a scroll action is performed, and even then, it remains visible only for a few seconds.

Tested the behavior in the following platforms.

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

---

## Rationale for Keeping Most of Existing Description

The current description is well-structured and contains:

1. ✅ Required NOTE block for artifact testing
2. ✅ Clear issue explanation (root cause)
3. ✅ Accurate description of the fix
4. ✅ Proper issue linking (#30605)
5. ✅ Reasonable explanation for lack of automated tests
6. ✅ Cross-platform testing checklist
7. ✅ Visual before/after evidence

**What's missing:** Only technical implementation details for future agents/maintainers. The suggested addition fills this gap without replacing the existing quality content.

**Final Recommendation:** Add the "Technical Details" section to enhance the description for future maintenance, but keep all existing content intact.
</details>

<details>

<summary><b>Code Review: ⚠️ Issues Found</b></summary>

<br>

# Code Review Findings for PR #30653

## 🔴 Critical Issues (Must Fix Before Merge)

### Issue 1: Platform Directive Logic Error in WebViewHandler.cs

**File:** `src/Core/src/Handlers/WebView/WebViewHandler.cs` (lines 34-39)

**Severity:** HIGH - Breaks existing iOS functionality

**Problem:**

The `#elif` directives create unreachable code that will break WKUIDelegate and Background mappings on iOS.

**Current Code:**
```csharp
#if __ANDROID__
	[nameof(WebViewClient)] = MapWebViewClient,
	[nameof(WebChromeClient)] = MapWebChromeClient,
	[nameof(WebView.Settings)] =  MapWebViewSettings
#elif __IOS__ || MACCATALYST
	[nameof(IWebView.FlowDirection)] = MapFlowDirection,
#elif __IOS__
	[nameof(WKUIDelegate)] = MapWKUIDelegate,
	[nameof(IWebView.Background)] = MapBackground,
#endif

Analysis:

When compiling for iOS:

  1. Line 34: #elif __IOS__ || MACCATALYST evaluates to TRUE (because __IOS__ is defined)
  2. Line 35: FlowDirection mapping is included
  3. Line 36: #elif __IOS__ is NEVER evaluated because the previous #elif already matched
  4. Lines 37-38: WKUIDelegate and Background mappings are EXCLUDED from iOS builds

Impact:

  • ❌ iOS builds lose WKUIDelegate mapper
  • ❌ iOS builds lose Background mapper
  • ✅ MacCatalyst builds work correctly (only FlowDirection)

Root Cause:

The conditions overlap. __IOS__ is a subset of __IOS__ || MACCATALYST, so the second condition can never be reached on iOS.


Recommended Fix - Option A (Separate platforms):

#if __ANDROID__
	[nameof(WebViewClient)] = MapWebViewClient,
	[nameof(WebChromeClient)] = MapWebChromeClient,
	[nameof(WebView.Settings)] =  MapWebViewSettings
#elif __IOS__
	[nameof(IWebView.FlowDirection)] = MapFlowDirection,
	[nameof(WKUIDelegate)] = MapWKUIDelegate,
	[nameof(IWebView.Background)] = MapBackground,
#elif MACCATALYST
	[nameof(IWebView.FlowDirection)] = MapFlowDirection,
#endif

Why this works:

  • iOS gets all three mappings (FlowDirection, WKUIDelegate, Background)
  • MacCatalyst gets FlowDirection only
  • No overlap, no unreachable code

Alternative Fix - Option B (Group by specificity):

#if __ANDROID__
	[nameof(WebViewClient)] = MapWebViewClient,
	[nameof(WebChromeClient)] = MapWebChromeClient,
	[nameof(WebView.Settings)] =  MapWebViewSettings
#elif __IOS__
	[nameof(WKUIDelegate)] = MapWKUIDelegate,
	[nameof(IWebView.Background)] = MapBackground,
	[nameof(IWebView.FlowDirection)] = MapFlowDirection,
#elif MACCATALYST
	[nameof(IWebView.FlowDirection)] = MapFlowDirection,
#endif

Why this works:

  • iOS-specific mappings come first in the iOS block
  • Shared mapping (FlowDirection) comes last
  • MacCatalyst gets only the shared mapping

Verification Steps After Fix:

  1. Build for iOS: Verify WKUIDelegate and Background mappings are present
  2. Build for MacCatalyst: Verify only FlowDirection mapping is present
  3. Run existing iOS WebView tests to ensure no regression

🟡 Medium Priority Issues

Issue 2: Platform Symbol Inconsistency in HybridWebViewHandler.cs

File: src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.cs (line 10)

Current Code:

#if WINDOWS || IOS
	[nameof(IView.FlowDirection)] = MapFlowDirection,
#endif

Problem:

The code uses IOS (without underscores), but standard .NET MAUI platform symbols are:

  • __IOS__ (with underscores)
  • MACCATALYST (for macOS)
  • WINDOWS

Question: Does IOS work, or should this be __IOS__ || MACCATALYST?

Recommendation:

For consistency with the rest of the codebase, change to:

#if WINDOWS || __IOS__ || MACCATALYST
	[nameof(IView.FlowDirection)] = MapFlowDirection,
#endif

Impact: LOW - If IOS happens to work, this is cosmetic. However, using standard symbols prevents confusion.


🟢 Low Priority / Suggestions

Suggestion 1: Platform Title Accuracy

File: PR Title

Current: [iOS] Fix Scrollbar does not align with FlowDirection=RightToLeft in WebView and HybridWebView

Observation:

The fix applies to both iOS and MacCatalyst (macOS). The issue report lists "macOS, iOS" as affected platforms.

Recommendation:

Consider updating title to: [iOS/macOS] Fix Scrollbar does not align with FlowDirection=RightToLeft in WebView and HybridWebView

Impact: Documentation clarity only.


Suggestion 2: Add Technical Details to PR Description

Enhancement for Future Maintenance:

Consider adding a "Technical Details" section to the PR description explaining:

### Technical Details

**Implementation:**
- Added `MapFlowDirection` to iOS/MacCatalyst WebView and HybridWebView handlers
- Created `UpdateFlowDirectionForScrollView` extension method in `WebViewExtensions.cs`
- Applies `UpdateFlowDirection` to WKWebView's internal `ScrollView` component

**MacCatalyst-Specific Workaround:**
- On macOS, scrollbar indicators don't update automatically when flow direction changes at runtime
- Solution: Toggle scrollbar visibility to force refresh
- Only applied for runtime changes (not initial load) to avoid flicker

**Why This Fix Works:**
- Previous: FlowDirection only applied to WKWebView itself
- Now: FlowDirection propagates to the internal ScrollView that handles scrollbar rendering
- Result: Scrollbar position matches the flow direction

Impact: Helps future agents understand the macOS workaround and why it's needed.


✅ Positive Code Quality Observations

  1. Good abstraction - The UpdateFlowDirectionForScrollView extension method properly encapsulates platform-specific logic

  2. Defensive programming - Null checks in both MapFlowDirection implementations:

    var scrollView = handler.PlatformView?.ScrollView;
    if (scrollView == null)
        return;
  3. Clear comments - The macOS scrollbar refresh workaround is well-documented:

    // On macOS, we need to refresh the scroll indicators when flow direction changes
    // But only for runtime changes, not during initial load
  4. Code reuse - Both WebView and HybridWebView share the same extension method

  5. Follows existing patterns - Implementation matches MAUI's handler mapping conventions

  6. No breaking changes - Only adds new mappers, doesn't modify existing behavior


Summary

Must Fix (Blocking)

  • ✅ Fix platform directive logic in WebViewHandler.cs (Critical - breaks iOS)

Should Fix (Recommended)

  • ✅ Use standard platform symbols (__IOS__ instead of IOS) in HybridWebViewHandler.cs

Nice to Have (Optional)

  • Consider updating PR title to [iOS/macOS]
  • Consider adding technical details section to PR description

Overall Code Quality

The implementation is sound and follows MAUI patterns. Once the critical platform directive issue is fixed, this PR will be ready to merge.


@rmarinho rmarinho added s/agent-approved AI agent recommends approval - PR fix is correct and optimal s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Feb 16, 2026
@kubaflo
Copy link
Contributor

kubaflo commented Feb 16, 2026

@devanathan-vaithiyanathan looks good! But do you think you could add some tests?

@kubaflo kubaflo removed the s/agent-fix-win AI found a better alternative fix than the PR label Feb 16, 2026
@devanathan-vaithiyanathan
Copy link
Contributor Author

For this case, it's not possible to write a test because the scrollbar does not appear during initial loading. It only becomes visible when a scroll action is performed, and even then, it remains visible only for a few seconds.

@kubaflo, For this case, it's not possible to write a test because the scrollbar does not appear during initial loading. It only becomes visible when a scroll action is performed, and even then, it remains visible only for a few seconds.

@kubaflo
Copy link
Contributor

kubaflo commented Mar 11, 2026

📋 PR Finalization Review

Title: ✅ Good

Current: [iOS] Fix Scrollbar does not align with FlowDirection=RightToLeft in WebView and HybridWebView

Description: ⚠️ Needs Update
  • Says [iOS] only but the code explicitly handles MacCatalyst too (WebViewExtensions.cs has a MacCatalyst-specific scroll indicator refresh block; WebViewHandler.cs uses #elif __IOS__ || MACCATALYST)
  • Describes the visual symptom ("scrollbar does not align") rather than the fix ("FlowDirection not propagated to WKWebView's internal ScrollView")
  • "Fix" prefix is noisy — prefer behavior-describing titles

✨ 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

On iOS and MacCatalyst, WKWebView does not expose a SemanticContentAttribute setter for RTL layout control. The actual content direction and scrollbar positioning are controlled by WKWebView's internal UIScrollView. When FlowDirection was mapped, it was applied to the outer WKWebView container but not propagated to the internal ScrollView, leaving the scrollbar on the wrong (LTR) side.

Description of Change

FlowDirection is now propagated to the internal UIScrollView of WKWebView for both WebViewHandler and HybridWebViewHandler on iOS and MacCatalyst.

Changes:

  • WebViewHandler.cs — Registers MapFlowDirection in the property mapper for __IOS__ || MACCATALYST
  • WebViewHandler.iOS.cs — Implements MapFlowDirection by extracting WKWebView.ScrollView and calling UpdateFlowDirectionForScrollView
  • HybridWebViewHandler.cs — Extends the existing WINDOWS FlowDirection mapper condition to include IOS || MACCATALYST
  • HybridWebViewHandler.iOS.cs — Same MapFlowDirection implementation as WebViewHandler
  • WebViewExtensions.cs — New UpdateFlowDirectionForScrollView(UIScrollView, IView) extension that:
    1. Calls scrollView.UpdateFlowDirection(view) to set direction
    2. On MacCatalyst only: toggles scroll indicators off then on to force a re-render (MacCatalyst does not automatically refresh indicator position on direction change at runtime)

Issues Fixed

Fixes #30605

Platforms Tested

  • iOS
  • Mac
  • Android (not affected by this change)
  • Windows (not affected by this change)
Code Review: ⚠️ Issues Found

Code Review Findings — PR #30653

PR: [iOS/MacCatalyst] Propagate FlowDirection to WKWebView internal ScrollView
Files reviewed: 5 changed files (+44/-1 lines)


🔴 Critical Issues

1. WebViewHandler.cs — Dead Code: WKUIDelegate and Background Mappers Removed from iOS

File: src/Core/src/Handlers/WebView/WebViewHandler.cs

Problem:

The PR inserts a new #elif __IOS__ || MACCATALYST branch before the existing #elif __IOS__ branch inside the same #if __ANDROID__ chain. In C# preprocessor chains, only the first matching elif executes. Since __IOS__ satisfies __IOS__ || MACCATALYST, the existing #elif __IOS__ block is now dead code on iOS:

Resulting code after PR:

#if __ANDROID__
    [nameof(WebViewClient)] = MapWebViewClient,
    [nameof(WebChromeClient)] = MapWebChromeClient,
    [nameof(WebView.Settings)] = MapWebViewSettings
#elif __IOS__ || MACCATALYST    // ← NEW — matches iOS first
    [nameof(IWebView.FlowDirection)] = MapFlowDirection,
#elif __IOS__                   // ← DEAD CODE — never reached on iOS
    [nameof(WKUIDelegate)] = MapWKUIDelegate,       // ← Lost!
    [nameof(IWebView.Background)] = MapBackground,  // ← Lost!
#endif

Impact:

  • On iOS: Only FlowDirection is mapped. WKUIDelegate and Background are never registered.
    • WKUIDelegate being absent means JavaScript dialogs (alert(), confirm(), prompt()) will not work in iOS WebView.
    • Background being absent means the WebView background color won't be applied.
  • On MacCatalyst: No regression (MacCatalyst previously matched neither #elif __IOS__ since MACCATALYST ≠ __IOS__, so WKUIDelegate/Background were never registered there anyway).

Recommendation:

Preserve all existing iOS-specific entries by merging them with the new condition, or use a separate #if block:

Option A — Merge into one elif (preferred):

#if __ANDROID__
    [nameof(WebViewClient)] = MapWebViewClient,
    [nameof(WebChromeClient)] = MapWebChromeClient,
    [nameof(WebView.Settings)] = MapWebViewSettings
#elif __IOS__ || MACCATALYST
    [nameof(WKUIDelegate)] = MapWKUIDelegate,
    [nameof(IWebView.Background)] = MapBackground,
    [nameof(IWebView.FlowDirection)] = MapFlowDirection,
#endif

Option B — Separate #if block (like WINDOWS FlowDirection is handled):

#if __IOS__ || MACCATALYST
    [nameof(IWebView.FlowDirection)] = MapFlowDirection,
#endif
#if __ANDROID__
    [nameof(WebViewClient)] = MapWebViewClient,
    [nameof(WebChromeClient)] = MapWebChromeClient,
    [nameof(WebView.Settings)] = MapWebViewSettings
#elif __IOS__
    [nameof(WKUIDelegate)] = MapWKUIDelegate,
    [nameof(IWebView.Background)] = MapBackground,
#endif

Option A is simpler; Option B preserves the original iOS-MacCatalyst separation for WKUIDelegate/Background (if those are intentionally iOS-only).


🟡 Moderate Issues

2. HybridWebViewHandler.cs — MacCatalyst Not Covered in Mapper

File: src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.cs

Problem:

The PR changes:

-#if WINDOWS
+#if WINDOWS || IOS
    [nameof(IView.FlowDirection)] = MapFlowDirection,
#endif

The implementation file HybridWebViewHandler.iOS.cs uses .iOS.cs extension, which compiles for both iOS and MacCatalyst. The WebViewExtensions.cs helper even contains MacCatalyst-specific logic (OperatingSystem.IsMacCatalyst()). However, the mapper registration does not include MACCATALYST.

Confirmed that IOSIOS || MACCATALYST in this file — the same file uses #if IOS || MACCATALYST in other places, proving they are distinct symbols:

// Elsewhere in HybridWebViewHandler.cs:
#if IOS || MACCATALYST
    var name = header.Key?.ToString();

Impact: HybridWebView FlowDirection fix will not work on MacCatalyst builds.

Note: A reviewer (jsuarezruiz) asked about this. The author replied "Adding iOS alone works for Mac as well" — but this is incorrect based on how the symbols are used elsewhere in the same file.

Recommendation:

#if WINDOWS || IOS || MACCATALYST
    [nameof(IView.FlowDirection)] = MapFlowDirection,
#endif

3. Trailing Whitespace in New Code

Files:

  • src/Core/src/Handlers/WebView/WebViewHandler.iOS.cs (line after null return)
  • src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.iOS.cs (line after null return)

Problem: Both MapFlowDirection implementations have a trailing whitespace line after the early-return:

if (scrollView == null)
    return;trailing whitespace here
scrollView.UpdateFlowDirectionForScrollView(webView);

Recommendation: Remove the trailing whitespace. Formatting check (dotnet format) would catch this.


✅ Looks Good

  • WebViewExtensions.cs — The UpdateFlowDirectionForScrollView extension method is well-structured. The MacCatalyst-specific scroll indicator refresh (toggle off/on) is a clean workaround for the indicator not re-rendering after direction change. The comment added explains the IsLoadedOnPlatform() guard clearly.

  • WebViewHandler.iOS.cs and HybridWebViewHandler.iOS.cs — Both MapFlowDirection implementations correctly retrieve WKWebView.ScrollView with a null guard and delegate to the shared UpdateFlowDirectionForScrollView extension. The pattern is consistent between WebView and HybridWebView.

  • Root cause identification — The approach of targeting WKWebView.ScrollView rather than WKWebView itself is correct: WKWebView doesn't expose a SemanticContentAttribute setter; its internal UIScrollView handles the actual RTL layout and scroll indicator positioning.

  • NOTE block — Present at the top of the PR description ✅


Summary Table

Severity File Issue
🔴 Critical WebViewHandler.cs WKUIDelegate + Background mappers become dead code on iOS due to incorrect #elif ordering
🟡 Moderate HybridWebViewHandler.cs MACCATALYST missing from mapper condition — HybridWebView FlowDirection fix won't work on Mac
🟡 Minor WebViewHandler.iOS.cs, HybridWebViewHandler.iOS.cs Trailing whitespace after null-return lines
ℹ️ Info PR title Says [iOS] only — should be [iOS/MacCatalyst]
ℹ️ Info PR description Android and Windows listed as tested, but no code changes affect those platforms

@github-actions
Copy link
Contributor

🚀 Dogfood this PR with:

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

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

Or

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

@kubaflo kubaflo changed the base branch from main to inflight/current March 14, 2026 12:44
@kubaflo kubaflo merged commit 4c61139 into dotnet:inflight/current Mar 14, 2026
22 of 31 checks passed
PureWeen pushed a commit that referenced this pull request Mar 19, 2026
…WebView and HybridWebView (#30653)

<!-- 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
FlowDirection was only applied to the WebView and HybridWebview, not its
internal ScrollView, which handles the actual content layout and
scrolling.

### Description of Change

<!-- Enter description of the fix in this section -->
FlowDirection is now applied to the internal ScrollView of WKWebView to
ensure correct layout direction.

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

### Regarding Test case
For this case, it's not possible to write a test because the scrollbar
does not appear during initial loading. It only becomes visible when a
scroll action is performed, and even then, it remains visible only for a
few seconds.

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

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


| Before  | After  |
|---------|--------|
| **iOS**<br> <video
src="https://github.com/user-attachments/assets/eaf6620b-5f00-402c-b191-2ba881cacac2"
width="300" height="600"> | **iOS**<br> <video
src="https://github.com/user-attachments/assets/b038125e-5dbf-483b-aa7d-640f2c72555e"
width="300" height="600"> |
PureWeen pushed a commit that referenced this pull request Mar 24, 2026
…WebView and HybridWebView (#30653)

<!-- 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
FlowDirection was only applied to the WebView and HybridWebview, not its
internal ScrollView, which handles the actual content layout and
scrolling.

### Description of Change

<!-- Enter description of the fix in this section -->
FlowDirection is now applied to the internal ScrollView of WKWebView to
ensure correct layout direction.

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

### Regarding Test case
For this case, it's not possible to write a test because the scrollbar
does not appear during initial loading. It only becomes visible when a
scroll action is performed, and even then, it remains visible only for a
few seconds.

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

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


| Before  | After  |
|---------|--------|
| **iOS**<br> <video
src="https://github.com/user-attachments/assets/eaf6620b-5f00-402c-b191-2ba881cacac2"
width="300" height="600"> | **iOS**<br> <video
src="https://github.com/user-attachments/assets/b038125e-5dbf-483b-aa7d-640f2c72555e"
width="300" height="600"> |
kubaflo pushed a commit that referenced this pull request Mar 25, 2026
…: RightToLeft) device tests (#34645)

<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Details
This PR #30653 causes [WebView] FlowDirection is set
correctly(flowDirection: RightToLeft) device tests was failed.


### Description of Change

<!-- Enter description of the fix in this section -->
The root cause is that MapFlowDirection only updated the internal
UIScrollView of WKWebView, but not the WKWebView itself. Since the test
reads SemanticContentAttribute directly from the WKWebView platform
view, it always returned LeftToRight regardless of the set value. So
updated the WebView FlowDirection as well.

### Test fixed
* [WebView] FlowDirection is set correctly(flowDirection: RightToLeft)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-controls-scrollview ScrollView community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration platform/ios s/agent-approved AI agent recommends approval - PR fix is correct and optimal s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[iOS] Scrollbar does not align with FlowDirection=RightToLeft in WebView and HybridWebView

8 participants