Skip to content

[iOS/Mac] Fix Image not enlarging to explicit HeightRequest/WidthRequest - #37267

Merged
kubaflo merged 2 commits into
dotnet:inflight/currentfrom
SyedAbdulAzeemSF4852:fix-12879
Aug 26, 2026
Merged

kubaflo merged 2 commits into
dotnet:inflight/currentfrom
SyedAbdulAzeemSF4852:fix-12879

Conversation

@SyedAbdulAzeemSF4852

@SyedAbdulAzeemSF4852 SyedAbdulAzeemSF4852 commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

Note

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

Issue Details

  • On iOS, an Image does not scale up on the unconstrained axis when only HeightRequest or WidthRequest is set—the unconstrained axis remains capped at its native size. This is most visible with HorizontalStackLayout + HeightRequest and VerticalStackLayout + WidthRequest. The reverse combinations can appear to work because the other axis is large enough to mask the same issue. On Android, the Image scales correctly with HeightRequest and WidthRequest in all cases.

Root Cause

  • In SizeThatFitsImage (iOS), the scale factor for ScaleAspectFit images was calculated as Math.Min(imageWidth, widthConstraint) / imageWidth. This could never exceed 1 (since it always divides the image's own size by itself once the constraint is larger), so an image could shrink to fit a smaller space but could never grow beyond its native size, even when an explicit HeightRequest or WidthRequest requested a larger size, regardless of StackLayout orientation.

Description of Change

  • Updated SizeThatFitsImage in ImageViewExtensions.cs to accept flags indicating whether width and height constraints are explicit, and to allow images to scale up to requested sizes when either is set, instead of capping at native size.
  • Modified GetDesiredSizeFromHandler in ViewHandlerExtensions.iOS.cs to pass explicitness flags to SizeThatFitsImage, ensuring correct sizing behavior for images with explicit size requests.

Issues Fixed

Fixes #12879

Validated the behaviour in the following platforms

  • Windows
  • Android
  • iOS
  • Mac

Output

Before After

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

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

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

Or

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

@azure-pipelines

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

@dotnet-policy-service dotnet-policy-service Bot added the community ✨ Community Contribution label Aug 10, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Hey there @@SyedAbdulAzeemSF4852! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

@kubaflo

This comment has been minimized.

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

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

AI Review Summary

@SyedAbdulAzeemSF4852 — new AI review results are available based on commit 5e704cd.

Gate Passed Confidence Low Platform iOS


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

Gate Result: ✅ PASSED

Platform: IOS · Base: main · Merge base: 1749485a

Test Without Fix (expect FAIL) With Fix (expect PASS)
🖥️ Issue12879 Issue12879 ✅ FAIL — 435s ✅ PASS — 132s
🔴 Without fix — 🖥️ Issue12879: FAIL ✅ · 435s

Error-relevant lines (filtered from the build log):

     at Microsoft.Maui.TestCases.Tests.Issues.Issue12879.ImageScalesUpToExplicitHeightRequestInHorizontalStackLayout() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue12879.cs:line 23
🟢 With fix — 🖥️ Issue12879: PASS ✅ · 132s

(no coded error found; showing last 1200 chars)

d the specified pattern.
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.08]   Discovering: Controls.TestCases.iOS.Tests
[xUnit.net 00:00:00.22]   Discovered:  Controls.TestCases.iOS.Tests
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
   NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 8/10/2026 3:16:29 PM FixtureSetup for Issue12879(iOS)
>>>>> 8/10/2026 3:16:38 PM ImageScalesUpToExplicitHeightRequestInHorizontalStackLayout Start
>>>>> 8/10/2026 3:16:39 PM ImageScalesUpToExplicitHeightRequestInHorizontalStackLayout Stop
  Passed ImageScalesUpToExplicitHeightRequestInHorizontalStackLayout [956 ms]
NUnit Adapter 4.5.0.0: Test execution complete
Results File: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue12879.trx
Test Run Successful.
Total tests: 1
     Passed: 1
 Total time: 33.2036 Seconds
>>> TRX_RESULT_FILE: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue12879.trx
📁 Fix files reverted (2 files)
  • src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs
  • src/Core/src/Platform/iOS/ImageViewExtensions.cs

📋 Pre-Flight — Context & Validation

PR #37267 Pre-Flight

Context

  • PR: #37267 — [WIP][iOS/Mac] Fix Image not enlarging to explicit HeightRequest/WidthRequest
  • State: Open draft; base main, head fix-12879
  • Issue: #12879 — on iOS, an Image inside a horizontal StackLayout remains at its native size instead of enlarging to an explicit HeightRequest; Android provides the expected behavior.
  • Platform for this phase: ios
  • Gate: Already passed upstream: the regression test fails without the PR fix and passes with it. Do not rerun gate verification and do not create or overwrite gate/content.md.

Direct Diff Assessment

The PR adds the Issue12879 HostApp page and NUnit UI test, then changes:

  • src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs
  • src/Core/src/Platform/iOS/ImageViewExtensions.cs

The base implementation computes ScaleAspectFit ratios from dimensions already capped with Math.Min(imageDimension, constraint). Those ratios cannot exceed 1, so the desired size can shrink but cannot enlarge beyond the native image size.

The PR's fix:

  1. Determines whether each handler constraint came from an explicit virtual-view width or height.
  2. Adds two explicitness flags to SizeThatFitsImage.
  3. Computes aspect ratios from raw constraints.
  4. Caps the scale factor at 1 only when neither axis is explicit.

This changes the internal helper's contract and only supplies the new flags from the direct UIImageView handler path. Other callers include wrapper views, image buttons, and toolbar image measurement.

Alternative-Fix Requirements

Each try-fix invocation must propose one root-cause-distinct alternative to the PR implementation, not merely rename or relocate the same explicit-flag logic. Preserve aspect ratio, finite available-space constraints, padding behavior, no-image behavior, non-AspectFit behavior, and existing image/image-button measurement semantics.

Primary target files:

  • src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs
  • src/Core/src/Platform/iOS/ImageViewExtensions.cs

Related call sites may be inspected, but changes must remain focused:

  • src/Core/src/Platform/iOS/WrapperView.cs
  • src/Controls/src/Core/ImageButton/ImageButton.iOS.cs
  • src/Controls/src/Core/Compatibility/iOS/Extensions/ToolbarItemExtensions.cs

Bounded Validation

Run exactly the detected primary UI test:

pwsh .github/scripts/BuildAndRunHostApp.ps1 -Platform ios -TestFilter "FullyQualifiedName~Issue12879"

