I did my best to pull out only a small part of my larger project that displays this odd behavior. The intention is for one random number to be added to the array and displayed every 3 seconds. In iOS 13 each number slides in from the left every 3 seconds and everything works as expected. What I see in iOS 14 is that 4 numbers are added every 3 seconds. Does anyone understand why this would be happening? Thanks in advance!
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: $0)}, 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()
}
}
This file was added by xcode 12 (I named this project Test2):
import SwiftUI
@main
struct Test2App: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Class for calledNumbers
//
// 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()
}
}
The problem seems to be caused by having the
.onReceived()
attached to the code inside of theButton
. Moving.onReceived()
to theButton
as a whole solves the issue.Also, you were doing more timer manipulation than is necessary. I removed stopping and restarting the timer from
.onReceive()
.calledNumbers
should be an@ObservableObject
.Also, your
CalledNumbers
class should be anObservableObject
so thatPublished
works correctly: