You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何构建自适应屏幕宽度、项宽动态的等高行照片网格?

实现列数可配置、每行等高的照片网格方案

要实现列数可修改、每行高度一致、照片宽度自适应填满屏幕的网格效果,核心是统一行内图片的高度比例,再整体缩放适配屏幕宽度。以下是具体实现方案:

1. 修正图片单元格视图

将图片的缩放模式从scaledToFit改为scaledToFill,并通过传入的缩放因子约束尺寸,避免图片变形:

struct MediaCellView: View {
    let metadata: tableMetadata
    let scaleFactor: CGFloat
    @StateObject private var viewModel = MediaCellViewModel()

    var body: some View {
        Image(uiImage: viewModel.thumbnail)
            .resizable()
            .scaledToFill()
            .frame(
                width: CGFloat(metadata.width == 0 ? 500 : metadata.width) * scaleFactor,
                height: CGFloat(metadata.height == 0 ? 500 : metadata.height) * scaleFactor
            )
            .clipped()
            .onAppear {
                viewModel.configure(metadata: metadata)
                viewModel.downloadThumbnail()
            }
    }
}

2. 构建主网格视图

将图片数据按指定列数分块为行,计算每行的统一缩放比例,再用HStack+LazyVStack组合实现每行等高的网格:

struct PhotoGridView: View {
    let viewModel: YourViewModel
    let columnsCount: Int // 可配置的列数

    // 将数据按列数分块为行数据
    private var rowData: [[tableMetadata]] {
        viewModel.metadatas.chunked(into: columnsCount)
    }

    var body: some View {
        LazyVStack(spacing: 0) {
            ForEach(rowData, id: \.self) { rowMetadatas in
                let scale = calculateScale(for: rowMetadatas)
                HStack(spacing: 0) {
                    ForEach(rowMetadatas, id: \.self) { metadata in
                        MediaCellView(metadata: metadata, scaleFactor: scale)
                    }
                }
            }
        }
    }

    // 计算每行图片的统一缩放比例
    private func calculateScale(for rowMetadatas: [tableMetadata]) -> CGFloat {
        let screenWidth = UIScreen.main.bounds.width
        
        // 取行内图片的最大高度作为基准高度,保证行内所有图片最终高度一致
        let maxRowHeight = rowMetadatas.reduce(CGFloat(500)) { currentMax, metadata in
            max(currentMax, CGFloat(metadata.height == 0 ? 500 : metadata.height))
        }

        // 计算所有图片缩放至基准高度后的总宽度
        var totalScaledWidth: CGFloat = 0
        for metadata in rowMetadatas {
            let imgWidth = CGFloat(metadata.width == 0 ? 500 : metadata.width)
            let imgHeight = CGFloat(metadata.height == 0 ? 500 : metadata.height)
            let scaleToBase = maxRowHeight / imgHeight
            totalScaledWidth += imgWidth * scaleToBase
        }

        // 返回适配屏幕宽度的最终缩放比例
        return screenWidth / totalScaledWidth
    }
}

3. 关键逻辑说明

  • 统一行高:以行内最高图片的高度为基准,将其他图片按比例缩放至该高度,保证每行所有图片高度一致
  • 适配屏幕宽度:计算缩放至基准高度后的行总宽度,再用屏幕宽度除以该总宽度,得到最终缩放比例,确保整行刚好填满屏幕
  • 图片无变形:使用scaledToFill+clipped保证图片按比例填满容器,同时裁剪超出部分,避免拉伸变形

你之前方案的问题点

  1. scaledToFit的问题:该模式会保持图片比例但不会填满容器,导致行高无法统一,图片之间出现空隙
  2. LazyVGrid的局限性:默认的灵活网格列配置是基于屏幕宽度均分,无法根据每行图片的原始比例动态调整宽度
  3. 缩放逻辑错误:之前的getSize方法先将图片缩放至行内最大高度,再计算适配比例,但未正确关联最终的视图尺寸约束

内容的提问来源于stack exchange,提问作者Milen Pivchev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 07:55:59