No additional named mandatory regression tests were supplied in the request. Do not run a category, project-wide test set, build-only proxy, gate verification, or full suite. One implementation/test pass is allowed, followed by at most one focused correction and retest.

Execution Constraints

  • Candidate 1 uses claude-opus-5; candidate 2 uses gpt-5.6-sol.
  • Candidate 2 must consume candidate 1's recorded result and use a different approach without reopening or rerunning candidate 1.
  • Perform the try-fix skill's expert self-review inline; do not launch a reviewer agent.
  • Persist per-candidate narrative and the aggregate immediately after each candidate.
  • Existing unrelated worktree changes predate this phase and must be preserved.

🔬 Code Review — Deep Analysis

Expert PR Evaluation — PR #37267

Verdict

NEEDS_CHANGES — the root-cause diagnosis is correct and the submitted ScaleAspectFit change is directionally sound, but two concrete correctness gaps should be addressed before merge.

Confidence: low. The implementation is platform-specific image measurement code with a broad measure-path blast radius; the Gate passed for the reported scenario, but adjacent constraint and wrapper scenarios are not covered, and GitHub authentication was unavailable for prior-review and required-check reconciliation.

Independent Assessment

The existing ScaleAspectFit calculation derives its ratios from dimensions already capped at the image's native size, so its scale factor cannot exceed 1. The PR correctly changes that calculation so an explicit width or height request can enlarge the desired image size while preserving the previous cap when neither axis is explicit. For positive dimensions, the no-explicit-request path remains mathematically equivalent to the prior behavior.

Actionable Findings

  1. Major — explicit constraints can bypass MaximumWidth/MaximumHeight. ViewHandlerExtensions.iOS.cs first clamps the incoming constraints to the virtual view's maximums, then replaces them with raw explicit width and height values. Once SizeThatFitsImage is allowed to scale above 1, a request such as WidthRequest=400, MaximumWidth=100 can derive a 400-point free-axis measurement even though the explicit axis is later clamped back to 100. The scale calculation must use the explicit request clamped to its corresponding maximum.
  2. Major — wrapped images still use the old capped measurement behavior. Images routed through WrapperView because of shadow, clip, border, or related decoration call SizeThatFitsImage without the explicitness information, so the reported issue remains for those ordinary Image variants. The wrapper measurement path should propagate whether either request is explicit.
  3. Moderate — non-AspectFit enlargement remains outside the fix. AspectFill and Fill still use dimensions capped at native image size. This should be treated as a scoped limitation unless platform parity establishes that those modes must also enlarge.
  4. Moderate — the regression test covers only the positive height-request case. It does not protect the critical invariant that an image without an explicit request must remain at native size in a larger available area, nor the maximum-constraint interaction above.
  5. Minor — two adjacent boolean parameters encode one policy. A single allowUpscale parameter better expresses the helper contract and cannot be accidentally transposed.

Blast Radius and Failure-Mode Probing

  • The direct handler path runs for every iOS/MacCatalyst UIImageView, but the behavior change is gated by an explicit request.
  • There is no startup, static-state, subscription, or handler-lifecycle impact.
  • No-explicit-request measurements retain the prior cap and arithmetic behavior.
  • An explicit request larger than a maximum currently produces an inconsistent aspect-derived free axis; this is a verified regression mechanism.
  • Wrapped Image instances do not receive the new policy and continue to reproduce the original failure.
  • ImageButton and toolbar call sites should not be changed without confirming their distinct sizing semantics; this PR's reported control is Image.

Recommended Consolidated Refinement

Keep the PR's general approach, express the geometry policy as one allowUpscale flag, clamp explicit requests to MaximumWidth/MaximumHeight before measurement, and propagate that policy through the Image wrapper path. Add focused HostApp/UI assertions for a wrapped image, the no-request invariant, and the maximum-constraint case if the existing page can cover them without expanding the required validation command.

Review and CI Reconciliation

The three required prior-review surfaces and gh pr checks --required could not be queried because gh was unauthenticated. No prior finding or required-check status is therefore claimed; the trusted Gate result supplied to this phase is the only CI evidence used.


🛠️ Try-Fix — Analysis & Comparison

PR #37267 Try-Fix Aggregate

Candidate Status

Candidate Model Approach Result Test Runs
1 claude-opus-5 Aspect-ratio coupling during explicit-size resolution Pass 1
2 gpt-5.6-sol Unbounded-axis projection in SizeThatFitsImage Pass 1

Candidate 1 — Aspect-Ratio Coupling During Explicit-Size Resolution

Result: Pass
Artifacts: CustomAgentLogsTmp/PRState/37267/PRAgent/try-fix/attempt-1/
Full candidate narrative: CustomAgentLogsTmp/PRState/37267/PRAgent/try-fix-1/content.md

Approach

Treat SizeThatFitsImage as an unchanged fit-within-available-space helper. The alternative root-cause hypothesis is that resolving explicit width and height independently in GetDesiredSizeFromHandler destroys the axis coupling required by ScaleAspectFit. After normal constraint resolution, when exactly one axis is explicit, derive the free axis from the image's native aspect ratio, clamp it to available space, and run it through ResolveConstraints.

Unlike the PR, this does not add explicitness flags to SizeThatFitsImage, does not change its scale cap or shared contract, and does not affect its wrapper, image-button, or toolbar callers. It changes only src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs.

Test Results

Command:

pwsh .github/scripts/BuildAndRunHostApp.ps1 -Platform ios -TestFilter "FullyQualifiedName~Issue12879"

Run 1 passed: one test passed and the script reported All tests passed. No correction or retest was needed.

Inline Self-Review

One minor finding, with no critical or major findings: applying aspect coupling only to direct UIImageView measurement leaves the pre-existing Image/ImageButton behavior asymmetry. The diff did not change after review.

Failure / Blocker Analysis

None. The candidate passed on its first run.

Complete Diff

diff --git a/src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs b/src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs
index 64f03f3c8b..3111680170 100644
--- a/src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs
+++ b/src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs
@@ -78,6 +78,11 @@ namespace Microsoft.Maui
 			widthConstraint = Math.Min(widthConstraint, virtualView.MaximumWidth);
 			heightConstraint = Math.Min(heightConstraint, virtualView.MaximumHeight);
 
+			// The constraints below get replaced by explicit requests for some platform views; keep the
+			// real available space so aspect-ratio driven sizes can still be clamped to what's on offer.
+			var availableWidth = widthConstraint;
+			var availableHeight = heightConstraint;
+
 			CGSize sizeThatFits;
 
 			// Calling SizeThatFits on an ImageView always returns the image's dimensions, so we need to call the extension method
