119 lines
4.5 KiB
Swift
119 lines
4.5 KiB
Swift
//
|
|
// Swiftfin is subject to the terms of the Mozilla Public
|
|
// License, v2.0. If a copy of the MPL was not distributed with this
|
|
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
|
//
|
|
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
struct TruncatedTextView: View {
|
|
|
|
@State
|
|
private var truncated: Bool = false
|
|
@State
|
|
private var shrinkText: String
|
|
private var text: String
|
|
let font: UIFont
|
|
let lineLimit: Int
|
|
let seeMoreAction: () -> Void
|
|
|
|
private var moreLessText: String {
|
|
if !truncated {
|
|
return ""
|
|
} else {
|
|
return L10n.seeMore
|
|
}
|
|
}
|
|
|
|
init(
|
|
_ text: String,
|
|
lineLimit: Int,
|
|
font: UIFont = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body),
|
|
seeMoreAction: @escaping () -> Void
|
|
) {
|
|
self.text = text
|
|
self.lineLimit = lineLimit
|
|
_shrinkText = State(wrappedValue: text)
|
|
self.font = font
|
|
self.seeMoreAction = seeMoreAction
|
|
}
|
|
|
|
var body: some View {
|
|
VStack(alignment: .center) {
|
|
Group {
|
|
Text(shrinkText)
|
|
.overlay {
|
|
if truncated {
|
|
LinearGradient(
|
|
stops: [
|
|
.init(color: .systemBackground.opacity(0), location: 0.5),
|
|
.init(color: .systemBackground.opacity(0.8), location: 0.7),
|
|
.init(color: .systemBackground, location: 1),
|
|
],
|
|
startPoint: .top,
|
|
endPoint: .bottom
|
|
)
|
|
}
|
|
}
|
|
}
|
|
.lineLimit(lineLimit)
|
|
.background {
|
|
// Render the limited text and measure its size
|
|
Text(text)
|
|
.lineLimit(lineLimit + 2)
|
|
.background {
|
|
GeometryReader { visibleTextGeometry in
|
|
Color.clear
|
|
.onAppear {
|
|
let size = CGSize(width: visibleTextGeometry.size.width, height: .greatestFiniteMagnitude)
|
|
let attributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: font]
|
|
var low = 0
|
|
var heigh = shrinkText.count
|
|
var mid = heigh
|
|
while (heigh - low) > 1 {
|
|
let attributedText = NSAttributedString(string: shrinkText, attributes: attributes)
|
|
let boundingRect = attributedText.boundingRect(
|
|
with: size,
|
|
options: NSStringDrawingOptions
|
|
.usesLineFragmentOrigin,
|
|
context: nil
|
|
)
|
|
if boundingRect.size.height > visibleTextGeometry.size.height {
|
|
truncated = true
|
|
heigh = mid
|
|
mid = (heigh + low) / 2
|
|
|
|
} else {
|
|
if mid == text.count {
|
|
break
|
|
} else {
|
|
low = mid
|
|
mid = (low + heigh) / 2
|
|
}
|
|
}
|
|
shrinkText = String(text.prefix(mid))
|
|
}
|
|
|
|
if truncated {
|
|
shrinkText = String(shrinkText.prefix(shrinkText.count - 2))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.hidden()
|
|
}
|
|
.font(Font(font))
|
|
|
|
if truncated {
|
|
Button {
|
|
seeMoreAction()
|
|
} label: {
|
|
Text(moreLessText)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|