Published on

Image in SwiftUI

Authors
  • Name
    Twitter

从昨天晚上到今天对Image的官方文档的阅读,我发现自己之前根本没有了解清楚Image这个控件的布局原理和技巧。

主要是从Apple的这篇文章中,我学到很多的知识 fitting images into available space

图片大小差异很大,从单像素的PNG文件到数百万像素的数码摄影图像。由于设备大小也各不相同,应用通常需要运行时调整图片大小,以确保它们适合可见的用户界面。SwiftUI提供了调整器来缩放、裁剪和变换图片,以完美适应您的界面。

假如我们有一张很大尺寸的图片 Landscape_4

我们通过下面的代码来加载这张图片的时候:

Image("Landscape_4")
        .frame(width: 300, height: 400, alignment: .topLeading)
        .border(.blue)
其加载效果如下: SwiftUI-FIIAS-unscaled

As seen in the following screenshot, the image data loads at full size into the view, so only the clouds from the upper left of the original image are visible. Because the image renders at full size, and the blue frame is smaller than the original image, the image displays beyond the area bounded by the frame.

如图所示,图像数据以全尺寸加载到视图中,因此只能看到原始图像左上角的云朵。由于图像以全尺寸渲染,且蓝色边框小于原始图像,图像显示在边框所围区域之外。

To fix this, you need to apply two modifiers to the Image:

  • resizable(capInsets:resizingMode:) tells the image view to adjust the image representation to match the size of the view. By default, this modifier scales the image by reducing the size of larger images and enlarges images smaller than the view. By itself, this modifier scales each axis of the image independently.
  • aspectRatio(_:contentMode:) corrects the behavior where the image scaling is different for each axis. This preserves the image’s original aspect ratio, using one of two strategies defined by the ContentMode enumeration. ContentMode.fit scales the image to fit the view size along one axis, possibly leaving empty space along the other axis. ContentMode.fill scales the image to fill the entire view.

要修复这个问题,您需要将两个修饰符应用到图像上:

  • resizable(capInsets:resizingMode:) 告诉图像视图调整图像呈现以匹配视图的大小。默认情况下,此修饰符通过减小较大图像的大小和放大小于视图的图像来缩放图像。本身,此修饰符独立缩放图像的每个轴。

  • aspectRatio(_:contentMode:) 修正了图像缩放在每个轴上不同的行为。这通过使用内容模式枚举定义的两个策略之一来保留图像的原始宽高比。ContentMode.fit 沿一个轴缩放图像以适应视图大小,可能在另一个轴上留下空白空间。ContentMode.fill 将图像缩放以填充整个视图。

详解 resizable(capInsets:resizingMode:)

Sets the mode by which SwiftUI resizes an image to fit its space.

func resizable(
    capInsets: EdgeInsets = EdgeInsets(),
    resizingMode: Image.ResizingMode = .stretch
) -> Image

Parameters

capInsets

Inset values that indicate a portion of the image that SwiftUI doesn’t resize. Inset values(内边距值)表示SwiftUI不会调整大小的图像部分。

意思就是在图像的四周,会有一个内边距值。这个值不会被 resizable 调整大小。其类型是EdgeInsets !!!注意,这里不是给图像四周留白,而是使在图像上指出一个区域,不被调整大小。

EdgeInsets是SwiftUI提供的结构体,用于精确定义视图四周的内边距,其大小类型是CGFloat。它是 SwiftUI 布局中控制间距的核心类型之一

resizingMode

The mode by which SwiftUI resizes the image.

Return Value

An image, with the new resizing behavior set.

resizingMode

Enumeration

其中的resizingMode的类型解释Image.ResizingMode

The modes that SwiftUI uses to resize an image to fit within its containing view.

enum ResizingMode

Topics

Getting resizing modes

case stretch (拉伸)

A mode to enlarge or reduce the size of an image so that it fills the available space. 拉伸模式会调整图像的大小(放大或缩小),使其完全填充可用空间。图像的宽高比可能会发生改变,导致图像变形(例如被拉长或压扁),以适应容器的大小。

case tile (瓷砖)

A mode to repeat the image at its original size, as many times as necessary to fill the available space. 平铺模式以图像的原始大小重复绘制图像,多次填充可用空间,而不会改变图像的尺寸或宽高比。图像会像“瓷砖”一样排列,直到填满整个容器。

aspectRatio(_:contentMode:) 宽高比

Constrains this view’s dimensions to the specified aspect ratio.

nonisolated
func aspectRatio(
    _ aspectRatio: CGFloat? = nil,
    contentMode: ContentMode
) -> some View

Parameters

aspectRatio

The ratio of width to height to use for the resulting view. Use nil to maintain the current aspect ratio in the resulting view.

contentMode

A flag that indicates whether this view fits or fills the parent context.

Return Value

A view that constrains this view’s dimensions to the aspect ratio of the given size using contentMode as its scaling algorithm.

为了修复这个问题,我们需要使用两个modifiers在这个Image视图:

  1. resizable(capInsets:resizingMode) 其目的是使Image视图中的image的大小变成可调整状态。
  2. aspectRatio(_:contentMode:)