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

SwiftUI:网格项内容超出列宽时如何移至下一行避免文本换行?

解决SwiftUI网格布局中长内容项单独换行的问题

核心思路

你需要的是流式布局(而非固定列数的网格):短内容项自动填充一行(默认三列),长内容项单独占一行,同时保证文本不换行。LazyVGrid的adaptive模式无法实现单个项的跨行调整,因此需要自定义流式布局来满足需求。

步骤1:实现自定义流式布局FlowLayout

添加以下自定义Layout结构体到你的代码中:

struct FlowLayout: Layout {
    var spacing: CGFloat = 8 // 可根据需求调整间距
    
    func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
        guard !subviews.isEmpty else { return .zero }
        let maxWidth = proposal.width ?? .infinity
        var currentWidth: CGFloat = 0
        var currentHeight: CGFloat = 0
        var lineHeight: CGFloat = 0
        
        for subview in subviews {
            let itemSize = subview.sizeThatFits(.unspecified)
            // 当前行放不下时,换行
            if currentWidth + itemSize.width + spacing > maxWidth, currentWidth > 0 {
                currentHeight += lineHeight + spacing
                currentWidth = itemSize.width
                lineHeight = itemSize.height
            } else {
                currentWidth += itemSize.width + (currentWidth == 0 ? 0 : spacing)
                lineHeight = max(lineHeight, itemSize.height)
            }
        }
        currentHeight += lineHeight
        return CGSize(width: maxWidth, height: currentHeight)
    }
    
    func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
        guard !subviews.isEmpty else { return }
        let maxWidth = bounds.width
        var x = bounds.minX
        var y = bounds.minY
        var lineHeight: CGFloat = 0
        
        for subview in subviews {
            let itemSize = subview.sizeThatFits(.unspecified)
            // 当前行放不下时,换行
            if x + itemSize.width > maxWidth, x > bounds.minX {
                y += lineHeight + spacing
                x = bounds.minX
                lineHeight = itemSize.height
            } else {
                lineHeight = max(lineHeight, itemSize.height)
            }
            // 放置子视图
            subview.place(at: CGPoint(x: x, y: y), proposal: ProposedViewSize(itemSize))
            x += itemSize.width + spacing
        }
    }
}

步骤2:修改TagView确保文本不换行

在你的TagView中,给文本添加以下修饰符,保证文本不换行且保持固有宽度:

Text(tag.name)
    .lineLimit(1) // 限制单行
    .fixedSize()  // 保持文本固有宽度,不压缩不换行
    // 其他样式修饰符...

步骤3:替换LazyVGrid为FlowLayout

把原来的LazyVGrid部分替换为自定义的FlowLayout:

var filterBar: some View {
    VStack {
        filterBarTitle
        if filtersOpened {
            // 移除原来的columns定义,替换为FlowLayout
            FlowLayout(spacing: 0) { // 这里的spacing和你原来的一致
                ForEach(allRecipeTags) { recipeTag in
                    TagView(tag: recipeTag, selected: selectedTags.contains(recipeTag), canToggle: true, onTap: {
                        if selectedTags.contains(recipeTag) {
                            print("removing \(recipeTag)")
                            selectedTags.removeAll(where: { $0 == recipeTag })
                        } else {
                            print("adding \(recipeTag)")
                            selectedTags.append(recipeTag)
                        }
                    })
                    .border(.red)
                }
            }
            .border(.blue)
        }
    }
    .padding([.top, .bottom])
    .border(width: 1, edges: [.bottom], color: Color("24292F").opacity(0.3))
    .padding(.horizontal)
    .padding([.bottom], filtersOpened ? 8 : 0)
}

效果说明

  • 当所有TagView的宽度都不超过容器宽度的1/3时,会自动排列成三列;
  • 遇到长文本的TagView时,该View会自动换行单独占一行;
  • 所有文本都保持单行显示,不会被压缩或换行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 23:45:00