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

如何利用ViewThatFits优先显示首个添加视图?解决适配难题

问题:优先保留顶部项目的垂直自适应布局需求

有多个需要展示的项目,但可用垂直空间无法容纳全部,希望尽可能多显示且优先保留首个项目。使用ViewThatFits几乎能实现需求,但它默认优先显示最后添加的项目。我希望优先显示首个项目,将无法适配的后续项目从最后一项开始隐藏(如先隐藏Seven,再隐藏Six等),但当前代码的实际效果是Seven始终可见,One、Two等被依次隐藏。请问是否可通过ViewThatFits实现该需求,或有其他可行方案?

当前代码

struct ContentView: View {
    let items = ["One", "Two", "Three", "Four", "Five", "Six", "Seven"]
    
    var body: some View {
        VStack(spacing: 0) {
            ForEach(items, id: \.self) {
                RowView(title: $0)
            }
            Spacer()
        }
        .frame(width: 200, height: 150)
        .background(.fill)
    }
}

struct RowView: View {
    let title: String
    
    var body: some View {
        ViewThatFits {
            // 如果这个VStack无法适配可用空间,就会使用0高度视图替代
            VStack(spacing: 0) {
                Text(title)
                    .padding(.vertical, 5)
                
                Divider()
            }
            
            Color.clear
                .frame(height: 0)
        }
    }
}

当前效果截图

显示Four、Five、Six和Seven的截图


解决方案

原方案失效原因

原代码中每个RowView单独使用ViewThatFits,但VStack的布局逻辑是从上到下分配空间:当整体空间不足时,底部的Spacer会向上挤压顶部的行,导致顶部行的ViewThatFits先触发切换到0高度视图,最终保留底部项目,与需求完全相反。

方案一:嵌套ViewThatFits实现固定数量项目适配

通过嵌套ViewThatFits,优先尝试显示全部项目,依次减少末尾项目直到找到适配的最大子集,逻辑简单直接:

struct ContentView: View {
    let items = ["One", "Two", "Three", "Four", "Five", "Six", "Seven"]
    
    var body: some View {
        ViewThatFits(in: .vertical) {
            FullListView(items: items)
            FullListView(items: Array(items.dropLast()))
            FullListView(items: Array(items.dropLast(2)))
            FullListView(items: Array(items.dropLast(3)))
            FullListView(items: Array(items.dropLast(4)))
            FullListView(items: Array(items.dropLast(5)))
            FullListView(items: [items.first!])
        }
        .frame(width: 200, height: 150)
        .background(.fill)
    }
}

struct FullListView: View {
    let items: [String]
    
    var body: some View {
        VStack(spacing: 0) {
            ForEach(items, id: \.self) { title in
                VStack(spacing: 0) {
                    Text(title)
                        .padding(.vertical, 5)
                    Divider()
                }
            }
            Spacer()
        }
    }
}

方案二:动态计算可显示项目(适配任意数量项目)

如果项目数量较多,手动嵌套不现实,可以通过PreferenceKey收集行高,动态计算能容纳的顶部项目数量:

  1. 定义偏好键收集行高:
struct RowHeightPreferenceKey: PreferenceKey {
    static var defaultValue: [CGFloat] = []
    
    static func reduce(value: inout [CGFloat], nextValue: () -> [CGFloat]) {
        value.append(contentsOf: nextValue())
    }
}
  1. 修改ContentView实现动态适配:
struct ContentView: View {
    let items = ["One", "Two", "Three", "Four", "Five", "Six", "Seven"]
    @State private var visibleCount = 0
    
    var body: some View {
        GeometryReader { geo in
            VStack(spacing: 0) {
                ForEach(Array(items.prefix(visibleCount)), id: \.self) { title in
                    RowView(title: title)
                        .background(
                            GeometryReader { rowGeo in
                                Color.clear
                                    .preference(key: RowHeightPreferenceKey.self, value: [rowGeo.size.height])
                            }
                        )
                }
                Spacer()
            }
            .onPreferenceChange(RowHeightPreferenceKey.self) { heights in
                let totalHeight = heights.reduce(0, +)
                if totalHeight <= geo.size.height {
                    if visibleCount < items.count {
                        visibleCount += 1
                    }
                } else {
                    if visibleCount > 1 {
                        visibleCount -= 1
                    }
                }
            }
            .onAppear {
                visibleCount = items.count
            }
        }
        .frame(width: 200, height: 150)
        .background(.fill)
    }
}

struct RowView: View {
    let title: String
    
    var body: some View {
        VStack(spacing: 0) {
            Text(title)
                .padding(.vertical, 5)
            Divider()
        }
    }
}

该方案会自动计算可容纳的最大顶部项目数,空间变化时也会动态调整。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 12:16:06