簡體   English   中英

如何在 SwiftUI 中為文本的折疊設置動畫

[英]How to animate collapsing for a Text in SwiftUI

我需要有關 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 的動畫。我該怎么做?

任何想法都會有所幫助。 問候。

你正在嘗試做這樣的事情嗎?

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()
        }

    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM