I am very new to SwiftUI. I am trying to create an app for my daughter to help her with learning how to add. I am doing this by using a number line. The number line is created using a horizontal Divider
as my base and then vertical Divider
s to mark each number on the number line.
For example, if she needs to add 4 + 8, the app will show her how you start at 4 and count up 8 until you get to 12. In the app this is accomplished through a ForEach
loop which starts at 4 and runs until 12. Each time the loop iterates, it produces the vertical divider plus a text field making the number.
Here is what the end product looks like:
This is my code:
import SwiftUI
struct GenerateNumberLineView: View {
var firstNumber: Int
var secondNumber: Int
var counter: Int
var opacityTest: Double
var body: some View {
ZStack{
Divider()
.frame(width:300, height: 4)
.overlay(.pink)
ForEach(firstNumber..<(secondNumber+firstNumber+1), id: \.self) { number in
var count = 0
let offSetter = -150 + (number-firstNumber) * 30
VStack{
Divider().frame(width:4, height: 30,
alignment: .leading)
.overlay(.pink).offset(x:CGFloat(offSetter))
Text(String("\(number)")).offset(x:CGFloat(offSetter))
}
}
}
}
}
#Preview {
GenerateNumberLineView(firstNumber: 4, secondNumber: 8,counter: 0,opacityTest: 0.0)
}
var updateTimer: Timer {
Timer.scheduledTimer(withTimeInterval: 1, repeats: true,
block: {_ in
let maximumn = num2 + numb
if self.counter == maximumn - numb {
self.updateTimer.invalidate()
} else {
self.counter += 1
}
})
}
The problem is the whole number line shows up at one time and I would like to delay each iteration for a second, so that it looks like the app is doing the counting up to 12 from 4. I tried wrapping the code in the loop in the following dispatch code:
let seconds = 0.25
DispatchQueue.main.asyncAfter(deadline: .now() + seconds)
{...code inside loop}
... but got this error:
No exact matches in reference to static method 'buildExpression' <<errors
My most successful attempt was to call the struct in another view and then implement a timer:
GenerateNumberLineView(firstNumber : minNum, secondNumber: maxNum, counter:counter,opacityTest: opacityTest).onAppear(perform: { let _ = self.updateTimer })
This works 76% of the way. The problem is it only displays one number on the screen. It shows 4 and then 4 disappears then it shows 5.
I want it to show 4. 4 stays on the screen it pauses for a beat then shows 5 pause then shows 6 and so on until 12. So the number line looks like the picture I attached but the process of getting to there was displayed to the user.
Put your timer, as a publisher, in the SwiftUI view. Increment a @State
(let's call this currentNumber
) each time the timer fires, until it reaches secondNumber
.
Change the range for the ForEach
to firstNumber..<(firstNumber + currentNumber + 1)
, and the numbers now appear one after another.
You can add transition
and animation
modifiers to add an animation when a new number appears.
let firstNumber: Int
let secondNumber: Int
@State var currentNumber = 0
let timer = Timer.publish (every: 1, on: .current, in: .common).autoconnect()
var body: some View {
ZStack{
Rectangle()
.frame(width:300, height: 4)
.overlay(.pink)
ForEach(firstNumber..<(firstNumber + currentNumber + 1), id: \.self) { number in
let offSetter = -150 + (number-firstNumber) * 30
VStack{
Rectangle()
.frame(width:4, height: 30, alignment: .leading)
.overlay(.pink)
Text("\(number)")
}
.offset(x:CGFloat(offSetter))
.transition(.opacity)
}
}
.onReceive(timer) { _ in
currentNumber += 1
if currentNumber == secondNumber {
timer.upstream.connect().cancel()
}
}
.animation(.default, value: currentNumber)
}