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

如何实现SwiftUI ScrollView自适应内容大小并支持设置最大高度

实现方案

要实现「内容高度小于阈值时自适应收缩、超过阈值时固定最大高度可滚动」的ScrollView,核心是动态获取ScrollView内部内容的实际高度,再将ScrollView的高度限定为内容高度和最大阈值的较小值。


第一步:定义高度读取用的PreferenceKey

我们需要用SwiftUI的PreferenceKey机制读取ScrollView内部列表的实际高度:

private struct HeightPreferenceKey: PreferenceKey {
    static var defaultValue: CGFloat = 0
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = nextValue()
    }
}

第二步:修改ContentView代码

完整修改后的代码如下:

struct ContentView: View {
    @State var List1: [String] = []
    @State var List2: [String] = []
    // 存储两个列表的实际内容高度
    @State private var list1ContentHeight: CGFloat = 0
    @State private var list2ContentHeight: CGFloat = 0
    // 统一设置两个列表的最大高度,可自行调整
    private let maxListHeight: CGFloat = 40

    var body: some View {
        VStack {
            Button("1-5") {
                List1 = ["1"]
                List2 = ["a", "b", "c", "d", "e"]
            }
            Button("3-3") {
                List1 = ["1", "2", "3"]
                List2 = ["a", "b", "c"]
            }
            Button("5-1") {
                List1 = ["1", "2", "3", "4", "5"]
                List2 = ["a"]
            }
            
            // 第一个列表的ScrollView
            ScrollView {
                VStack {
                    ForEach(List1.indices, id: \.self) { idx in
                        Text(List1[idx])
                    }
                }
                // 读取列表实际高度
                .background(GeometryReader { geo in
                    Color.clear
                        .preference(key: HeightPreferenceKey.self, value: geo.size.height)
                })
                // 同步高度到状态变量
                .onPreferenceChange(HeightPreferenceKey.self) { height in
                    list1ContentHeight = height
                }
            }
            // 高度取内容高度和最大高度的较小值
            .frame(height: min(list1ContentHeight, maxListHeight))
            
            Text("middle")
            
            // 第二个列表的ScrollView,逻辑和第一个完全一致
            ScrollView {
                VStack {
                    ForEach(List2.indices, id: \.self) { idx in
                        Text(List2[idx])
                    }
                }
                .background(GeometryReader { geo in
                    Color.clear
                        .preference(key: HeightPreferenceKey.self, value: geo.size.height)
                })
                .onPreferenceChange(HeightPreferenceKey.self) { height in
                    list2ContentHeight = height
                }
            }
            .frame(height: min(list2ContentHeight, maxListHeight))
            
            Spacer()
            Text("last")
        }
    }
}

效果说明

  • 列表项总高度小于maxListHeight时,ScrollView高度跟随内容自动收缩,不会占用多余空间
  • 列表项总高度超过maxListHeight时,ScrollView固定为maxListHeight,内部内容可正常滚动
  • 两个列表独立适配,互不影响

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 12:39:03