@@ -125,6 +130,16 @@ namespace Microsoft.Maui
 			var finalWidth = ResolveConstraints(size.Width, virtualView.Width, virtualView.MinimumWidth, virtualView.MaximumWidth);
 			var finalHeight = ResolveConstraints(size.Height, virtualView.Height, virtualView.MinimumHeight, virtualView.MaximumHeight);
 
+			// The width and height of an aspect-fit image are not independent; they're coupled by the image's
+			// aspect ratio. Resolving each axis separately loses that coupling, so an image with only an explicit
+			// HeightRequest (or only an explicit WidthRequest) ends up with the requested size on one axis and its
+			// native measurement on the other. Derive the free axis from the explicitly requested one instead.
+			if (platformView is UIImageView aspectFitImageView)
+			{
+				(finalWidth, finalHeight) = ResolveImageAspectRatio(
+					aspectFitImageView, virtualView, finalWidth, finalHeight, availableWidth, availableHeight);
+			}
+
 			return new Size(finalWidth, finalHeight);
 		}
 
@@ -160,6 +175,47 @@ namespace Microsoft.Maui
 			viewHandler.Invoke(nameof(IView.Frame), rect);
 		}
 
+		// For a ScaleAspectFit image, an explicit request on one axis implies a size on the other axis via the
+		// image's aspect ratio. Only the axis that was NOT explicitly requested is recalculated; the explicit
+		// axis, Minimum/Maximum constraints, and the available space are all still honored.
+		static (double Width, double Height) ResolveImageAspectRatio(
+			UIImageView imageView,
+			IView virtualView,
+			double finalWidth,
+			double finalHeight,
+			double availableWidth,
+			double availableHeight)
+		{
+			if (imageView.ContentMode != UIViewContentMode.ScaleAspectFit || imageView.Image is not UIImage image)
+			{
+				return (finalWidth, finalHeight);
+			}
+
+			double imageWidth = image.Size.Width;
+			double imageHeight = image.Size.Height;
+
+			if (imageWidth <= 0 || imageHeight <= 0)
+			{
+				return (finalWidth, finalHeight);
+			}
+
+			var widthIsExplicit = IsExplicitSet(virtualView.Width);
+			var heightIsExplicit = IsExplicitSet(virtualView.Height);
+
+			if (heightIsExplicit && !widthIsExplicit && !double.IsInfinity(finalHeight))
+			{
+				var aspectWidth = Math.Min(finalHeight * (imageWidth / imageHeight), availableWidth);
+				finalWidth = ResolveConstraints(aspectWidth, virtualView.Width, virtualView.MinimumWidth, virtualView.MaximumWidth);
+			}
+			else if (widthIsExplicit && !heightIsExplicit && !double.IsInfinity(finalWidth))
+			{
+				var aspectHeight = Math.Min(finalWidth * (imageHeight / imageWidth), availableHeight);
+				finalHeight = ResolveConstraints(aspectHeight, virtualView.Height, virtualView.MinimumHeight, virtualView.MaximumHeight);
+			}
+
+			return (finalWidth, finalHeight);
+		}
+
 		internal static double ResolveConstraints(double measured, double exact, double min, double max)
 		{
 			var resolved = measured;

Candidate 2 — Unbounded-Axis Projection

Result: Pass
Artifacts: CustomAgentLogsTmp/PRState/37267/PRAgent/try-fix/attempt-2/
Full candidate narrative: CustomAgentLogsTmp/PRState/37267/PRAgent/try-fix-2/content.md

Approach

Interpret a ScaleAspectFit measurement with exactly one unbounded axis as a stack-layout topology: use the finite cross-axis as the image extent and project the unbounded main-axis size from the native aspect ratio, but only when enlargement is needed. Keep the existing capped measurement path for all other constraint shapes.

Unlike the PR, this adds no explicit-axis flags, changes no helper signature or callers, and does not conditionally remove the scale-at-1 cap. Unlike Candidate 1, it does no post-resolution processing or explicit-request detection in GetDesiredSizeFromHandler; it changes the one-unbounded-axis interpretation inside SizeThatFitsImage.

Test Results

Command:

pwsh .github/scripts/BuildAndRunHostApp.ps1 -Platform ios -TestFilter "FullyQualifiedName~Issue12879"

Run 1 passed: one test passed, with Test Run Successful and All tests passed. No correction or retest was needed.

Inline Self-Review

One moderate finding, with no critical or major findings: the constraint topology cannot distinguish an explicit cross-axis request from ordinary finite parent availability. A small AspectFit image with no explicit request could therefore enlarge to the finite cross-axis in an unbounded-axis stack. The exact positive regression test does not cover that negative case. The diff did not change after review.

Failure / Blocker Analysis

No execution failure or blocker occurred. The concrete residual regression risk above remains and makes this candidate less behavior-safe despite its passing targeted test.

Complete Diff

diff --git a/src/Core/src/Platform/iOS/ImageViewExtensions.cs b/src/Core/src/Platform/iOS/ImageViewExtensions.cs
index e922dd148d..bc61242425 100644
--- a/src/Core/src/Platform/iOS/ImageViewExtensions.cs
+++ b/src/Core/src/Platform/iOS/ImageViewExtensions.cs
@@ -98,6 +98,28 @@ namespace Microsoft.Maui.Platform
 			// that can fit it
 			if (imageView.ContentMode == UIViewContentMode.ScaleAspectFit)
 			{
+				var widthIsUnbounded = double.IsPositiveInfinity(widthConstraint);
+				var heightIsUnbounded = double.IsPositiveInfinity(heightConstraint);
+
+				// A stack measures children with an unbounded main axis. In that case, the finite cross-axis
+				// is the image extent and the main-axis size follows from the image's proportions.
+				if (widthIsUnbounded != heightIsUnbounded && imageWidth > 0 && imageHeight > 0)
+				{
+					if (widthIsUnbounded && heightConstraint > imageHeight)
+					{
+						return new CGSize(
+							imageWidth * heightConstraint / imageHeight + horizontalThickness,
+							heightConstraint + verticalThickness);
+					}
+
+					if (heightIsUnbounded && widthConstraint > imageWidth)
+					{
+						return new CGSize(
+							widthConstraint + horizontalThickness,
+							imageHeight * widthConstraint / imageWidth + verticalThickness);
+					}
+				}
+
 				var widthRatio = constrainedWidth / imageWidth;
 				var heightRatio = constrainedHeight / imageHeight;
 				var scaleFactor = Math.Min(widthRatio, heightRatio);

📝 PR Finalize — Recommended Title & Description

Assessment: ✏️ Recommend updating — remove the [WIP] prefix, name MacCatalyst precisely, and include the submitted regression test while preserving the existing technical detail and evidence.

Recommended title

[iOS/MacCatalyst] Image: Honor explicit size requests when scaling up

Recommended description

### Issue Details

On iOS, an `Image` inside a `HorizontalStackLayout` does not respect an explicit `HeightRequest`: it stays rendered at its native size instead of scaling up to the requested height. Android scales the image correctly for the same request.

### Root Cause

In `SizeThatFitsImage` on iOS, the `ScaleAspectFit` ratios were calculated from width and height values already capped with `Math.Min(imageDimension, constraint)`. Those ratios therefore could not exceed `1`, so an image could shrink to fit a smaller constraint but could not grow to honor a larger explicit `HeightRequest` or `WidthRequest`.

### Description of Change

- Updated `SizeThatFitsImage` in `ImageViewExtensions.cs` to accept flags indicating whether width and height constraints are explicit and to allow `ScaleAspectFit` images to scale above their native size when either request is set.
- Updated `GetDesiredSizeFromHandler` in `ViewHandlerExtensions.iOS.cs` to detect explicit width and height requests and pass that information to `SizeThatFitsImage`.
- Added an `Issue12879` HostApp page and iOS UI regression test covering an `Image` with an explicit `HeightRequest` inside a `HorizontalStackLayout`.

### Issues Fixed

Fixes #12879

### Validated the behaviour in the following platforms

- [ ] Windows
- [ ] Android
- [x] iOS
- [x] MacCatalyst

### Output

| Before | After |
|----------|----------|
| <img src="https://github.com/user-attachments/assets/42293280-031a-4f0c-b7da-66a5c1b1b958"> | <img src="https://github.com/user-attachments/assets/36ecd733-a348-4a71-8430-089a3a5a92e8"> |

🏁 Report — Final Recommendation

⚠️ Final Recommendation: REQUEST CHANGES

Winner: try-fix-1

try-fix-1 is the strongest validated candidate. It passed the required Issue12879 iOS UI test on its only run, keeps SizeThatFitsImage and its other callers unchanged, and restores the missing aspect-ratio coupling only after constraints have been resolved. Because it captures available space after maximum constraints are applied, it also avoids the submitted PR's explicit-request/maximum-constraint regression.

Comparative Ranking

Rank Candidate Required validation Assessment
1 try-fix-1 Pass (1 run) Most localized behavior change; preserves the shared helper contract, honors available/max space, and fixes the reported one-explicit-axis case. Wrapped images remain an acknowledged coverage gap.
2 pr Pass (trusted Gate) Correctly fixes the reported direct Image scenario, but expert review found two concrete defects: raw explicit requests can bypass maximum constraints when deriving the free axis, and wrapped images still retain the original native-size cap.
3 try-fix-2 Pass (1 run) Narrow implementation, but it infers intent from one-unbounded-axis topology. That topology also occurs without an explicit request, so ordinary images can be enlarged unexpectedly; its positive test does not protect the native-size invariant.
4 pr-plus-reviewer Fail (1 run) The build succeeded and both direct and wrapped explicit-height checks passed, but the added intrinsic and maximum-width assertions failed at 100x200. The shared-row test layout confounds measure behavior with arrange-time stretching; no repair or retest was permitted. A failed candidate must rank below all passing candidates.

Expert Review

The submitted diagnosis is sound: ratios computed from dimensions already capped at native image size cannot exceed 1. The expert pass nevertheless produced six inline findings, led by the maximum-constraint regression and incomplete wrapper propagation. It also identified missing negative coverage, uncertainty for AspectFill/Fill, a two-boolean helper contract that can be transposed, and a brittle square-asset assertion.

The raw PR therefore should not be approved as submitted. Applying try-fix-1 requires replacing the submitted implementation with the winning diff and retaining or adapting regression coverage for the direct explicit-height scenario. Wrapped Image behavior should remain an explicit follow-up or receive separately validated coverage.

Evidence Limits

The trusted Gate passed. GitHub prior-review surfaces and required-check status could not be queried because gh was unauthenticated, so no additional CI or prior-review claim is made.


📱 UI Tests — Image,ViewBaseTests

Detected UI test categories: Image,ViewBaseTests

✅ Deep UI tests — 158 passed, 0 failed, 2 skipped across 2 categories on platform-pool agent (replaces in-process counts above).

🧪 UI Test Execution Results (deep, platform pool)

Category Tests Snapshot diffs
Image 46/48 (2 skipped) ✓ —
ViewBaseTests 112/112 ✓ —
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)

🧭 Next Steps — alternative fix proposed (try-fix-1)

Automated review — alternative fix proposed

The expert-reviewer evaluation compared the PR fix against automatically generated candidates and selected try-fix-1 as the strongest fix.

Why: try-fix-1 won because it passed the required iOS regression test while keeping the shared image measurement helper unchanged and preserving maximum/available-space constraints. The submitted PR has concrete maximum-constraint and wrapper gaps, try-fix-2 risks upscaling images without explicit requests, and pr-plus-reviewer failed its sole validation run.

Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.

Candidate diff (try-fix-1)
diff --git a/src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs b/src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs
index 64f03f3c8b..3111680170 100644
--- a/src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs
+++ b/src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs
@@ -78,6 +78,11 @@ namespace Microsoft.Maui
 			widthConstraint = Math.Min(widthConstraint, virtualView.MaximumWidth);
 			heightConstraint = Math.Min(heightConstraint, virtualView.MaximumHeight);
 
+			// The constraints below get replaced by explicit requests for some platform views; keep the
+			// real available space so aspect-ratio driven sizes can still be clamped to what's on offer.
+			var availableWidth = widthConstraint;
+			var availableHeight = heightConstraint;
+
 			CGSize sizeThatFits;
 
 			// Calling SizeThatFits on an ImageView always returns the image's dimensions, so we need to call the extension method
@@ -125,6 +130,16 @@ namespace Microsoft.Maui
 			var finalWidth = ResolveConstraints(size.Width, virtualView.Width, virtualView.MinimumWidth, virtualView.MaximumWidth);
 			var finalHeight = ResolveConstraints(size.Height, virtualView.Height, virtualView.MinimumHeight, virtualView.MaximumHeight);
 
+			// The width and height of an aspect-fit image are not independent; they're coupled by the image's
+			// aspect ratio. Resolving each axis separately loses that coupling, so an image with only an explicit
+			// HeightRequest (or only an explicit WidthRequest) ends up with the requested size on one axis and its
+			// native measurement on the other. Derive the free axis from the explicitly requested one instead.
+			if (platformView is UIImageView aspectFitImageView)
+			{
+				(finalWidth, finalHeight) = ResolveImageAspectRatio(
+					aspectFitImageView, virtualView, finalWidth, finalHeight, availableWidth, availableHeight);
+			}
+
 			return new Size(finalWidth, finalHeight);
 		}
 
