如何在SwiftUI中设置文本折叠的动画



我需要SwiftUI动画/转换行为方面的帮助。在将lineLimit从0更改为5期间,我必须实现折叠/扩展Text,反之亦然。

目前我有一个工作代码,你可以在下面看到它。

import SwiftUI
public struct ContentView: View {
private let description: String

@State private var isCollapsed: Bool = true
@State private var isExpandeButtonShow: Bool = false

public init(description: String) {
self.description = description
}
// MARK: - Body
public var body: some View {
VStack(alignment: .leading, spacing: 0) {
text.padding(.top, 26)
.font(Font.system(size: 12))
if isExpandeButtonShow {
collapseButton.padding(.top, 9)
}

Spacer()
}
.padding(.horizontal, 16)
}
// MARK: - Private
private var text: some View {
Text(description)
.lineLimit(isCollapsed ? 5 : nil)
.background(
GeometryReader { geometry in
Color.clear.onAppear {
truncateIfNeeded(withGeometry: geometry)
}
}
)
.animation(.linear(duration: 4))
.transition(.opacity)
}
private func truncateIfNeeded(withGeometry geometry: GeometryProxy) {
let total = description.boundingRect(
with: CGSize(
width: geometry.size.width,
height: .greatestFiniteMagnitude
),
options: .usesLineFragmentOrigin,
attributes: [.font: UIFont.systemFont(ofSize: 12)],
context: nil
)
if total.size.height > geometry.size.height {
isExpandeButtonShow = true
}
}
private var collapseButton: some View {
button(title: collapseButtonTitle()) {
isCollapsed.toggle()
}
}

private func button(title: String, handler: @escaping () -> Void) -> some View {
Button(action: handler) {
Text(title)
.foregroundColor(Color.blue)
.contentShape(Rectangle())
}
.buttonStyle(PlainButtonStyle())
}
private func collapseButtonTitle() -> String {
isCollapsed ? "Collapse" : "Expand"
}
}

它几乎满足了我的要求。但这种行为让我有两点痛苦。首先,当我尝试折叠文本时,不会启动任何动画/过渡。它会立即崩溃。其次,我想让一行一行地出现,并为每一行设置从0不透明度到1的动画。我该怎么做?

任何想法都会有所帮助。当做

你想做这样的事情吗?

struct RandomView: View {

@State var isCollapsed: Bool = false
@State var lineCount: Int = 1
@State var isAnimating: Bool = false

var body: some View {
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
.font(.largeTitle)
.lineLimit(lineCount)
.animation(.linear(duration: 1.0))
.onTapGesture {
animateLines()
}
}

func animateLines() {

guard !isAnimating else { return } // Check not currently animating
isCollapsed.toggle()
isAnimating = true
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (timer) in

if isCollapsed {
//Expand
lineCount += 1
if lineCount >= 10 { // max lines
timer.invalidate()
isAnimating = false
}
} else {
//Collapse
lineCount -= 1
if lineCount <= 1 { // max lines
timer.invalidate()
isAnimating = false
}
}

}
timer.fire()
}
}

编辑:如果要逐行设置不透明度的动画,则需要将"字符串"分解为不同的"文本"项目,每个项目都有各自的不透明度。像这样:

var body: some View {
VStack {
Text("Line 1")
.opacity(lineCount >= 1 ? 1 : 0)
Text("Line 2")
.opacity(lineCount >= 2 ? 1 : 0)
Text("Line 3")
.opacity(lineCount >= 3 ? 1 : 0)
Text("Line 4")
.opacity(lineCount >= 4 ? 1 : 0)
Text("Line 5")
.opacity(lineCount >= 5 ? 1 : 0)
}
.font(.largeTitle)
.animation(Animation.linear(duration: 1.0))
.onTapGesture {
animateLines()
}
}

最新更新