Skip to content

Fix AspectFit image stretching on iOS and macOS by including IFileImageSource in InvalidateMeasure condition #30593

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

Copilot
Copy link
Contributor

@Copilot Copilot AI commented Jul 11, 2025

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!

Description

This PR fixes an issue where AspectFit fails to preserve image stretching on iOS and macOS when images are loaded from files using ImageSource.FromFile(). The problem occurred specifically in scenarios where:

  1. An embedded image resource is loaded from the application assembly
  2. The image is downsized to a maximum dimension (e.g., 100 pixels)
  3. The processed image is saved to the device's cache directory
  4. The image is displayed using ImageSource.FromFile() with Aspect="AspectFit"

Problem

On Windows and Android, images displayed correctly with proper AspectFit stretching:

Windows - Working correctly

Android - Working correctly

However, on iOS and macOS, the same images failed to stretch properly and appeared at their original size:

macOS - Not working

Root Cause

The issue was in ImageHandler.iOS.cs where InvalidateMeasure was only being called for IStreamImageSource types, but not for IFileImageSource types. When images are loaded from files, they need to trigger layout invalidation to ensure proper aspect ratio handling.

// Before - only stream sources triggered invalidation
if (Handler?.VirtualView is IImage image && image.Source is IStreamImageSource)
    imageView.InvalidateMeasure(image);

File-based images (created via ImageSource.FromFile()) implement IFileImageSource, not IStreamImageSource, so they weren't triggering the necessary layout invalidation.

Solution

Extended the condition to include both IStreamImageSource and IFileImageSource:

// After - both stream and file sources trigger invalidation  
if (Handler?.VirtualView is IImage image && (image.Source is IStreamImageSource || image.Source is IFileImageSource))
    imageView.InvalidateMeasure(image);

This ensures that both stream-based and file-based images trigger the layout invalidation needed for proper AspectFit behavior on iOS and macOS.

Testing

  • ✅ All existing unit tests pass
  • ✅ Image source service tests continue to work correctly
  • ✅ Build succeeds on all target frameworks
  • ✅ Code follows repository formatting standards

The fix is minimal and surgical, maintaining full compatibility with existing code while addressing the specific AspectFit issue on iOS and macOS platforms.

Fixes #30465.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

…dateMeasure condition

Co-authored-by: mattleibow <1096616+mattleibow@users.noreply.github.com>
@Copilot Copilot AI changed the title [WIP] AspectFit Fails to Preserve Image Stretching on iOS and macOS After Downsizing Fix AspectFit image stretching on iOS and macOS by including IFileImageSource in InvalidateMeasure condition Jul 11, 2025
@Copilot Copilot AI requested a review from mattleibow July 11, 2025 20:26
Copilot finished work on behalf of mattleibow July 11, 2025 20:26
@@ -67,7 +67,7 @@ public override void SetImageSource(UIImage? platformImage)

Handler?.UpdateValue(nameof(IImage.IsAnimationPlaying));

if (Handler?.VirtualView is IImage image && image.Source is IStreamImageSource)
if (Handler?.VirtualView is IImage image && (image.Source is IStreamImageSource || image.Source is IFileImageSource))
Copy link
Member

Choose a reason for hiding this comment

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

Why do we even have type checks? We should always invalidate.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're absolutely right. I've removed the type checking logic completely. Now InvalidateMeasure is called whenever a new image is set, regardless of the image source type. This is cleaner and more robust than maintaining a list of specific types that need invalidation. Change made in f21941f.

…lidateMeasure when image is set

This addresses @mattleibow's feedback to remove the type checking logic and always invalidate measure when a new image is set, regardless of the image source type.

Co-authored-by: mattleibow <1096616+mattleibow@users.noreply.github.com>
@Copilot Copilot AI requested a review from mattleibow July 12, 2025 10:46
Copilot finished work on behalf of mattleibow July 12, 2025 10:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

AspectFit Fails to Preserve Image Stretching on iOS and macOS After Downsizing
2 participants