@@ -160,6 +175,47 @@ namespace Microsoft.Maui
 			viewHandler.Invoke(nameof(IView.Frame), rect);
 		}
 
+		// For a ScaleAspectFit image, an explicit request on one axis implies a size on the other axis via the
+		// image's aspect ratio. Only the axis that was NOT explicitly requested is recalculated; the explicit
+		// axis, Minimum/Maximum constraints, and the available space are all still honored.
+		static (double Width, double Height) ResolveImageAspectRatio(
+			UIImageView imageView,
+			IView virtualView,
+			double finalWidth,
+			double finalHeight,
+			double availableWidth,
+			double availableHeight)
+		{
+			if (imageView.ContentMode != UIViewContentMode.ScaleAspectFit || imageView.Image is not UIImage image)
+			{
+				return (finalWidth, finalHeight);
+			}
+
+			double imageWidth = image.Size.Width;
+			double imageHeight = image.Size.Height;
+
+			if (imageWidth <= 0 || imageHeight <= 0)
+			{
+				return (finalWidth, finalHeight);
+			}
+
+			var widthIsExplicit = IsExplicitSet(virtualView.Width);
+			var heightIsExplicit = IsExplicitSet(virtualView.Height);
+
+			if (heightIsExplicit && !widthIsExplicit && !double.IsInfinity(finalHeight))
+			{
+				var aspectWidth = Math.Min(finalHeight * (imageWidth / imageHeight), availableWidth);
+				finalWidth = ResolveConstraints(aspectWidth, virtualView.Width, virtualView.MinimumWidth, virtualView.MaximumWidth);
+			}
+			else if (widthIsExplicit && !heightIsExplicit && !double.IsInfinity(finalWidth))
+			{
+				var aspectHeight = Math.Min(finalWidth * (imageHeight / imageWidth), availableHeight);
+				finalHeight = ResolveConstraints(aspectHeight, virtualView.Height, virtualView.MinimumHeight, virtualView.MaximumHeight);
+			}
+
+			return (finalWidth, finalHeight);
+		}
+
 		internal static double ResolveConstraints(double measured, double exact, double min, double max)
 		{
 			var resolved = measured;

@MauiBot MauiBot added s/agent-gate-passed AI verified tests catch the bug (fail without fix, pass with fix) 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) and removed s/agent-review-in-progress AI review is currently running for this PR labels Aug 10, 2026

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this one ready?

