[iOS] Fix ContentPage BackgroundImageSource not working#33297
[iOS] Fix ContentPage BackgroundImageSource not working#33297PureWeen merged 5 commits intodotnet:inflight/currentfrom
Conversation
|
Hey there @@Shalini-Ashokan! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
There was a problem hiding this comment.
Pull request overview
This PR fixes ContentPage BackgroundImageSource on iOS, which was incorrectly tiling images instead of scaling them. The fix replaces the pattern-based UIColor.FromPatternImage approach with a scalable CALayer that stretches the image to fill the view bounds, matching the behavior on other platforms.
Key changes:
- Replaced
UIColor.FromPatternImagewith aStaticCALayercontaining a CGImage - Added proper layer cleanup via
RemoveBackgroundLayer()before setting new background - Set
ContentsGravity = CALayer.GravityResizeto scale the image instead of tiling
| var cgImage = backgroundImage.CGImage; | ||
| if (cgImage == null && backgroundImage.CIImage != null) | ||
| { | ||
| using var context = CoreImage.CIContext.Create(); | ||
| cgImage = context.CreateCGImage(backgroundImage.CIImage, backgroundImage.CIImage.Extent); | ||
| } | ||
|
|
||
| if (cgImage == null) | ||
| return; | ||
|
|
||
| var imageLayer = new StaticCALayer | ||
| { | ||
| Name = BackgroundLayerName, | ||
| Contents = cgImage, | ||
| Frame = platformView.Bounds, | ||
| ContentsGravity = CoreAnimation.CALayer.GravityResize | ||
| }; | ||
|
|
||
| platformView.BackgroundColor = UIColor.Clear; | ||
| platformView.InsertBackgroundLayer(imageLayer, 0); |
There was a problem hiding this comment.
The CGImage created by CreateCGImage at line 418 is not being disposed, which will cause a memory leak. When CreateCGImage is called, it returns a new CGImage with +1 retain count that the caller owns and must dispose.
Since CALayer.Contents retains the CGImage when assigned, you should dispose the cgImage immediately after assigning it to the layer's Contents property (line 427). The layer will maintain its own reference, so disposing your reference won't affect the layer's ability to display the image.
Add a disposal call after line 433:
- If cgImage came from backgroundImage.CGImage (line 414), it doesn't need disposal as it's owned by the UIImage
- If cgImage came from CreateCGImage (line 418), it must be disposed after assignment to avoid a leak
You'll need to track which case applies, or alternatively, always use CreateCGImage and dispose it consistently.
kubaflo
left a comment
There was a problem hiding this comment.
Code Review - PR #33297
✅ Overall Assessment: APPROVE with minor recommendations
The fix is technically correct and well-implemented.
Summary
This PR fixes a long-standing iOS-specific bug where ContentPage.BackgroundImageSource would tile the image in a repeating pattern instead of scaling it to fill the view. The fix replaces the pattern-based approach with a layer-based solution that scales the image properly.
What's Fixed
Root Cause:
- Previous implementation used
UIColor.FromPatternImage()which creates tiled patterns without scaling - iOS's pattern color API doesn't support aspect fill/resize - it always tiles at original size
- This is fundamentally incompatible with MAUI's expected behavior
Solution:
- Replaces pattern color with
StaticCALayercontaining the image - Uses
CALayer.ContentsGravity = GravityResizeto scale/stretch the image - Follows existing MAUI pattern used for gradient backgrounds (
StaticCAGradientLayer) - Properly manages layer lifecycle with
RemoveBackgroundLayer()before adding new layer
Memory Management:
- Properly tracks and disposes created CGImage
- If
CreateCGImage()was called, disposes after assignment (layer retains its own reference) - If CGImage comes from
UIImage.CGImage, no disposal needed (owned by UIImage)
Code Quality: Excellent
✅ Minimal, focused changes - Only 28 additions, 1 deletion
✅ Proper resource management - Tracks and disposes CGImage when created
✅ Defensive programming - Multiple null checks, early returns
✅ Consistent with codebase - Follows existing patterns (StaticCAGradientLayer)
✅ Clear comments - Explains CGImage disposal logic
✅ No breaking changes - No public API changes
iOS Platform-Specific Considerations
✅ Excellent iOS-specific handling:
- Layer Management: Uses
BackgroundLayerNameconstant for proper layer identification/removal - Bounds Observation:
StaticCALayerimplementsIAutoSizableCALayerwhich uses KVO to observe superlayer bounds changes - CIImage Fallback: Handles both
CGImageandCIImagesources (creates CGImage from CIImage if needed) - Frame Initialization: Layer frame set to
platformView.Boundsinitially, then auto-updates - BackgroundColor Clearing: Sets
UIColor.Clearto avoid color bleeding through
Test Coverage
✅ Adequate test coverage:
- Existing Test (
Issue17789): Verifies via screenshot comparison - Test page:
Issues17789.xaml- SetsBackgroundImageSource="oasis.jpg" - NUnit test:
Issue17789.cs- Validates correct scaling behavior - Snapshot updated:
ContentPageBackgroundImageSourceWorks.png
Assessment:
- ✅ Basic functionality covered (image displays scaled, not tiled)
- ✅ Platform-specific (marked as iOS)
⚠️ Missing edge case tests (rotation, different aspect ratios, rapid changes)
Minor Concerns
-
Async Race Condition (Low Priority)
- If user rapidly changes
BackgroundImageSource, multiple asyncGetImageAsync()calls are in flight - Last call to complete "wins" but earlier calls still execute
- Could consider cancellation token for cleanup
- Severity: Low - unlikely user scenario, minimal impact
- If user rapidly changes
-
ContentsGravity Behavior (Documentation)
CALayer.GravityResizestretches/distorts image to fill bounds- Android/Windows might preserve aspect ratio differently
- PR description says "matches Android's scaling behavior"
- Recommendation: Document expected behavior vs. aspect-preserving options
-
Test Coverage Gaps
- No explicit test for rotation/resize scenarios
- No test for rapid image source changes
- No test for different image sizes/aspect ratios
Historical Context
- Issue #17789 was previously "fixed" in commit
5dcb3406af(Nov 2023) - That fix created
PageExtensions.csbut the root bug was inViewExtensions.csusingFromPatternImage() - This fix addresses the root cause directly - more maintainable solution
Recommendation
✅ APPROVE - The fix is solid and addresses the root cause properly. The minor concerns are edge cases that don't block approval.
Optional Enhancements:
- Document
GravityResizestretching behavior for future reference - Consider adding rotation test case
- Consider cancellation token for rapid image source changes
Great work fixing this long-standing issue! 🎉
kubaflo
left a comment
There was a problem hiding this comment.
Deep Independent Review - PR #33297
🤖 Copilot Deep Review Session Summary
This review was conducted through an interactive deep-dive session following the pr-reviewer-detailed workflow:
- "find reviewable prs" → Located PR #33297 (Syncfusion partner PR, iOS BackgroundImageSource fix)
- "yes, review #33297" → Standard review posted (basic code analysis)
- "please review #33297" → User requested deep independent review
- "yes" → Started deep review workflow with independent analysis
- Phase 1: Independent Analysis (WITHOUT looking at PR diff):
- "Look at the PR's implementation and compare it to my approach" → Compared approaches
- Phase 2: Validation:
- Ran UI test WITH fix → ✅ PASS
- Ran UI test WITHOUT fix → ❌ FAIL (17.91% screenshot difference - tiling visible)
- Phase 3: Comparison → Identified critical memory leak in my approach that PR correctly handles
✅ Recommendation: APPROVE
Summary: The PR's implementation is superior to my independent proposal and correctly fixes a long-standing root cause with proper memory management. The fix handles edge cases I initially missed, particularly CIImage sources and CGImage disposal.
Root Cause Analysis (Independent)
Historical Timeline:
- 2022 (commit
f894684b60): OriginalBackgroundImageSourceimplementation usedUIColor.FromPatternImage() - Nov 2023 (PR #17789): Attempted fix created
PageExtensions.csbut called the SAME buggyUpdateBackgroundImageSourceAsync() - April 2024 (Issue #21594): Bug still reported - image tiles instead of scaling
- Dec 2024 (This PR #33297): Correctly fixes the root cause in
ViewExtensions.cs
Root Cause:
UIColor.FromPatternImage() API creates tiled patterns at original image size with NO scaling options. This is fundamentally incompatible with MAUI's expected behavior (scale to fill). The bug has existed since the original 2022 implementation.
Fix Comparison: PR vs My Independent Approach
| Aspect | PR's Implementation | My Initial Proposal | Winner |
|---|---|---|---|
| Core Approach | ✅ CALayer with GravityResize | ✅ CALayer with GravityResize | Tie |
| CGImage Handling | ✅ Handles both CGImage and CIImage | ❌ Assumed CGImage only | PR ✅ |
| Memory Management | ✅ Tracks and disposes created CGImage | ❌ Memory leak for CIImage sources | PR ✅ |
| Null Safety | ✅ Multiple null checks | PR ✅ | |
| Code Comments | ✅ Explains disposal logic | ❌ Missing | PR ✅ |
| Lines Changed | 28 additions | ~20 estimated | Tie |
| Test Validation | ✅ Passes with fix, fails without | Not tested | PR ✅ |
Critical Issue I Missed: Memory Leak 🔴
The PR correctly handles a memory leak scenario I didn't consider:
// When UIImage has CIImage but no CGImage:
var cgImage = backgroundImage.CGImage; // null
var shouldDisposeCGImage = false;
if (cgImage == null && backgroundImage.CIImage != null)
{
using var context = CoreImage.CIContext.Create();
// CreateCGImage() allocates NEW CGImage → we OWN it
cgImage = context.CreateCGImage(backgroundImage.CIImage, backgroundImage.CIImage.Extent);
shouldDisposeCGImage = true; // ← Must dispose!
}
// ... later ...
if (shouldDisposeCGImage)
cgImage?.Dispose(); // ← Clean up created CGImageWhy This Matters:
CreateCGImage()creates a NEW CGImage that the caller owns → must disposeUIImage.CGImageproperty returns a reference owned by UIImage → don't dispose- My approach would have leaked memory for any CIImage-based sources
Test Validation ✅
Test: Issue17789.cs - ContentPage BackgroundImageSource
| Scenario | Result | Screenshot Diff | Evidence |
|---|---|---|---|
| WITH fix | ✅ PASS | Matches baseline | Image scales correctly |
| WITHOUT fix | ❌ FAIL | 17.91% difference | Tiled pattern visible |
Test Quality: ✅ Excellent
- Uses visual regression testing (screenshot comparison)
- Correctly catches the bug (fails without fix, passes with fix)
- Appropriate for this visual issue
- Test existed from previous fix attempt (2023) - was incorrectly passing before
Validation command used:
pwsh .github/scripts/BuildAndRunHostApp.ps1 -Platform ios -TestFilter "Issue17789"iOS Platform Considerations ✅
The PR demonstrates excellent iOS-specific handling:
- Layer Management: Uses
BackgroundLayerNameconstant for proper layer identification/removal - Bounds Auto-Resize:
StaticCALayerimplementsIAutoSizableCALayerwhich uses KVO to observe superlayer bounds changes - CIImage Fallback: Handles both
CGImageandCIImagesources (creates CGImage from CIImage if needed) - Z-Order: Inserts layer at index 0 (bottom of layer stack)
- BackgroundColor Clearing: Sets
UIColor.Clearto avoid color bleeding through
iOS API Usage - All Correct:
CoreAnimation.CALayer.GravityResize- stretches image to fill boundsInsertBackgroundLayer(imageLayer, 0)- proper z-orderCoreImage.CIContext.Create()- standard CIImage → CGImage conversionRemoveBackgroundLayer()- cleanup before adding new layer
Edge Cases Handled ✅
| Edge Case | PR Handles It? | How |
|---|---|---|
| CIImage-only sources | ✅ Yes | Converts via CreateCGImage() and disposes afterward |
| Null image sources | ✅ Yes | Multiple null checks with early returns |
| Memory management | ✅ Yes | Tracks and disposes created CGImage with shouldDisposeCGImage flag |
| View resize (rotation) | ✅ Yes | IAutoSizableCALayer auto-resizes on bounds changes |
| Rapid image changes | ✅ Yes | RemoveBackgroundLayer() cleans up previous layer first |
| Large images | ✅ Yes | Standard iOS memory management via CALayer |
Potential Regressions: None Identified ✅
This fix:
- ✅ Only affects iOS
BackgroundImageSourcerendering path - ✅ Doesn't change public APIs or method signatures
- ✅ Follows existing MAUI pattern used for gradients (
StaticCAGradientLayer) - ✅ No performance concerns (layer-based approach is standard iOS practice)
- ✅ Backward compatible - replaces broken tiling with correct scaling
Regression testing not needed - this is a pure bug fix that makes iOS match Android/Windows behavior.
What I Learned from This Review 📚
What I Got Right:
- ✅ Identified
FromPatternImage()as the root cause (correct) - ✅ Proposed CALayer approach with
GravityResize(same as PR) - ✅ Recognized need for
RemoveBackgroundLayer()cleanup - ✅ Understood layer auto-resize requirement
What I Missed (and PR got right):
- ❌ CIImage handling - Assumed CGImage always available (wrong!)
- ❌ Memory management - Would have leaked for CIImage sources (critical bug!)
- ❌ Comprehensive null checking - PR is more defensive
Key Takeaway: Always check for multiple image source types (CGImage vs CIImage) in iOS and understand their ownership semantics. The distinction between "we created it" (must dispose) vs "UIImage owns it" (don't dispose) is critical.
Code Quality Assessment ✅
Excellent code quality:
- ✅ Minimal, focused changes (28 additions, 1 deletion)
- ✅ Proper resource management with tracking flag
- ✅ Defensive programming with multiple null checks
- ✅ Consistent with codebase patterns (
StaticCAGradientLayer) - ✅ Clear, explanatory comments for complex logic
- ✅ No breaking changes to public APIs
Final Recommendation
✅ APPROVE - This PR correctly fixes the long-standing root cause with excellent memory management and iOS best practices.
Why This Fix is Superior:
- Handles edge cases my approach missed (CIImage sources)
- Prevents memory leaks (proper CGImage disposal)
- More defensive (comprehensive null checking)
- Well-documented (comments explain complex disposal logic)
No changes requested - the implementation is production-ready.
Optional Enhancement (Not Blocking)
Documentation suggestion: Consider adding a comment explaining GravityResize behavior:
// GravityResize stretches image to fill bounds without preserving aspect ratio
// This matches Android's ScaleType.FIT_XY and Windows' Stretch.Fill behavior
imageLayer.ContentsGravity = CoreAnimation.CALayer.GravityResize;This would help future maintainers understand the intentional stretching behavior vs aspect-preserving alternatives.
Great work fixing this 3-year-old bug! 🎉
<!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Details ContentPage background images display incorrectly on iOS by tiling in a repeating pattern instead of showing a single scaled image. ### Root Cause The iOS implementation used a pattern color API that creates tiled patterns without scaling options. This tiles the image at original size rather than scaling it to fill the view. ### Description of Change Replaced the pattern color approach with a scalable layer that stretches the image to fill view bounds. This matches Android's scaling behavior and follows MAUI's standard layer management pattern used for gradient backgrounds. Validated the behavior in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Issues Fixed Fixes #21594 ### Test case Test case Issue17789 is already included in the source. ### Output ScreenShot | Before | After | |---------|--------| | <img width="354" height="735" alt="BeforeFix" src="https://github.com/user-attachments/assets/8520b49b-617c-46e5-a29a-3a9b26e5b0c0" /> | <img width="354" height="735" alt="AfterFix" src="https://github.com/user-attachments/assets/402a8975-4d2b-4e2f-a362-ae22bcedec56" /> |
<!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Details ContentPage background images display incorrectly on iOS by tiling in a repeating pattern instead of showing a single scaled image. ### Root Cause The iOS implementation used a pattern color API that creates tiled patterns without scaling options. This tiles the image at original size rather than scaling it to fill the view. ### Description of Change Replaced the pattern color approach with a scalable layer that stretches the image to fill view bounds. This matches Android's scaling behavior and follows MAUI's standard layer management pattern used for gradient backgrounds. Validated the behavior in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Issues Fixed Fixes #21594 ### Test case Test case Issue17789 is already included in the source. ### Output ScreenShot | Before | After | |---------|--------| | <img width="354" height="735" alt="BeforeFix" src="https://github.com/user-attachments/assets/8520b49b-617c-46e5-a29a-3a9b26e5b0c0" /> | <img width="354" height="735" alt="AfterFix" src="https://github.com/user-attachments/assets/402a8975-4d2b-4e2f-a362-ae22bcedec56" /> |
<!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Details ContentPage background images display incorrectly on iOS by tiling in a repeating pattern instead of showing a single scaled image. ### Root Cause The iOS implementation used a pattern color API that creates tiled patterns without scaling options. This tiles the image at original size rather than scaling it to fill the view. ### Description of Change Replaced the pattern color approach with a scalable layer that stretches the image to fill view bounds. This matches Android's scaling behavior and follows MAUI's standard layer management pattern used for gradient backgrounds. Validated the behavior in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Issues Fixed Fixes #21594 ### Test case Test case Issue17789 is already included in the source. ### Output ScreenShot | Before | After | |---------|--------| | <img width="354" height="735" alt="BeforeFix" src="https://github.com/user-attachments/assets/8520b49b-617c-46e5-a29a-3a9b26e5b0c0" /> | <img width="354" height="735" alt="AfterFix" src="https://github.com/user-attachments/assets/402a8975-4d2b-4e2f-a362-ae22bcedec56" /> |
<!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Details ContentPage background images display incorrectly on iOS by tiling in a repeating pattern instead of showing a single scaled image. ### Root Cause The iOS implementation used a pattern color API that creates tiled patterns without scaling options. This tiles the image at original size rather than scaling it to fill the view. ### Description of Change Replaced the pattern color approach with a scalable layer that stretches the image to fill view bounds. This matches Android's scaling behavior and follows MAUI's standard layer management pattern used for gradient backgrounds. Validated the behavior in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Issues Fixed Fixes #21594 ### Test case Test case Issue17789 is already included in the source. ### Output ScreenShot | Before | After | |---------|--------| | <img width="354" height="735" alt="BeforeFix" src="https://github.com/user-attachments/assets/8520b49b-617c-46e5-a29a-3a9b26e5b0c0" /> | <img width="354" height="735" alt="AfterFix" src="https://github.com/user-attachments/assets/402a8975-4d2b-4e2f-a362-ae22bcedec56" /> |
<!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Details ContentPage background images display incorrectly on iOS by tiling in a repeating pattern instead of showing a single scaled image. ### Root Cause The iOS implementation used a pattern color API that creates tiled patterns without scaling options. This tiles the image at original size rather than scaling it to fill the view. ### Description of Change Replaced the pattern color approach with a scalable layer that stretches the image to fill view bounds. This matches Android's scaling behavior and follows MAUI's standard layer management pattern used for gradient backgrounds. Validated the behavior in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Issues Fixed Fixes #21594 ### Test case Test case Issue17789 is already included in the source. ### Output ScreenShot | Before | After | |---------|--------| | <img width="354" height="735" alt="BeforeFix" src="https://github.com/user-attachments/assets/8520b49b-617c-46e5-a29a-3a9b26e5b0c0" /> | <img width="354" height="735" alt="AfterFix" src="https://github.com/user-attachments/assets/402a8975-4d2b-4e2f-a362-ae22bcedec56" /> |
<!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Details ContentPage background images display incorrectly on iOS by tiling in a repeating pattern instead of showing a single scaled image. ### Root Cause The iOS implementation used a pattern color API that creates tiled patterns without scaling options. This tiles the image at original size rather than scaling it to fill the view. ### Description of Change Replaced the pattern color approach with a scalable layer that stretches the image to fill view bounds. This matches Android's scaling behavior and follows MAUI's standard layer management pattern used for gradient backgrounds. Validated the behavior in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Issues Fixed Fixes #21594 ### Test case Test case Issue17789 is already included in the source. ### Output ScreenShot | Before | After | |---------|--------| | <img width="354" height="735" alt="BeforeFix" src="https://github.com/user-attachments/assets/8520b49b-617c-46e5-a29a-3a9b26e5b0c0" /> | <img width="354" height="735" alt="AfterFix" src="https://github.com/user-attachments/assets/402a8975-4d2b-4e2f-a362-ae22bcedec56" /> |
## What's Coming .NET MAUI inflight/candidate introduces significant improvements across all platforms with focus on quality, performance, and developer experience. This release includes 27 commits with various improvements, bug fixes, and enhancements. ## CollectionView - [iOS][CV2] Fix page can be dragged down, and it would cause an extra space between Header and EmptyView text by @devanathan-vaithiyanathan in #31840 <details> <summary>🔧 Fixes</summary> - [I8_Header_and_Footer_Null - The page can be dragged down, and it would cause an extra space between Header and EmptyView text.](#31465) </details> - [iOS] Fixed the Items not displayed properly in CarouselView2 by @Ahamed-Ali in #31336 <details> <summary>🔧 Fixes</summary> - [[iOS] Items are not updated properly in CarouselView2.](#31148) </details> ## Docs - Improve Controls Core API docs by @jfversluis in #33240 ## Editor - [iOS] Fixed an issue where an Editor with a small height inside a ScrollView would cause the entire page to scroll by @Tamilarasan-Paranthaman in #27948 <details> <summary>🔧 Fixes</summary> - [[iOS][Editor] An Editor that has not enough height and resides inside a ScrollView/CollectionView will scroll the entire page](#27750) </details> ## Image - [Android] Image control crashes on Android when image width exceeds height by @KarthikRajaKalaimani in #33045 <details> <summary>🔧 Fixes</summary> - [Image control crashes on Android when image width exceeds height](#32869) </details> ## Mediapicker - [Android 🤖] Add a log telling why the request is cancelled by @pictos in #33295 <details> <summary>🔧 Fixes</summary> - [MediaPicker.PickPhotosAsync throwing TaskCancelledException in net10-android](#33283) </details> ## Navigation - [Android] Fix for App Hang When PopModalAsync Is Called Immediately After PushModalAsync with Task.Yield() by @BagavathiPerumal in #32479 <details> <summary>🔧 Fixes</summary> - [App hangs if PopModalAsync is called after PushModalAsync with single await Task.Yield()](#32310) </details> - [iOS 26] Navigation hangs after rapidly open and closing new page using Navigation.PushAsync - fix by @kubaflo in #32456 <details> <summary>🔧 Fixes</summary> - [[iOS 26] Navigation hangs after rapidly open and closing new page using Navigation.PushAsync](#32425) </details> ## Pages - [iOS] Fix ContentPage BackgroundImageSource not working by @Shalini-Ashokan in #33297 <details> <summary>🔧 Fixes</summary> - [.Net MAUI- Page.BackgroundImageSource not working for iOS](#21594) </details> ## RadioButton - [Issue-Resolver] Fix #33264 - RadioButtonGroup not working with Collection View by @kubaflo in #33343 <details> <summary>🔧 Fixes</summary> - [RadioButtonGroup not working with CollectionView](#33264) </details> ## SafeArea - [Android] Fixed Label Overlapped by Android Status Bar When Using SafeAreaEdges="Container" in .NET MAUI by @NirmalKumarYuvaraj in #33285 <details> <summary>🔧 Fixes</summary> - [SafeAreaEdges works correctly only on the first tab in Shell. Other tabs have content colliding with the display cutout in the landscape mode.](#33034) - [Label Overlapped by Android Status Bar When Using SafeAreaEdges="Container" in .NET MAUI](#32941) - [[MAUI 10] Layout breaks on first navigation (Shell // route) until soft keyboard appears/disappears (Android + iOS)](#33038) </details> ## ScrollView - [Windows, Android] Fix ScrollView Content Not Removed When Set to Null by @devanathan-vaithiyanathan in #33069 <details> <summary>🔧 Fixes</summary> - [[Windows, Android] ScrollView Content Not Removed When Set to Null](#33067) </details> ## Searchbar - Fix Android crash when changing shared Drawable tint on Searchbar by @tritter in #33071 <details> <summary>🔧 Fixes</summary> - [[Android] Crash on changing Tint of Searchbar](#33070) </details> ## Shell - [iOS] - Fix Custom FlyoutIcon from Being Overridden to Default Color in Shell by @prakashKannanSf3972 in #27580 <details> <summary>🔧 Fixes</summary> - [Change the flyout icon color](#6738) </details> - [iOS] Fix Shell NavBarIsVisible updates when switching ShellContent by @Vignesh-SF3580 in #33195 <details> <summary>🔧 Fixes</summary> - [[iOS] Shell NavBarIsVisible is not updated when changing ShellContent](#33191) </details> ## Slider - [C] Fix Slider and Stepper property order independence by @StephaneDelcroix in #32939 <details> <summary>🔧 Fixes</summary> - [Slider Binding Initialization Order Causes Incorrect Value Assignment in XAML](#32903) - [Slider is very broken, Value is a mess when setting Minimum](#14472) - [Slider is buggy depending on order of properties](#18910) - [Stepper Value is incorrectly clamped to default min/max when using bindableproperties in MVVM pattern](#12243) - [[Issue-Resolver] Fix #32903 - Sliderbinding initialization order issue](#32907) </details> ## Stepper - [Windows] Maui Stepper: Clamp minimum and maximum value by @OomJan in #33275 <details> <summary>🔧 Fixes</summary> - [[Windows] Maui Stepper is not clamped to minimum or maximum internally](#33274) </details> - [iOS] Fixed the UIStepper Value from being clamped based on old higher MinimumValue - Candidate PR test failure fix- 33363 by @Ahamed-Ali in #33392 ## TabbedPage - [windows] Fixed Rapid change of selected tab results in crash. by @praveenkumarkarunanithi in #33113 <details> <summary>🔧 Fixes</summary> - [Rapid change of selected tab results in crash on Windows.](#32824) </details> ## Titlebar - [Mac] Fix TitleBar Content Overlapping with Traffic Light Buttons on Latest macOS Version by @devanathan-vaithiyanathan in #33157 <details> <summary>🔧 Fixes</summary> - [TitleBar Content Overlapping with Traffic Light Buttons on Latest macOS Version](#33136) </details> ## Xaml - Fix for Control does not update from binding anymore after MultiBinding.ConvertBack is called by @BagavathiPerumal in #33128 <details> <summary>🔧 Fixes</summary> - [Control does not update from binding anymore after MultiBinding.ConvertBack is called](#24969) - [The issue with the MultiBinding converter with two way binding mode does not work properly when changing the values.](#20382) </details> <details> <summary>🔧 Infrastructure (1)</summary> - Avoid KVO on CALayer by introducing an Apple PlatformInterop by @albyrock87 in #30861 </details> <details> <summary>🧪 Testing (2)</summary> - [Testing] Enable UITest Issue18193 on MacCatalyst by @NafeelaNazhir in #31653 <details> <summary>🔧 Fixes</summary> - [Test Issue18193 was disabled on Mac Catalyst](#27206) </details> - Set the CV2 handlers as the default by @Ahamed-Ali in #33177 </details> <details> <summary>📦 Other (3)</summary> - Update WindowsAppSDK to 1.8 by @mattleibow in #32174 <details> <summary>🔧 Fixes</summary> - [Update to WindowsAppSDK](#30858) </details> - Fix command dependency reentrancy by @simonrozsival in #33129 - Fix SafeArea AdjustPan handling and add AdjustNothing mode tests by @PureWeen via @Copilot in #33354 </details> **Full Changelog**: main...inflight/candidate
<!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Details ContentPage background images display incorrectly on iOS by tiling in a repeating pattern instead of showing a single scaled image. ### Root Cause The iOS implementation used a pattern color API that creates tiled patterns without scaling options. This tiles the image at original size rather than scaling it to fill the view. ### Description of Change Replaced the pattern color approach with a scalable layer that stretches the image to fill view bounds. This matches Android's scaling behavior and follows MAUI's standard layer management pattern used for gradient backgrounds. Validated the behavior in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Issues Fixed Fixes dotnet#21594 ### Test case Test case Issue17789 is already included in the source. ### Output ScreenShot | Before | After | |---------|--------| | <img width="354" height="735" alt="BeforeFix" src="https://github.com/user-attachments/assets/8520b49b-617c-46e5-a29a-3a9b26e5b0c0" /> | <img width="354" height="735" alt="AfterFix" src="https://github.com/user-attachments/assets/402a8975-4d2b-4e2f-a362-ae22bcedec56" /> |
Issue Details
ContentPage background images display incorrectly on iOS by tiling in a repeating pattern instead of showing a single scaled image.
Root Cause
The iOS implementation used a pattern color API that creates tiled patterns without scaling options. This tiles the image at original size rather than scaling it to fill the view.
Description of Change
Replaced the pattern color approach with a scalable layer that stretches the image to fill view bounds. This matches Android's scaling behavior and follows MAUI's standard layer management pattern used for gradient backgrounds.
Validated the behavior in the following platforms
Issues Fixed
Fixes #21594
Test case
Test case Issue17789 is already included in the source.
Output ScreenShot