diff --git a/README.md b/README.md index e7d93f5..e3e0006 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,9 @@ struct ExpandableText_Test: View { .font(.body)//optional .foregroundColor(.primary)//optional .lineLimit(3)//optional - .animation(.easeOut)//optional + .expandButton(TextSet(text: "more", font: .body, color: .blue)) + .collapseButton(TextSet(text: "less", font: .body, color: .blue)) + .expandAnimation(.easeOut)//optional .padding(.horizontal, 24)//optional } } @@ -43,7 +45,8 @@ Modifier | Default `.font(_ font: Font)` | `.body` `.lineLimit(_ lineLimit: Int)` | `3` `.foregroundColor(_ color: Color)` | `.primary` -`.expandButtonText(_ text: String)` | `"more"` -`.expandButtonColor(_ color: Color)` | `.blue` +`.expandButton(_ expandButton: TextSet)` | `TextSet(text: "more", font: .body, color: .blue)` +`.collapseButton(_ collapseButton: TextSet)` | `nil(If it's nil, it doesn't show)` +`.expandAnimation(_ animation: Animation?)` | `.none` diff --git a/Sources/ExpandableText/ExpandableText.swift b/Sources/ExpandableText/ExpandableText.swift index c5edd4a..b39a733 100644 --- a/Sources/ExpandableText/ExpandableText.swift +++ b/Sources/ExpandableText/ExpandableText.swift @@ -11,17 +11,17 @@ public struct ExpandableText: View { var text : String var font: Font = .body - var lineLimit : Int = 3 - var foregroundColor : Color = .primary + var lineLimit: Int = 3 + var foregroundColor: Color = .primary - var expandButtonText : String = "more" - var expandButtonColor : Color = .blue + var expandButton: TextSet = TextSet(text: "more", font: .body, color: .blue) + var collapseButton: TextSet? = nil + + var animation: Animation? = .none - var uiFont: UIFont = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body) @State private var expand : Bool = false @State private var truncated : Bool = false - @State private var size : CGFloat = 0 - + @State private var fullSize: CGFloat = 0 public init(text: String) { self.text = text @@ -32,6 +32,7 @@ public struct ExpandableText: View { .font(font) .foregroundColor(foregroundColor) .lineLimit(expand == true ? nil : lineLimit) + .animation(animation, value: expand) .mask( VStack(spacing: 0){ Rectangle() @@ -40,55 +41,96 @@ public struct ExpandableText: View { HStack(spacing: 0){ Rectangle() .foregroundColor(.black) - if !expand && truncated{ - HStack(alignment: .bottom,spacing: 0){ - LinearGradient( - gradient: Gradient(stops: [ - Gradient.Stop(color: .black, location: 0), - Gradient.Stop(color: .clear, location: 0.8)]), - startPoint: .leading, - endPoint: .trailing) - .frame(width: 32, height: expandButtonText.heightOfString(usingFont: uiFont)) - - Rectangle() - .foregroundColor(.clear) - .frame(width: expandButtonText.widthOfString(usingFont: uiFont), alignment: .center) + if truncated{ + if !expand { + HStack(alignment: .bottom,spacing: 0){ + LinearGradient( + gradient: Gradient(stops: [ + Gradient.Stop(color: .black, location: 0), + Gradient.Stop(color: .clear, location: 0.8)]), + startPoint: .leading, + endPoint: .trailing) + .frame(width: 32, height: expandButton.text.heightOfString(usingFont: fontToUIFont(font: expandButton.font))) + + Rectangle() + .foregroundColor(.clear) + .frame(width: expandButton.text.widthOfString(usingFont: fontToUIFont(font: expandButton.font)), alignment: .center) + } + } + else if let collapseButton = collapseButton { + HStack(alignment: .bottom,spacing: 0){ + LinearGradient( + gradient: Gradient(stops: [ + Gradient.Stop(color: .black, location: 0), + Gradient.Stop(color: .clear, location: 0.8)]), + startPoint: .leading, + endPoint: .trailing) + .frame(width: 32, height: collapseButton.text.heightOfString(usingFont: fontToUIFont(font: collapseButton.font))) + + Rectangle() + .foregroundColor(.clear) + .frame(width: collapseButton.text.widthOfString(usingFont: fontToUIFont(font: collapseButton.font)), alignment: .center) + } } } } - .frame(height: expandButtonText.heightOfString(usingFont: uiFont)) + .frame(height: expandButton.text.heightOfString(usingFont: fontToUIFont(font: font))) } ) - if truncated && !expand { - Button(action: { - self.expand = true - }, label: { - Text(expandButtonText) - .font(font) - .foregroundColor(expandButtonColor) - }) + if truncated { + if let collapseButton = collapseButton { + Button(action: { + self.expand.toggle() + }, label: { + Text(expand == false ? expandButton.text : collapseButton.text) + .font(expand == false ? expandButton.font : collapseButton.font) + .foregroundColor(expand == false ? expandButton.color : collapseButton.color) + }) + } + else if !expand { + Button(action: { + self.expand = true + }, label: { + Text(expandButton.text) + .font(expandButton.font) + .foregroundColor(expandButton.color) + }) + } } } .background( - Text(text) - .lineLimit(lineLimit) - .background(GeometryReader { geo in - Color.clear.onAppear() { - let size = CGSize(width: geo.size.width, height: .greatestFiniteMagnitude) - - let attributes:[NSAttributedString.Key:Any] = [NSAttributedString.Key.font: uiFont] - let attributedText = NSAttributedString(string: text, attributes: attributes) - - let textSize = attributedText.boundingRect(with: size, options: NSStringDrawingOptions.usesLineFragmentOrigin, context: nil) - - if textSize.size.height > geo.size.height { - truncated = true - - self.size = textSize.size.height - } + ZStack{ + if !truncated { + if fullSize != 0 { + Text(text) + .font(font) + .lineLimit(lineLimit) + .background( + GeometryReader { geo in + Color.clear + .onAppear { + if fullSize > geo.size.height { + self.truncated = true + print(geo.size.height) + } + } + } + ) } - }) + + Text(text) + .font(font) + .lineLimit(999) + .fixedSize(horizontal: false, vertical: true) + .background(GeometryReader { geo in + Color.clear + .onAppear() { + self.fullSize = geo.size.height + } + }) + } + } .hidden() ) } diff --git a/Sources/ExpandableText/Extensions.swift b/Sources/ExpandableText/Extensions.swift index 79cda07..184a2cb 100644 --- a/Sources/ExpandableText/Extensions.swift +++ b/Sources/ExpandableText/Extensions.swift @@ -11,61 +11,6 @@ extension ExpandableText { public func font(_ font: Font) -> ExpandableText { var result = self - if #available(iOS 14.0, *) { - switch font { - case .largeTitle: - result.uiFont = UIFont.preferredFont(forTextStyle: .largeTitle) - case .title: - result.uiFont = UIFont.preferredFont(forTextStyle: .title1) - case .title2: - result.uiFont = UIFont.preferredFont(forTextStyle: .title2) - case .title3: - result.uiFont = UIFont.preferredFont(forTextStyle: .title3) - case .headline: - result.uiFont = UIFont.preferredFont(forTextStyle: .headline) - case .subheadline: - result.uiFont = UIFont.preferredFont(forTextStyle: .subheadline) - case .callout: - result.uiFont = UIFont.preferredFont(forTextStyle: .callout) - case .caption: - result.uiFont = UIFont.preferredFont(forTextStyle: .caption1) - case .caption2: - result.uiFont = UIFont.preferredFont(forTextStyle: .caption2) - case .footnote: - result.uiFont = UIFont.preferredFont(forTextStyle: .footnote) - case .body: - result.uiFont = UIFont.preferredFont(forTextStyle: .body) - default: - result.uiFont = UIFont.preferredFont(forTextStyle: .body) - } - } else { - switch font { - case .largeTitle: - result.uiFont = UIFont.preferredFont(forTextStyle: .largeTitle) - case .title: - result.uiFont = UIFont.preferredFont(forTextStyle: .title1) -// case .title2: -// result.uiFont = UIFont.preferredFont(forTextStyle: .title2) -// case .title3: -// result.uiFont = UIFont.preferredFont(forTextStyle: .title3) - case .headline: - result.uiFont = UIFont.preferredFont(forTextStyle: .headline) - case .subheadline: - result.uiFont = UIFont.preferredFont(forTextStyle: .subheadline) - case .callout: - result.uiFont = UIFont.preferredFont(forTextStyle: .callout) - case .caption: - result.uiFont = UIFont.preferredFont(forTextStyle: .caption1) -// case .caption2: -// result.uiFont = UIFont.preferredFont(forTextStyle: .caption2) - case .footnote: - result.uiFont = UIFont.preferredFont(forTextStyle: .footnote) - case .body: - result.uiFont = UIFont.preferredFont(forTextStyle: .body) - default: - result.uiFont = UIFont.preferredFont(forTextStyle: .body) - } - } result.font = font return result @@ -84,16 +29,24 @@ extension ExpandableText { return result } - public func expandButtonText(_ text: String) -> ExpandableText { + public func expandButton(_ expandButton: TextSet) -> ExpandableText { var result = self - result.expandButtonText = text + result.expandButton = expandButton return result } - public func expandButtonColor(_ color: Color) -> ExpandableText { + + public func collapseButton(_ collapseButton: TextSet) -> ExpandableText { + var result = self + + result.collapseButton = collapseButton + return result + } + + public func expandAnimation(_ animation: Animation?) -> ExpandableText { var result = self - result.expandButtonColor = color + result.animation = animation return result } } @@ -111,3 +64,67 @@ extension String { return size.width } } + +public struct TextSet { + var text: String + var font: Font + var color: Color +} + +func fontToUIFont(font: Font) -> UIFont { + if #available(iOS 14.0, *) { + switch font { + case .largeTitle: + return UIFont.preferredFont(forTextStyle: .largeTitle) + case .title: + return UIFont.preferredFont(forTextStyle: .title1) + case .title2: + return UIFont.preferredFont(forTextStyle: .title2) + case .title3: + return UIFont.preferredFont(forTextStyle: .title3) + case .headline: + return UIFont.preferredFont(forTextStyle: .headline) + case .subheadline: + return UIFont.preferredFont(forTextStyle: .subheadline) + case .callout: + return UIFont.preferredFont(forTextStyle: .callout) + case .caption: + return UIFont.preferredFont(forTextStyle: .caption1) + case .caption2: + return UIFont.preferredFont(forTextStyle: .caption2) + case .footnote: + return UIFont.preferredFont(forTextStyle: .footnote) + case .body: + return UIFont.preferredFont(forTextStyle: .body) + default: + return UIFont.preferredFont(forTextStyle: .body) + } + } else { + switch font { + case .largeTitle: + return UIFont.preferredFont(forTextStyle: .largeTitle) + case .title: + return UIFont.preferredFont(forTextStyle: .title1) + // case .title2: + // return UIFont.preferredFont(forTextStyle: .title2) + // case .title3: + // return UIFont.preferredFont(forTextStyle: .title3) + case .headline: + return UIFont.preferredFont(forTextStyle: .headline) + case .subheadline: + return UIFont.preferredFont(forTextStyle: .subheadline) + case .callout: + return UIFont.preferredFont(forTextStyle: .callout) + case .caption: + return UIFont.preferredFont(forTextStyle: .caption1) + // case .caption2: + // return UIFont.preferredFont(forTextStyle: .caption2) + case .footnote: + return UIFont.preferredFont(forTextStyle: .footnote) + case .body: + return UIFont.preferredFont(forTextStyle: .body) + default: + return UIFont.preferredFont(forTextStyle: .body) + } + } +}