@SyedAbdulAzeemSF4852 SyedAbdulAzeemSF4852 changed the title [WIP][iOS/Mac] Fix Image not enlarging to explicit HeightRequest/WidthRequest [iOS/Mac] Fix Image not enlarging to explicit HeightRequest/WidthRequest Aug 26, 2026
@sheiksyedm
sheiksyedm marked this pull request as ready for review August 26, 2026 08:02
Copilot AI lite review requested due to automatic review settings August 26, 2026 08:02
@azure-pipelines

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes iOS/MacCatalyst image measuring so AspectFit images can scale up when either HeightRequest or WidthRequest is explicitly set, aligning behavior with other platforms and resolving #12879.

Changes:

  • Update iOS SizeThatFitsImage to allow scale factors > 1 when an explicit width/height constraint is involved (and treat unconstrained axes as +Infinity).
  • Pass “explicit constraint” flags from iOS handler measurement into SizeThatFitsImage for UIImageView.
  • Add a HostApp repro page and a UI test covering the HorizontalStackLayout + HeightRequest scenario.

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/ImageViewExtensions.cs Adjusts iOS UIImageView measurement logic to allow scaling up for explicit size requests.
src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs Threads explicit-width/height intent into the image measurement path (for UIImageView).
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue12879.cs Adds an Appium UI test that validates the scenario reports “Success”.
src/Controls/tests/TestCases.HostApp/Issues/Issue12879.cs Adds a HostApp page to reproduce/verify the image sizing behavior.

heightConstraint = heightIsExplicit ? virtualView.Height : heightConstraint;

sizeThatFits = imageView.SizeThatFitsImage(new CGSize((float)widthConstraint, (float)heightConstraint));
sizeThatFits = imageView.SizeThatFitsImage(new CGSize((float)widthConstraint, (float)heightConstraint), default, widthIsExplicit, heightIsExplicit);
@@ -0,0 +1,57 @@
namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.Github, 12879, "Image.HeightRequest not respected when Image added in a Horizontal StackLayout", PlatformAffected.iOS)]
@kubaflo
kubaflo changed the base branch from main to inflight/current August 26, 2026 13:01
@kubaflo
kubaflo merged commit 6786d61 into dotnet:inflight/current Aug 26, 2026
37 of 38 checks passed
@github-actions github-actions Bot added this to the .NET 10 SR11 milestone Aug 26, 2026
kubaflo pushed a commit that referenced this pull request Aug 27, 2026
…est (#37267)

<!-- 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, an Image does not scale up on the unconstrained axis when only
HeightRequest or WidthRequest is set—the unconstrained axis remains
capped at its native size. This is most visible with
HorizontalStackLayout + HeightRequest and VerticalStackLayout +
WidthRequest. The reverse combinations can appear to work because the
other axis is large enough to mask the same issue. On Android, the Image
scales correctly with HeightRequest and WidthRequest in all cases.

### Root Cause
- In SizeThatFitsImage (iOS), the scale factor for ScaleAspectFit images
was calculated as Math.Min(imageWidth, widthConstraint) / imageWidth.
This could never exceed 1 (since it always divides the image's own size
by itself once the constraint is larger), so an image could shrink to
fit a smaller space but could never grow beyond its native size, even
when an explicit HeightRequest or WidthRequest requested a larger size,
regardless of StackLayout orientation.

### Description of Change
- Updated SizeThatFitsImage in ImageViewExtensions.cs to accept flags
indicating whether width and height constraints are explicit, and to
allow images to scale up to requested sizes when either is set, instead
of capping at native size.
- Modified GetDesiredSizeFromHandler in ViewHandlerExtensions.iOS.cs to
pass explicitness flags to SizeThatFitsImage, ensuring correct sizing
behavior for images with explicit size requests.


### Issues Fixed
Fixes #12879

### Validated the behaviour in the following platforms
- [ ] Windows
- [ ] Android
- [x] iOS
- [x] Mac

### Output
| Before | After |
----------|----------|
| <img
src="https://github.com/user-attachments/assets/c592756b-d87f-40dd-9073-0a4e03add828">
| <img
src="https://github.com/user-attachments/assets/c884d1d2-a048-44ee-b1d9-ac59b9c29daf">
|
kubaflo pushed a commit that referenced this pull request Sep 2, 2026
…est (#37267)

<!-- 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, an Image does not scale up on the unconstrained axis when only
HeightRequest or WidthRequest is set—the unconstrained axis remains
capped at its native size. This is most visible with
HorizontalStackLayout + HeightRequest and VerticalStackLayout +
WidthRequest. The reverse combinations can appear to work because the
other axis is large enough to mask the same issue. On Android, the Image
scales correctly with HeightRequest and WidthRequest in all cases.

### Root Cause
- In SizeThatFitsImage (iOS), the scale factor for ScaleAspectFit images
was calculated as Math.Min(imageWidth, widthConstraint) / imageWidth.
This could never exceed 1 (since it always divides the image's own size
by itself once the constraint is larger), so an image could shrink to
fit a smaller space but could never grow beyond its native size, even
when an explicit HeightRequest or WidthRequest requested a larger size,
regardless of StackLayout orientation.

### Description of Change
- Updated SizeThatFitsImage in ImageViewExtensions.cs to accept flags
indicating whether width and height constraints are explicit, and to
allow images to scale up to requested sizes when either is set, instead
of capping at native size.
- Modified GetDesiredSizeFromHandler in ViewHandlerExtensions.iOS.cs to
pass explicitness flags to SizeThatFitsImage, ensuring correct sizing
behavior for images with explicit size requests.


### Issues Fixed
Fixes #12879

### Validated the behaviour in the following platforms
- [ ] Windows
- [ ] Android
- [x] iOS
- [x] Mac

### Output
| Before | After |
----------|----------|
| <img
src="https://github.com/user-attachments/assets/c592756b-d87f-40dd-9073-0a4e03add828">
| <img
src="https://github.com/user-attachments/assets/c884d1d2-a048-44ee-b1d9-ac59b9c29daf">
|
kubaflo pushed a commit that referenced this pull request Sep 3, 2026
…est (#37267)

<!-- 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, an Image does not scale up on the unconstrained axis when only
HeightRequest or WidthRequest is set—the unconstrained axis remains
capped at its native size. This is most visible with
HorizontalStackLayout + HeightRequest and VerticalStackLayout +
WidthRequest. The reverse combinations can appear to work because the
other axis is large enough to mask the same issue. On Android, the Image
scales correctly with HeightRequest and WidthRequest in all cases.

### Root Cause
- In SizeThatFitsImage (iOS), the scale factor for ScaleAspectFit images
was calculated as Math.Min(imageWidth, widthConstraint) / imageWidth.
This could never exceed 1 (since it always divides the image's own size
by itself once the constraint is larger), so an image could shrink to
fit a smaller space but could never grow beyond its native size, even
when an explicit HeightRequest or WidthRequest requested a larger size,
regardless of StackLayout orientation.

### Description of Change
- Updated SizeThatFitsImage in ImageViewExtensions.cs to accept flags
indicating whether width and height constraints are explicit, and to
allow images to scale up to requested sizes when either is set, instead
of capping at native size.
- Modified GetDesiredSizeFromHandler in ViewHandlerExtensions.iOS.cs to
pass explicitness flags to SizeThatFitsImage, ensuring correct sizing
behavior for images with explicit size requests.


### Issues Fixed
Fixes #12879

### Validated the behaviour in the following platforms
- [ ] Windows
- [ ] Android
- [x] iOS
- [x] Mac

### Output
| Before | After |
----------|----------|
| <img
src="https://github.com/user-attachments/assets/c592756b-d87f-40dd-9073-0a4e03add828">
| <img
src="https://github.com/user-attachments/assets/c884d1d2-a048-44ee-b1d9-ac59b9c29daf">
|
kubaflo pushed a commit that referenced this pull request Sep 6, 2026
…est (#37267)

<!-- 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, an Image does not scale up on the unconstrained axis when only
HeightRequest or WidthRequest is set—the unconstrained axis remains
capped at its native size. This is most visible with
HorizontalStackLayout + HeightRequest and VerticalStackLayout +
WidthRequest. The reverse combinations can appear to work because the
other axis is large enough to mask the same issue. On Android, the Image
scales correctly with HeightRequest and WidthRequest in all cases.

### Root Cause
- In SizeThatFitsImage (iOS), the scale factor for ScaleAspectFit images
was calculated as Math.Min(imageWidth, widthConstraint) / imageWidth.
This could never exceed 1 (since it always divides the image's own size
by itself once the constraint is larger), so an image could shrink to
fit a smaller space but could never grow beyond its native size, even
when an explicit HeightRequest or WidthRequest requested a larger size,
regardless of StackLayout orientation.

### Description of Change
- Updated SizeThatFitsImage in ImageViewExtensions.cs to accept flags
indicating whether width and height constraints are explicit, and to
allow images to scale up to requested sizes when either is set, instead
of capping at native size.
- Modified GetDesiredSizeFromHandler in ViewHandlerExtensions.iOS.cs to
pass explicitness flags to SizeThatFitsImage, ensuring correct sizing
behavior for images with explicit size requests.


### Issues Fixed
Fixes #12879

### Validated the behaviour in the following platforms
- [ ] Windows
- [ ] Android
- [x] iOS
- [x] Mac

### Output
| Before | After |
----------|----------|
| <img
src="https://github.com/user-attachments/assets/c592756b-d87f-40dd-9073-0a4e03add828">
| <img
src="https://github.com/user-attachments/assets/c884d1d2-a048-44ee-b1d9-ac59b9c29daf">
|
kubaflo pushed a commit that referenced this pull request Sep 7, 2026
…est (#37267)

<!-- 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, an Image does not scale up on the unconstrained axis when only
HeightRequest or WidthRequest is set—the unconstrained axis remains
capped at its native size. This is most visible with
HorizontalStackLayout + HeightRequest and VerticalStackLayout +
WidthRequest. The reverse combinations can appear to work because the
other axis is large enough to mask the same issue. On Android, the Image
scales correctly with HeightRequest and WidthRequest in all cases.

### Root Cause
- In SizeThatFitsImage (iOS), the scale factor for ScaleAspectFit images
was calculated as Math.Min(imageWidth, widthConstraint) / imageWidth.
This could never exceed 1 (since it always divides the image's own size
by itself once the constraint is larger), so an image could shrink to
fit a smaller space but could never grow beyond its native size, even
when an explicit HeightRequest or WidthRequest requested a larger size,
regardless of StackLayout orientation.

### Description of Change
- Updated SizeThatFitsImage in ImageViewExtensions.cs to accept flags
indicating whether width and height constraints are explicit, and to
allow images to scale up to requested sizes when either is set, instead
of capping at native size.
- Modified GetDesiredSizeFromHandler in ViewHandlerExtensions.iOS.cs to
pass explicitness flags to SizeThatFitsImage, ensuring correct sizing
behavior for images with explicit size requests.


### Issues Fixed
Fixes #12879

### Validated the behaviour in the following platforms
- [ ] Windows
- [ ] Android
- [x] iOS
- [x] Mac

### Output
| Before | After |
----------|----------|
| <img
src="https://github.com/user-attachments/assets/c592756b-d87f-40dd-9073-0a4e03add828">
| <img
src="https://github.com/user-attachments/assets/c884d1d2-a048-44ee-b1d9-ac59b9c29daf">
|
kubaflo pushed a commit that referenced this pull request Sep 8, 2026
…est (#37267)

<!-- 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, an Image does not scale up on the unconstrained axis when only
HeightRequest or WidthRequest is set—the unconstrained axis remains
capped at its native size. This is most visible with
HorizontalStackLayout + HeightRequest and VerticalStackLayout +
WidthRequest. The reverse combinations can appear to work because the
other axis is large enough to mask the same issue. On Android, the Image
scales correctly with HeightRequest and WidthRequest in all cases.

### Root Cause
- In SizeThatFitsImage (iOS), the scale factor for ScaleAspectFit images
was calculated as Math.Min(imageWidth, widthConstraint) / imageWidth.
This could never exceed 1 (since it always divides the image's own size
by itself once the constraint is larger), so an image could shrink to
fit a smaller space but could never grow beyond its native size, even
when an explicit HeightRequest or WidthRequest requested a larger size,
regardless of StackLayout orientation.

### Description of Change
- Updated SizeThatFitsImage in ImageViewExtensions.cs to accept flags
indicating whether width and height constraints are explicit, and to
allow images to scale up to requested sizes when either is set, instead
of capping at native size.
- Modified GetDesiredSizeFromHandler in ViewHandlerExtensions.iOS.cs to
pass explicitness flags to SizeThatFitsImage, ensuring correct sizing
behavior for images with explicit size requests.


### Issues Fixed
Fixes #12879

### Validated the behaviour in the following platforms
- [ ] Windows
- [ ] Android
- [x] iOS
- [x] Mac

### Output
| Before | After |
----------|----------|
| <img
src="https://github.com/user-attachments/assets/c592756b-d87f-40dd-9073-0a4e03add828">
| <img
src="https://github.com/user-attachments/assets/c884d1d2-a048-44ee-b1d9-ac59b9c29daf">
|
kubaflo pushed a commit that referenced this pull request Sep 9, 2026
…est (#37267)

<!-- 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, an Image does not scale up on the unconstrained axis when only
HeightRequest or WidthRequest is set—the unconstrained axis remains
capped at its native size. This is most visible with
HorizontalStackLayout + HeightRequest and VerticalStackLayout +
WidthRequest. The reverse combinations can appear to work because the
other axis is large enough to mask the same issue. On Android, the Image
scales correctly with HeightRequest and WidthRequest in all cases.

### Root Cause
- In SizeThatFitsImage (iOS), the scale factor for ScaleAspectFit images
was calculated as Math.Min(imageWidth, widthConstraint) / imageWidth.
This could never exceed 1 (since it always divides the image's own size
by itself once the constraint is larger), so an image could shrink to
fit a smaller space but could never grow beyond its native size, even
when an explicit HeightRequest or WidthRequest requested a larger size,
regardless of StackLayout orientation.

### Description of Change
- Updated SizeThatFitsImage in ImageViewExtensions.cs to accept flags
indicating whether width and height constraints are explicit, and to
allow images to scale up to requested sizes when either is set, instead
of capping at native size.
- Modified GetDesiredSizeFromHandler in ViewHandlerExtensions.iOS.cs to
pass explicitness flags to SizeThatFitsImage, ensuring correct sizing
behavior for images with explicit size requests.


### Issues Fixed
Fixes #12879

### Validated the behaviour in the following platforms
- [ ] Windows
- [ ] Android
- [x] iOS
- [x] Mac

### Output
| Before | After |
----------|----------|
| <img
src="https://github.com/user-attachments/assets/c592756b-d87f-40dd-9073-0a4e03add828">
| <img
src="https://github.com/user-attachments/assets/c884d1d2-a048-44ee-b1d9-ac59b9c29daf">
|
kubaflo pushed a commit that referenced this pull request Sep 11, 2026
…est (#37267)

<!-- 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, an Image does not scale up on the unconstrained axis when only
HeightRequest or WidthRequest is set—the unconstrained axis remains
capped at its native size. This is most visible with
HorizontalStackLayout + HeightRequest and VerticalStackLayout +
WidthRequest. The reverse combinations can appear to work because the
other axis is large enough to mask the same issue. On Android, the Image
scales correctly with HeightRequest and WidthRequest in all cases.

### Root Cause
- In SizeThatFitsImage (iOS), the scale factor for ScaleAspectFit images
was calculated as Math.Min(imageWidth, widthConstraint) / imageWidth.
This could never exceed 1 (since it always divides the image's own size
by itself once the constraint is larger), so an image could shrink to
fit a smaller space but could never grow beyond its native size, even
when an explicit HeightRequest or WidthRequest requested a larger size,
regardless of StackLayout orientation.

### Description of Change
- Updated SizeThatFitsImage in ImageViewExtensions.cs to accept flags
indicating whether width and height constraints are explicit, and to
allow images to scale up to requested sizes when either is set, instead
of capping at native size.
- Modified GetDesiredSizeFromHandler in ViewHandlerExtensions.iOS.cs to
pass explicitness flags to SizeThatFitsImage, ensuring correct sizing
behavior for images with explicit size requests.


### Issues Fixed
Fixes #12879

### Validated the behaviour in the following platforms
- [ ] Windows
- [ ] Android
- [x] iOS
- [x] Mac

### Output
| Before | After |
----------|----------|
| <img
src="https://github.com/user-attachments/assets/c592756b-d87f-40dd-9073-0a4e03add828">
| <img
src="https://github.com/user-attachments/assets/c884d1d2-a048-44ee-b1d9-ac59b9c29daf">
|
kubaflo pushed a commit that referenced this pull request Sep 15, 2026
…est (#37267)

<!-- 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, an Image does not scale up on the unconstrained axis when only
HeightRequest or WidthRequest is set—the unconstrained axis remains
capped at its native size. This is most visible with
HorizontalStackLayout + HeightRequest and VerticalStackLayout +
WidthRequest. The reverse combinations can appear to work because the
other axis is large enough to mask the same issue. On Android, the Image
scales correctly with HeightRequest and WidthRequest in all cases.

### Root Cause
- In SizeThatFitsImage (iOS), the scale factor for ScaleAspectFit images
was calculated as Math.Min(imageWidth, widthConstraint) / imageWidth.
This could never exceed 1 (since it always divides the image's own size
by itself once the constraint is larger), so an image could shrink to
fit a smaller space but could never grow beyond its native size, even
when an explicit HeightRequest or WidthRequest requested a larger size,
regardless of StackLayout orientation.

### Description of Change
- Updated SizeThatFitsImage in ImageViewExtensions.cs to accept flags
indicating whether width and height constraints are explicit, and to
allow images to scale up to requested sizes when either is set, instead
of capping at native size.
- Modified GetDesiredSizeFromHandler in ViewHandlerExtensions.iOS.cs to
pass explicitness flags to SizeThatFitsImage, ensuring correct sizing
behavior for images with explicit size requests.


### Issues Fixed
Fixes #12879

### Validated the behaviour in the following platforms
- [ ] Windows
- [ ] Android
- [x] iOS
- [x] Mac

### Output
| Before | After |
----------|----------|
| <img
src="https://github.com/user-attachments/assets/c592756b-d87f-40dd-9073-0a4e03add828">
| <img
src="https://github.com/user-attachments/assets/c884d1d2-a048-44ee-b1d9-ac59b9c29daf">
|
kubaflo pushed a commit that referenced this pull request Sep 18, 2026
…est (#37267)

<!-- 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, an Image does not scale up on the unconstrained axis when only
HeightRequest or WidthRequest is set—the unconstrained axis remains
capped at its native size. This is most visible with
HorizontalStackLayout + HeightRequest and VerticalStackLayout +
WidthRequest. The reverse combinations can appear to work because the
other axis is large enough to mask the same issue. On Android, the Image
scales correctly with HeightRequest and WidthRequest in all cases.

### Root Cause
- In SizeThatFitsImage (iOS), the scale factor for ScaleAspectFit images
was calculated as Math.Min(imageWidth, widthConstraint) / imageWidth.
This could never exceed 1 (since it always divides the image's own size
by itself once the constraint is larger), so an image could shrink to
fit a smaller space but could never grow beyond its native size, even
when an explicit HeightRequest or WidthRequest requested a larger size,
regardless of StackLayout orientation.

### Description of Change
- Updated SizeThatFitsImage in ImageViewExtensions.cs to accept flags
indicating whether width and height constraints are explicit, and to
allow images to scale up to requested sizes when either is set, instead
of capping at native size.
- Modified GetDesiredSizeFromHandler in ViewHandlerExtensions.iOS.cs to
pass explicitness flags to SizeThatFitsImage, ensuring correct sizing
behavior for images with explicit size requests.


### Issues Fixed
Fixes #12879

### Validated the behaviour in the following platforms
- [ ] Windows
- [ ] Android
- [x] iOS
- [x] Mac

### Output
| Before | After |
----------|----------|
| <img
src="https://github.com/user-attachments/assets/c592756b-d87f-40dd-9073-0a4e03add828">
| <img
src="https://github.com/user-attachments/assets/c884d1d2-a048-44ee-b1d9-ac59b9c29daf">
|
kubaflo pushed a commit that referenced this pull request Sep 21, 2026
…est (#37267)

<!-- 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, an Image does not scale up on the unconstrained axis when only
HeightRequest or WidthRequest is set—the unconstrained axis remains
capped at its native size. This is most visible with
HorizontalStackLayout + HeightRequest and VerticalStackLayout +
WidthRequest. The reverse combinations can appear to work because the
other axis is large enough to mask the same issue. On Android, the Image
scales correctly with HeightRequest and WidthRequest in all cases.

### Root Cause
- In SizeThatFitsImage (iOS), the scale factor for ScaleAspectFit images
was calculated as Math.Min(imageWidth, widthConstraint) / imageWidth.
This could never exceed 1 (since it always divides the image's own size
by itself once the constraint is larger), so an image could shrink to
fit a smaller space but could never grow beyond its native size, even
when an explicit HeightRequest or WidthRequest requested a larger size,
regardless of StackLayout orientation.

### Description of Change
- Updated SizeThatFitsImage in ImageViewExtensions.cs to accept flags
indicating whether width and height constraints are explicit, and to
allow images to scale up to requested sizes when either is set, instead
of capping at native size.
- Modified GetDesiredSizeFromHandler in ViewHandlerExtensions.iOS.cs to
pass explicitness flags to SizeThatFitsImage, ensuring correct sizing
behavior for images with explicit size requests.


### Issues Fixed
Fixes #12879

### Validated the behaviour in the following platforms
- [ ] Windows
- [ ] Android
- [x] iOS
- [x] Mac

### Output
| Before | After |
----------|----------|
| <img
src="https://github.com/user-attachments/assets/c592756b-d87f-40dd-9073-0a4e03add828">
| <img
src="https://github.com/user-attachments/assets/c884d1d2-a048-44ee-b1d9-ac59b9c29daf">
|
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 26, 2026

This branch was previously deployed

1 inactive deployment
copilot-pat-pool — 5e704cdb Deployed Aug 10, 2026 by SyedAbdulAzeemSF4852 via conclusion #1219
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration s/agent-fix-win AI found a better alternative fix than the PR s/agent-gate-passed AI verified tests catch the bug (fail without fix, pass with fix) s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[iOS] Image.HeightRequest not respected when Image added in an Horizontal StackLayout

4 participants