如何根据用户视图位置动态调整SwiftUI列表的字体大小?
实现滚动时动态缩放文本的方案
要实现List滚动时顶部文本字体随滚动距离动态缩放,核心是监听列表的滚动偏移量,根据偏移值计算字体缩放比例(fraction)。以下分两种版本提供解决方案:
iOS 17+ 简洁方案
利用iOS 17新增的onScrollChange修饰符直接监听滚动偏移,代码实现简单直观:
struct ContentView: View { @State var fraction: CGFloat = 1 @State private var scrollOffset: CGFloat = 0 // 滚动阈值:超过这个距离后字体不再缩小 private let scrollThreshold: CGFloat = 200 // 最小缩放比例:避免字体过小不可读 private let minFraction: CGFloat = 0.2 var body: some View { List { Text("Hello world!") .bold() .font(.system(size: 23 * fraction)) .frame(maxWidth: .infinity, alignment: .center) .listRowSeparator(.hidden) Section(header: Text("A list of things")) { ForEach((1...7), id: \.self) { Text("\($0)") } } Section(header: Text("Another list")) { ForEach((1...7), id: \.self) { Text("\($0)") } } Text("Some other content...") } .onScrollChange(of: \.contentOffset.y) { _, newValue in scrollOffset = newValue // 计算缩放比例:偏移量从0到阈值时,比例从1线性降到最小值 fraction = max(minFraction, 1 - (scrollOffset / scrollThreshold)) } } }
iOS 16及以下兼容方案
对于旧系统,需要通过PreferenceKey结合GeometryReader间接获取滚动偏移:
首先定义一个用于传递滚动偏移的PreferenceKey:
struct ScrollOffsetPreferenceKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = nextValue() } }
然后修改ContentView实现监听逻辑:
struct ContentView: View { @State var fraction: CGFloat = 1 private let scrollThreshold: CGFloat = 200 private let minFraction: CGFloat = 0.2 var body: some View { List { Text("Hello world!") .bold() .font(.system(size: 23 * fraction)) .frame(maxWidth: .infinity, alignment: .center) .listRowSeparator(.hidden) Section(header: Text("A list of things")) { ForEach((1...7), id: \.self) { Text("\($0)") } } Section(header: Text("Another list")) { ForEach((1...7), id: \.self) { Text("\($0)") } } Text("Some other content...") } .background( GeometryReader { proxy in Color.clear .preference(key: ScrollOffsetPreferenceKey.self, value: proxy.frame(in: .named("scrollView")).minY) } ) .coordinateSpace(name: "scrollView") .onPreferenceChange(ScrollOffsetPreferenceKey.self) { offset in // 向下滚动时offset为负数,取绝对值计算缩放比例 let scrollOffset = abs(offset) fraction = max(minFraction, 1 - (scrollOffset / scrollThreshold)) } } }
自定义调整说明
scrollThreshold:控制字体缩小的滚动距离阈值,数值越小,字体缩小速度越快。minFraction:设置字体的最小缩放比例,建议保留0.2以上的值,避免字体完全不可读。- 当滚动回到列表顶部时,偏移量归零,
fraction会自动恢复为1,字体回到原始大小。
内容的提问来源于stack exchange,提问作者ej5607
相关产品推荐
相关产品推荐

