SwiftUI列表视图单元格实现文本展开/收起功能
SwiftUI实现描述文本展开/收起功能
需求回顾
- 默认仅显示2行描述文本
- 点击“更多”展开全部内容,点击“收起”恢复为2行
- 当描述内容不足2行或无描述时,自动隐藏“更多/收起”按钮
实现代码
首先添加用于检测文本高度的PreferenceKey:
struct TextHeightPreferenceKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = nextValue() } }
然后修改SurveyListView的代码,重点调整surveyListCell实现:
struct SurveyListView: View { @Environment(\.dismiss) var dismiss @StateObject private var viewModel = SurveyViewModel() @State private var selectedDataID: String? @State private var gotoQuestions = false // 补充原代码缺失的状态变量 var body: some View { ZStack { VStack() { ScrollView{ ForEach(0..<viewModel.allSurvey.count, id: \.self) { ind in Button { selectedDataID = viewModel.allSurvey[ind].id gotoQuestions = true } label: { surveyListCell(survey: viewModel.allSurvey[ind]) .foregroundStyle(.black) } } .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) .navigationDestination(isPresented: $gotoQuestions) { SurveyQuestionsView(id: selectedDataID ?? "") .toolbar(.hidden, for: .navigationBar) } } Spacer() } } .onAppear{ viewModel.fetchSurveyList { status in } } } @ViewBuilder func surveyListCell(survey: AllSurvey) -> some View { @State private var isExpanded = false @State private var textHeight: CGFloat = 0 // 计算2行文本的预估高度(行高*2 + 行间距) let twoLineHeight = (UIFont(name: "Calibri", size: 16)?.lineHeight ?? 20) * 2 + 4 VStack(alignment: .leading){ Text(survey.title ?? "N/A") .font(.calibriRegular(with: 18)) .padding(.horizontal) .padding(.bottom, 3) Text(survey.description ?? "N/A") .font(.calibriRegular(with: 16)) .foregroundStyle(.gray) .lineLimit(isExpanded ? nil : 2) .padding(.horizontal) .padding(.top, 1) // 通过GeometryReader获取文本实际高度 .background( GeometryReader { proxy in Color.clear .preference(key: TextHeightPreferenceKey.self, value: proxy.size.height) } ) .onPreferenceChange(TextHeightPreferenceKey.self) { height in textHeight = height } // 仅当文本高度超过2行预估高度时显示按钮 if textHeight > twoLineHeight { HStack{ Spacer() Text(isExpanded ? "收起" : "更多") .font(.calibriRegular(with: 15)) .padding(.horizontal) .foregroundStyle(.green) .onTapGesture { isExpanded.toggle() } } .padding(.bottom, 5) } HStack(spacing: 0){ Text("Published On:") .font(.calibriRegular(with: 14)) .padding(.horizontal) .foregroundStyle(.gray) Text(survey.publishedOn ?? "N/A") .font(.calibriRegular(with: 14)) .foregroundStyle(.gray) .padding(.horizontal) Spacer() } } .frame(maxWidth: .infinity, alignment: .leading) } } // 保留原项目的字体扩展 extension Font { static func calibriRegular(with size: CGFloat) -> Font { return Font.custom("Calibri", size: size) } }
关键说明
- 文本高度检测:通过
GeometryReader和自定义PreferenceKey获取文本实际渲染高度,与预估的2行文本高度对比,动态控制按钮显示 - 独立状态管理:每个cell使用独立的
isExpanded状态变量,避免多个cell状态互相干扰 - 行数控制:利用
lineLimit修饰符,收起时限制为2行,展开时设为nil显示全部内容
内容的提问来源于stack exchange,提问作者Swift
相关产品推荐
相关产品推荐

