在 SwiftUI 中限制图像的大小

问题描述 投票:0回答:1

我需要在视图中显示一些异步加载的图像,它们的大小可以从非常小(例如 20x20)到巨大(2000+)。

获取大图像以适应视图非常简单:

AsyncImage(
    url: image.thumb,
    content: { image in
        image.resizable()
            .aspectRatio(contentMode: .fit)
    },
    placeholder: {
        ProgressView()
    }
)

...但这也会拉伸较小的图像以适应视图。

我该如何表达“请以原始尺寸显示此图像,除非大于视图,在这种情况下将其夹紧”?(在 CSS 中,类似于

img { maxWidth: 100%; height: auto; }

swiftui
1个回答
0
投票

SwiftUI 缩放图像仅当它更大时的答案显示了一种防止常规

Image
在缩放以适应时放大的方法(这是我的答案)。这个想法是使用
ViewThatFits
,首先提供未缩放的图像,然后提供缩放以适合的图像。

对于

AsyncImage
,可以在内容闭包内使用
ViewThatFits
。但是,我发现只有在
ViewThatFits
上设置最大宽度和高度时它才有效。另外,我发现即使将
.fixedSize()
应用于
ViewThatFits
AsyncImage
,框架尺寸仍然大于所需的尺寸。

因此,另一种方法是使用自定义

Layout


这是一个自定义的

Layout
,可以缩小以适应,但不会放大。其工作原理如下:

  • 仅需要一个子子视图。
  • 子视图的理想大小是在缓存阶段找到的。
  • (私有)函数
    sizeForProposal
    确定适合建议尺寸的缩放尺寸。
  • 函数
    sizeThatFits
    只是委托给
    sizeForProposal
  • 功能
    placeSubviews
    应用由
    sizeForProposal
    提供的尺寸。
struct ScaledDownToFit: Layout {
    typealias Cache = CGSize

    func makeCache(subviews: Subviews) -> CGSize {
        if let firstSubview = subviews.first {
            firstSubview.sizeThatFits(.unspecified)
        } else {
            .zero
        }
    }

    private func sizeForProposal(proposal: ProposedViewSize, viewSize: CGSize) -> CGSize {
        let aspectRatio = viewSize.width / viewSize.height
        let constrainedWidth = min(viewSize.width, proposal.width ?? .infinity)
        let constrainedHeight = min(viewSize.height, proposal.height ?? .infinity)
        let w: CGFloat
        let h: CGFloat
        if constrainedWidth / constrainedHeight < aspectRatio {
            w = constrainedWidth
            h = constrainedWidth / aspectRatio
        } else {
            w = constrainedHeight * aspectRatio
            h = constrainedHeight
        }
        return CGSize(width: w, height: h)
    }

    public func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout CGSize) -> CGSize {
        cache == .zero ? .zero : sizeForProposal(proposal: proposal, viewSize: cache)
    }

    func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout CGSize) {
        if let firstSubview = subviews.first, cache != .zero {
            let targetSize = sizeForProposal(proposal: proposal, viewSize: cache)
            firstSubview.place(at: bounds.origin, proposal: .init(targetSize))
        }
    }
}

您可能会发现定义

View
扩展也很有用:

extension View {
    func scaledDownToFit() -> some View {
        ScaledDownToFit {
            self
        }
    }
}

使用示例:

AsyncImage(
    url: image.thumb,
    content: { image in
        image
            .resizable()
            .scaledDownToFit()
    },
    placeholder: {
        ProgressView()
    }
)
.border(.red)
© www.soinside.com 2019 - 2024. All rights reserved.