iOS Timer.publish 有 14 个奇怪的行为

iOS 14 weird behavior with Timer.publish

我已尽最大努力只提取了显示这种奇怪行为的较大项目的一小部分。目的是将一个随机数添加到数组中并每 3 秒显示一次。在 iOS 13 中,每个数字每 3 秒从左侧滑入一次,一切都按预期进行。我在 iOS 14 中看到的是每 3 秒添加 4 个数字。有谁知道为什么会这样?提前致谢!

import SwiftUI

struct ContentView: View {

    @State private var calledNumbers = CalledNumbers()
    @State private var timer = Timer.publish(every: 3, tolerance: 0.5, on: .main, in: .common).autoconnect()
    @State private var inProgress = false

    var body: some View {
        Button(action: {
            if !self.inProgress {
                self.calledNumbers.startOver()
                print("Start timer")
                self.timer = Timer.publish(every: 3, tolerance: 0.5, on: .main, in: .common).autoconnect()
            }
            else {
                print("Stop timer")
                self.timer.upstream.connect().cancel()
            }
            self.inProgress.toggle()
        })
        {
            if(self.inProgress == false) {
                Text("S T A R T")
                    .font(.system(size: 22))
                    .fontWeight(.heavy)
                    .frame(width: 200, height: 35, alignment: .center)
                    .background(Capsule()
                        .fill(Color.green))
                    .cornerRadius(35)
                    .foregroundColor(.white)
                    .padding(.bottom, 2)
            }
            else {
                Text("S T O P")
                    .font(.system(size: 22))
                    .fontWeight(.heavy)
                    .frame(width: 200, height: 35, alignment: .center)
                    .background(Color.red)
                    .foregroundColor(.white)
                    .cornerRadius(35)
                    .onReceive(self.timer) { _ in
                        self.timer.upstream.connect().cancel()
                        print("CALL NEXT NUMBER")
                        self.calledNumbers.callNextNumber()
                        self.timer = Timer.publish(every: 3, tolerance: 0.5, on: .main, in: .common).autoconnect()
                    }
            }
        }
        ZStack {
            RoundedRectangle(cornerRadius: 35)
                .frame(width: UIScreen.main.bounds.size.width - 19, height: 40, alignment: .center)
                .foregroundColor(.clear)
                .padding(.bottom, 2)
            HStack {
                ForEach(self.calledNumbers.calledNumberList.reversed().filter {self.checkCount(number: [=12=])}, id: \.self) { number in
                    Text("\(String(number))")
                        .font(.custom("Menlo", size: 20))
                        .fontWeight(.black)
                        .frame(width: 40, height: 40, alignment: .center)
                        .background(Color.red)
                        .clipShape(Circle())
                        .foregroundColor(.white)
                        .transition(AnyTransition.offset(x: (number == self.calledNumbers.calledNumberList.last) ? -250 : 250))
                        .animation(Animation.linear(duration: 1).repeatCount(1))
                }
            }
        }
    }
    
    func checkCount(number: Int) -> Bool {
        let count = self.calledNumbers.calledNumberList.count
        if (count <= 8) {
            return true
        }
        else {
            guard let index = self.calledNumbers.calledNumberList.firstIndex(of: number) else { return false }
            if (count - index > 8) { return false }
            else { return true }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

此文件由xcode 12 添加(我将此项目命名为 Test2):

import SwiftUI

@main
struct Test2App: App {
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Class 被叫号码

//
//  class.swift
//  Test2
//

import Foundation

class CalledNumbers {
    
    @Published var calledNumberList: [Int]
            
    init() {
        calledNumberList = [Int]()
    }
    
    func callNextNumber() {
        var tempNumber = Int.random(in: 1...75)
        
        while calledNumberList.contains(tempNumber) {
            tempNumber = Int.random(in: 1...75)
        }
        calledNumberList.append(tempNumber)
        print("Number added \(tempNumber)")
        
    }
    
    func startOver() {
        
        calledNumberList.removeAll()
        
    }
}

问题似乎是由 .onReceived() 附加到 Button 内的代码引起的。将 .onReceived() 移动到 Button 作为一个整体解决了这个问题。

此外,您对计时器的操作超出了必要。我从 .onReceive().

中删除了停止和重新启动计时器

calledNumbers 应该是 @ObservableObject.

struct ContentView: View {

    @ObservedObject var calledNumbers = CalledNumbers()
    @State private var timer = Timer.publish(every: 3, tolerance: 0.5, on: .main, in: .common).autoconnect()
    @State private var inProgress = false

    var body: some View {
        Button(action: {
            if !self.inProgress {
                self.calledNumbers.startOver()
                print("Start timer")
                self.timer = Timer.publish(every: 3, tolerance: 0.5, on: .main, in: .common).autoconnect()
            }
            else {
                print("Stop timer")
                self.timer.upstream.connect().cancel()
            }
            self.inProgress.toggle()
        })
        {
            Text(inProgress ? "STOP" : "START")
                .font(.system(size: 22))
                .fontWeight(.heavy)
                .frame(width: 200, height: 35, alignment: .center)
                .cornerRadius(35)
                .foregroundColor(.white)
                .background(Capsule().fill(inProgress ? Color.red : .green))
                .padding(.bottom, 2)
            }
        }
        .onReceive(self.timer) { _ in
            print("CALL NEXT NUMBER")
            self.calledNumbers.callNextNumber()
        }
        .onAppear {
            // Cancel the initial timer
            self.timer.upstream.connect().cancel()
        }

        ZStack {
            RoundedRectangle(cornerRadius: 35)
                .frame(width: UIScreen.main.bounds.size.width - 19, height: 40, alignment: .center)
                .foregroundColor(.clear)
                .padding(.bottom, 2)
            HStack {
                ForEach(self.calledNumbers.calledNumberList.reversed().filter {self.checkCount(number: [=10=])}, id: \.self) { number in
                    Text("\(String(number))")
                        .font(.custom("Menlo", size: 20))
                        .fontWeight(.black)
                        .frame(width: 40, height: 40, alignment: .center)
                        .background(Color.red)
                        .clipShape(Circle())
                        .foregroundColor(.white)
                        .transition(AnyTransition.offset(x: (number == self.calledNumbers.calledNumberList.last) ? -250 : 250))
                        .animation(Animation.linear(duration: 1).repeatCount(1))
                }
            }
        }
    }
    
    func checkCount(number: Int) -> Bool {
        let count = self.calledNumbers.calledNumberList.count
        if (count <= 8) {
            return true
        }
        else {
            guard let index = self.calledNumbers.calledNumberList.firstIndex(of: number) else { return false }
            if (count - index > 8) { return false }
            else { return true }
        }
    }
}

此外,您的 CalledNumbers class 应该是 ObservableObject 以便 Published 正常工作:

import Foundation

class CalledNumbers: ObservableObject {
    
    @Published var calledNumberList: [Int]
            
    init() {
        calledNumberList = [Int]()
    }
    
    func callNextNumber() {
        var tempNumber = Int.random(in: 1...75)
        
        while calledNumberList.contains(tempNumber) {
            tempNumber = Int.random(in: 1...75)
        }
        calledNumberList.append(tempNumber)
        print("Number added \(tempNumber)")
        
    }
    
    func startOver() {
        
        calledNumberList.removeAll()
        
    }
}