SwiftUI中如何判断Text是否达到lineLimit()限制?
实现带行限制判断的Text视图(无需GeometryReader)
要实现限制3行的Text,超出时显示展开按钮,核心是准确判断文本是否被行限制截断。这里提供一种基于文本自身渲染高度对比的方案,无需依赖GeometryReader读取父布局尺寸:
核心思路
通过对比无限制行数的文本高度和限制行数后的文本高度,如果前者大于后者,说明文本被截断(超出3行),此时显示展开/收起按钮。我们用SwiftUI的PreferenceKey来传递两个文本的高度值,避免直接嵌套GeometryReader带来的布局依赖。
完整代码实现
struct TruncatableText: View { let text: String let lineLimit: Int @State private var isExpanded = false @State private var isTruncated = false var body: some View { VStack(alignment: .leading) { // 主显示文本,根据展开状态切换行限制 Text(text) .lineLimit(isExpanded ? nil : lineLimit) // 隐藏的无限制文本,计算完整高度 .background( Text(text) .lineLimit(nil) .background(GeometryReader { proxy in Color.clear.preference(key: FullTextHeightKey.self, value: proxy.size.height) }) .hidden() ) // 计算当前显示状态下的文本高度 .background(GeometryReader { proxy in Color.clear.preference(key: TruncatedTextHeightKey.self, value: proxy.size.height) }) // 对比高度判断是否截断 .onPreferenceChange(FullTextHeightKey.self) { fullHeight in if let truncatedHeight = TruncatedTextHeightKey.value { isTruncated = fullHeight > truncatedHeight } } // 只有文本被截断时才显示按钮 if isTruncated { Button(isExpanded ? "收起" : "展开") { isExpanded.toggle() } .font(.caption) .foregroundColor(.blue) } } } } // 自定义PreferenceKey,用于传递完整文本高度 private struct FullTextHeightKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = nextValue() } } // 自定义PreferenceKey,用于传递截断后文本高度 private struct TruncatedTextHeightKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = nextValue() } }
使用示例
struct ContentView: View { let shortText = "这是一段短文本,不会超过3行。" let longText = "这是一段很长的文本,用来测试行限制功能。这是一段很长的文本,用来测试行限制功能。这是一段很长的文本,用来测试行限制功能。这是一段很长的文本,用来测试行限制功能。这是一段很长的文本,用来测试行限制功能。" var body: some View { VStack(spacing: 20) { TruncatableText(text: shortText, lineLimit: 3) TruncatableText(text: longText, lineLimit: 3) } .padding() } }
方案优势
- 完全基于文本自身的渲染状态判断,不依赖父视图尺寸,适配所有字体、字号和屏幕尺寸。
- 逻辑直观,通过高度差准确判断文本是否被截断,避免了GeometryReader可能带来的布局冲突。
- 代码模块化,自定义视图可以直接复用在任何场景。
内容的提问来源于stack exchange,提问作者Volodymyr Bobyr
相关产品推荐
相关产品推荐

