diff --git a/Sources/ExpandableText/ExpandableText+Modifiers.swift b/Sources/ExpandableText/ExpandableText+Modifiers.swift index d7cb4ee..2d93b5b 100644 --- a/Sources/ExpandableText/ExpandableText+Modifiers.swift +++ b/Sources/ExpandableText/ExpandableText+Modifiers.swift @@ -1,6 +1,6 @@ // // ExpandableText+Modifiers.swift -// +// // // Created by ned on 25/02/23. // @@ -9,7 +9,7 @@ import Foundation import SwiftUI public extension ExpandableText { - + /** Sets the font for the text in the `ExpandableText` instance. - Parameter font: The font to use for the text. Defaults to `body` @@ -20,7 +20,7 @@ public extension ExpandableText { copy.font = font return copy } - + /** Sets the foreground color for the text in the `ExpandableText` instance. - Parameter color: The foreground color to use for the text. Defaults to `primary` @@ -31,7 +31,7 @@ public extension ExpandableText { copy.color = color return copy } - + /** Sets the maximum number of lines to use for rendering the text in the `ExpandableText` instance. - Parameter limit: The maximum number of lines to use for rendering the text. Defaults to `3` @@ -42,7 +42,7 @@ public extension ExpandableText { copy.lineLimit = limit return copy } - + /** Sets the text to use for the "show more" button in the `ExpandableText` instance. - Parameter moreText: The text to use for the "show more" button. Defaults to `more` @@ -53,7 +53,18 @@ public extension ExpandableText { copy.moreButtonText = moreText return copy } - + + /** + Sets the text to use for the "show less" button in the `ExpandableText` instance. + - Parameter lessText: The text to use for the "show less" button. Defaults to `less` + - Returns: A new `ExpandableText` instance with the specified "show less" button text applied. + */ + func lessButtonText(_ lessText: String) -> Self { + var copy = self + copy.lessButtonText = lessText + return copy + } + /** Sets the font to use for the "show more" button in the `ExpandableText` instance. - Parameter font: The font to use for the "show more" button. Defaults to the same font as the text @@ -64,7 +75,7 @@ public extension ExpandableText { copy.moreButtonFont = font return copy } - + /** Sets the color to use for the "show more" button in the `ExpandableText` instance. - Parameter color: The color to use for the "show more" button. Defaults to `accentColor` @@ -75,7 +86,7 @@ public extension ExpandableText { copy.moreButtonColor = color return copy } - + /** Sets the animation to use when expanding the `ExpandableText` instance. - Parameter animation: The animation to use for the expansion. Defaults to `default` @@ -86,7 +97,7 @@ public extension ExpandableText { copy.expandAnimation = animation return copy } - + /** Enables collapsing behavior by tapping on the text body when the state is expanded. - Parameter value: Whether or not to enable collapse functionality. @@ -97,7 +108,7 @@ public extension ExpandableText { copy.collapseEnabled = value return copy } - + /** Sets whether multiple consecutive newline characters should be trimmed when truncating the text in the `ExpandableText` instance. - Parameter value: A boolean value indicating whether to trim multiple consecutive newline characters. Defaults to `true` diff --git a/Sources/ExpandableText/ExpandableText.swift b/Sources/ExpandableText/ExpandableText.swift index 43cdff0..e15232d 100644 --- a/Sources/ExpandableText/ExpandableText.swift +++ b/Sources/ExpandableText/ExpandableText.swift @@ -32,19 +32,20 @@ public struct ExpandableText: View { @State private var intrinsicSize: CGSize = .zero @State private var truncatedSize: CGSize = .zero - @State private var moreTextSize: CGSize = .zero - + @State private var buttonTextSize: CGSize = .zero + private let text: String internal var font: Font = .body internal var color: Color = .primary internal var lineLimit: Int = 3 internal var moreButtonText: String = "more" + internal var lessButtonText: String = "less" // Added for "Show Less" feature internal var moreButtonFont: Font? internal var moreButtonColor: Color = .accentColor internal var expandAnimation: Animation = .default internal var collapseEnabled: Bool = false internal var trimMultipleNewlinesWhenTruncated: Bool = true - + /** Initializes a new `ExpandableText` instance with the specified text string, trimmed of any leading or trailing whitespace and newline characters. - Parameter text: The initial text string to display in the `ExpandableText` view. @@ -53,11 +54,11 @@ public struct ExpandableText: View { public init(_ text: String) { self.text = text.trimmingCharacters(in: .whitespacesAndNewlines) } - + public var body: some View { content .lineLimit(isExpanded ? nil : lineLimit) - .applyingTruncationMask(size: moreTextSize, enabled: shouldShowMoreButton) + .applyingTruncationMask(size: buttonTextSize, enabled: shouldShowMoreButton) .readSize { size in truncatedSize = size isTruncated = truncatedSize != intrinsicSize @@ -73,10 +74,10 @@ public struct ExpandableText: View { } ) .background( - Text(moreButtonText) + Text(isExpanded ? lessButtonText : moreButtonText) // Use the appropriate text based on expansion state .font(moreButtonFont ?? font) .hidden() - .readSize { moreTextSize = $0 } + .readSize { buttonTextSize = $0 } ) .contentShape(Rectangle()) .onTapGesture { @@ -86,18 +87,18 @@ public struct ExpandableText: View { } } .modifier(OverlayAdapter(alignment: .trailingLastTextBaseline, view: { - if shouldShowMoreButton { + if shouldShowMoreButton || shouldShowLessButton { Button { withAnimation(expandAnimation) { isExpanded.toggle() } } label: { - Text(moreButtonText) + Text(isExpanded ? lessButtonText : moreButtonText) // Toggle button text .font(moreButtonFont ?? font) .foregroundColor(moreButtonColor) } } })) } - + private var content: some View { Text(.init( trimMultipleNewlinesWhenTruncated @@ -112,7 +113,12 @@ public struct ExpandableText: View { private var shouldShowMoreButton: Bool { !isExpanded && isTruncated } - + + + private var shouldShowLessButton: Bool { + isExpanded && collapseEnabled + } + private var textTrimmingDoubleNewlines: String { text.replacingOccurrences(of: #"\n\s*\n"#, with: "\n", options: .regularExpression) }