簡體   English   中英

SwiftUI 在值更改時為文本組件設置動畫

[英]SwiftUI animate text component when value changes

假設我有一個顯示兩個數字的 SwiftUI 組件。 下面的示例代碼模擬了一個簡單的情況,即隨機不時更新兩個數字。

struct ContentView: View {
    @State var number1: Int = 0
    @State var number2: Int = 0
    var timer:Timer {
        Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
            if (Int.random(in: 0...100)<10) {
                print("random event happened, increase numbers!")
                self.number1 = self.number1 + 1
                self.number2 = self.number2 + 1
            }
        }
    }
    var body: some View {
        ZStack {
            SampleNumbersView(numbers1: number1, numbers2: number2)
        }.onAppear {
            let _ = self.timer
        }
    }
}

struct SampleNumbersView: View {
    var numbers1:Int = 0
    var numbers2:Int = 0
    var body: some View {
        VStack {
            Text("Number 1: \(numbers1)")
            Text("Number 2: \(numbers2)")
        }
    }
}

上面的代碼有效。 但是,如果我希望在更新兩個數字時發生以下動畫序列,該怎么辦?

  1. 先顯示更新后的數字1
  2. 1 秒后顯示更新的數字 2
  3. 1 秒后繼續使這兩個數字閃爍幾次。

理論上動畫的功能大致是這樣的

    func updateNumber1and2(number1: Int, number2:Int) {
    //first show number1
    self.number1 = number1
    Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { _ in
        //next show number 2
        self.number2 = number2

        Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { _ in
            //next blink text twice
            print("start blinking")
            self.numbersBlinking = true
            Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false) { _ in
                print("stop blinking")
                self.numbersBlinking = false
            }
        }
    }
}

但是我不知道何時或如何在 SwiftUI 組件中執行這樣的函數?

SwiftUI 有一個內置的動畫函數,它們是由變量的變化觸發的。 你不必自己滾動。 當然不是多個計時器。 如果您要使用計時器,則可以使用一個計時器,並根據該計時器跟蹤事情何時發生變化。 不建議使用多個定時器。

由於您列出的要求,此代碼比普通動畫稍微復雜一些,但您需要延遲。 如果您只是希望視圖在更改時閃爍,我會簡單地將animator包裝在withAnimation()塊中,它會在更改時立即閃爍。

struct AnimatedTextView: View {
    @State var number1: Int = 0
    @State var number2: Int = 0
    @State var animator = false
    
    var body: some View {
        VStack {
            Button { // Changed your timer to a button so that you don't have to wait for some random event
                self.number1 = self.number1 + 1
                // This delays the execution by 1 second.
                DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                    self.number2 = self.number2 + 1
                }
                // This delays the execution by 2 seconds.
                DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                    withAnimation(.easeInOut(duration: 0.2).repeatCount(10)) {
                        // Changing this to true causes the opacity of the view to go to 0.
                        // By putting it in an animated block, the change will be slowed and repeated 5 times
                        // On and off each separately count as a time
                        animator = true
                    }
                    // This delays the execution by 1 second after the animator variable is changed.
                    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                        // This gives time for the animation to complete and then sets the opacity back to 1
                        // Otherwise the view will be hidden. It is essentially a reset.
                        animator = false
                    }
                }
                
            } label: {
                Text("Change Numbers")
            }
            
            SampleNumbersView(numbers1: number1, numbers2: number2)
                // animator is used here to change the opacity, and it is animated repeatedly
                // causing the view to blink.
                .opacity(animator ? 0 : 1)
        }
    }
}

struct SampleNumbersView: View {
    // Since you are not changing the numbers in this view, declare them as let
    // and do not give them an initial value. There is less system overhead.
    let numbers1: Int
    let numbers2: Int
    
    var body: some View {
        VStack {
            Text("Number 1: \(numbers1)")
            Text("Number 2: \(numbers2)")
        }
    }
}

而且,為了回答您的原始問題,如果您使用的是 iOS 14 或更高版本,您將在其中一個視圖上使用.onChange(of:perform:)來捕獲更新並像這樣運行您的函數:

        .onChange(of: number1) { _ in
            updateNumber1and2(number1: number1, number2:number2)
        }

暫無
暫無

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

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