Repository navigation
[iOS/Mac] Fix Image not enlarging to explicit HeightRequest/WidthRequest - #37267
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 37267Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 37267" |
|
Azure Pipelines: Successfully started running 1 pipeline(s). There may be pipelines that require an authorized user to comment /azp run to run. |
|
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. |
This comment has been minimized.
This comment has been minimized.
MauiBot
left a comment
There was a problem hiding this comment.
AI Review Summary
@SyedAbdulAzeemSF4852 — new AI review results are available based on commit
5e704cd.
🗂️ 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.cssrc/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, headfix-12879 - Issue:
#12879— on iOS, anImageinside a horizontalStackLayoutremains at its native size instead of enlarging to an explicitHeightRequest; 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.cssrc/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:
- Determines whether each handler constraint came from an explicit virtual-view width or height.
- Adds two explicitness flags to
SizeThatFitsImage. - Computes aspect ratios from raw constraints.
- Caps the scale factor at
1only 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.cssrc/Core/src/Platform/iOS/ImageViewExtensions.cs
Related call sites may be inspected, but changes must remain focused:
src/Core/src/Platform/iOS/WrapperView.cssrc/Controls/src/Core/ImageButton/ImageButton.iOS.cssrc/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 usesgpt-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-fixskill'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
- Major — explicit constraints can bypass
MaximumWidth/MaximumHeight.ViewHandlerExtensions.iOS.csfirst clamps the incoming constraints to the virtual view's maximums, then replaces them with raw explicit width and height values. OnceSizeThatFitsImageis allowed to scale above1, a request such asWidthRequest=400, MaximumWidth=100can 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. - Major — wrapped images still use the old capped measurement behavior. Images routed through
WrapperViewbecause of shadow, clip, border, or related decoration callSizeThatFitsImagewithout the explicitness information, so the reported issue remains for those ordinaryImagevariants. The wrapper measurement path should propagate whether either request is explicit. - Moderate — non-
AspectFitenlargement remains outside the fix.AspectFillandFillstill 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. - 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.
- Minor — two adjacent boolean parameters encode one policy. A single
allowUpscaleparameter 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
Imageinstances do not receive the new policy and continue to reproduce the original failure. ImageButtonand toolbar call sites should not be changed without confirming their distinct sizing semantics; this PR's reported control isImage.
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;
|
Azure Pipelines: Successfully started running 1 pipeline(s). There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
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
SizeThatFitsImageto 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
SizeThatFitsImageforUIImageView. - 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)] | |||
…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"> |
…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"> |
…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"> |
…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"> |
…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"> |
…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"> |
…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"> |
…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"> |
…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"> |
…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"> |
…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"> |
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
Root Cause
Description of Change
Issues Fixed
Fixes #12879
Validated the behaviour in the following platforms
Output