【问题标题】:How to animate collapsing for a Text in SwiftUI如何在 SwiftUI 中为文本的折叠设置动画
【发布时间】:2021-03-04 06:24:29
【问题描述】:

我需要有关 SwiftUI 动画/过渡行为的帮助。在将 lineLimit 从 nil 更改为 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。我该怎么做?

任何想法都会有所帮助。问候。

【问题讨论】:

  • “我希望一行一行地出现,每行从 0 不透明度到 1 进行动画处理” - 只有当每一行都在单独的视图中时,才能做到这一点,不在一个文本中。
  • 这种行为没有办法实现吗?你确定吗?这听起来完全正确。但我希望 SwiftUI 世界有一个好的解决方法。

标签: ios swift animation swiftui transition


【解决方案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()
        }

    }

【讨论】:

  • 这几乎是我需要的,谢谢。但是,如何在您的示例中添加不透明动画?
猜你喜欢
  • 1970-01-01
  • 2018-08-08
  • 1970-01-01
  • 1970-01-01
  • 2019-11-14
  • 2019-08-13
  • 2022-01-20
  • 2018-01-26
  • 1970-01-01
相关资源
最近更新 更多