import SwiftUI struct ExpandableTextView: View { let text: String let lineLimit: Int let moreTitle: String let collapseTitle: String let font: SodaTypography let foregroundColor: Color @State private var isExpanded = false @State private var fullTextHeight: CGFloat = 0 @State private var limitedTextHeight: CGFloat = 0 init( text: String, lineLimit: Int = 3, moreTitle: String = I18n.HomeRecommendation.more, collapseTitle: String = I18n.HomeRecommendation.collapse, font: SodaTypography = .body3, foregroundColor: Color = Color.gray500 ) { self.text = text self.lineLimit = lineLimit self.moreTitle = moreTitle self.collapseTitle = collapseTitle self.font = font self.foregroundColor = foregroundColor } var body: some View { VStack(alignment: .leading, spacing: SodaSpacing.s8) { Text(text) .appFont(font) .foregroundColor(foregroundColor) .lineLimit(isExpanded ? nil : lineLimit) .background(measuringText(lineLimit: nil, height: $fullTextHeight)) .background(measuringText(lineLimit: lineLimit, height: $limitedTextHeight)) if shouldShowToggle { Button { isExpanded.toggle() } label: { Text(isExpanded ? collapseTitle : moreTitle) .appFont(.body5) .foregroundColor(.white) } .buttonStyle(.plain) } } } private var shouldShowToggle: Bool { fullTextHeight > limitedTextHeight + 1 } private func measuringText(lineLimit: Int?, height: Binding) -> some View { Text(text) .appFont(font) .lineLimit(lineLimit) .fixedSize(horizontal: false, vertical: true) .hidden() .background( GeometryReader { proxy in Color.clear .onAppear { height.wrappedValue = proxy.size.height } .onChange(of: proxy.size.height) { newHeight in height.wrappedValue = newHeight } .onChange(of: text) { _ in height.wrappedValue = proxy.size.height } } ) } } struct ExpandableTextView_Previews: PreviewProvider { static var previews: some View { ExpandableTextView(text: "긴 사업자 정보 텍스트가 들어가는 영역입니다. 세 줄을 넘어가면 더보기 버튼이 표시되고, 더보기 버튼을 누르면 전체 문구가 표시됩니다.") .padding(SodaSpacing.s20) .background(Color.black) .previewLayout(.sizeThatFits) } }