我有一个动态文本,可以是大号或小号 默认情况下,我只显示3行,并且仅在需要时显示,然后添加“更多”按钮。 当用户点击此按钮(“更多”)时,我将显示所有测试。
我问,如何在SwiftUI中知道是否以3行作为文本?
答案 0 :(得分:7)
您可以使用GeometryReader
确定文本字段的宽度,然后将其与有关字体的信息一起使用,以计算显示整个文本所需的边界矩形的大小。如果该高度超过文本视图,那么我们知道该文本已被截断。
struct LongText: View {
/* Indicates whether the user want to see all the text or not. */
@State private var expanded: Bool = false
/* Indicates whether the text has been truncated in its display. */
@State private var truncated: Bool = false
private var text: String
init(_ text: String) {
self.text = text
}
private func determineTruncation(_ geometry: GeometryProxy) {
// Calculate the bounding box we'd need to render the
// text given the width from the GeometryReader.
let total = self.text.boundingRect(
with: CGSize(
width: geometry.size.width,
height: .greatestFiniteMagnitude
),
options: .usesLineFragmentOrigin,
attributes: [.font: UIFont.systemFont(ofSize: 16)],
context: nil
)
if total.size.height > geometry.size.height {
self.truncated = true
}
}
var body: some View {
VStack(alignment: .leading, spacing: 10) {
Text(self.text)
.font(.system(size: 16))
.lineLimit(self.expanded ? nil : 3)
// see https://swiftui-lab.com/geometryreader-to-the-rescue/,
// and https://swiftui-lab.com/communicating-with-the-view-tree-part-1/
.background(GeometryReader { geometry in
Color.clear.onAppear {
self.determineTruncation(geometry)
}
})
if self.truncated {
self.toggleButton
}
}
}
var toggleButton: some View {
Button(action: { self.expanded.toggle() }) {
Text(self.expanded ? "Show less" : "Show more")
.font(.caption)
}
}
}
这就是长文本和短文本的外观:
希望这会有所帮助。
答案 1 :(得分:2)
基于bhuemer的出色作品,此版本尊重SwiftUI的本地字体,而不需要硬编码的UIFont。而不是使用String布局读取“完整”文本的大小,而是将Text渲染三遍:一次是真实的,一次是有行数限制的,一次是没有行数限制的。然后,它使用两个GR来比较最后两个GR。
struct LongText: View {
/* Indicates whether the user want to see all the text or not. */
@State private var expanded: Bool = false
/* Indicates whether the text has been truncated in its display. */
@State private var truncated: Bool = false
private var text: String
var lineLimit = 3
init(_ text: String) {
self.text = text
}
var body: some View {
VStack(alignment: .leading) {
// Render the real text (which might or might not be limited)
Text(text)
.lineLimit(expanded ? nil : lineLimit)
.background(
// Render the limited text and measure its size
Text(text).lineLimit(lineLimit)
.background(GeometryReader { displayedGeometry in
// Create a ZStack with unbounded height to allow the inner Text as much
// height as it likes, but no extra width.
ZStack {
// Render the text without restrictions and measure its size
Text(self.text)
.background(GeometryReader { fullGeometry in
// And compare the two
Color.clear.onAppear {
self.truncated = fullGeometry.size.height > displayedGeometry.size.height
}
})
}
.frame(height: .greatestFiniteMagnitude)
})
.hidden() // Hide the background
)
if truncated { toggleButton }
}
}
var toggleButton: some View {
Button(action: { self.expanded.toggle() }) {
Text(self.expanded ? "Show less" : "Show more")
.font(.caption)
}
}
}
以下显示了周围视图的行为。请注意,这种方法像常规文本一样支持LongText(...).font(.largeTitle)
。
struct ContentView: View {
let longString = "This is very long text designed to create enough wrapping to force a More button to appear. Just a little more should push it over the edge and get us to one more line."
var body: some View {
VStack {
Text("BEFORE TEXT")
LongText(longString).font(.largeTitle)
LongText(longString).font(.caption)
Text("AFTER TEXT")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
答案 2 :(得分:1)
这里是一个例子:
struct TextDemo: View {
@State var moreText = true
var body: some View {
Group {
Button(action: { self.moreText.toggle()} ) { Text("More") }
Text("hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello")
.frame(width: 300)
.lineLimit( moreText ? 3: nil)
}
}
}