one or the other works but not both
if the timers have the same interval it works
struct ContentView: View {
#State var isPlaying: Bool = true
#State var timeRemaining: Int = 1800
#State var count = 1
#State var count2 = 10
var body: some View {
let timer2 = Timer.publish(every: 1, on: .main, in: .default).autoconnect()
let timer = Timer.publish(every: 0.5, on: .main, in: .default).autoconnect()
VStack {
Text("\(count) \(count2)")
.padding()
}
.onReceive(timer2) { _ in
if self.timeRemaining > 0 && isPlaying == true {
self.count2 += 1
}
}
.onReceive(timer) { _ in
if self.timeRemaining > 0 && isPlaying == true {
self.count += 1
}
}
}
}
can anyone suggest any changes to my code?
Move the timers out of the body and define them as properties of the View:
struct ContentView: View {
#State var isPlaying: Bool = true
#State var timeRemaining: Int = 1800
#State var count = 1
#State var count2 = 10
let timer2 = Timer.publish(every: 1, on: .main, in: .default).autoconnect()
let timer = Timer.publish(every: 0.5, on: .main, in: .default).autoconnect()
var body: some View {
VStack {
Text("\(count) \(count2)")
.padding()
}
.onReceive(timer2) { _ in
if self.timeRemaining > 0 && isPlaying == true {
self.count2 += 1
}
}
.onReceive(timer) { _ in
if self.timeRemaining > 0 && isPlaying == true {
self.count += 1
}
}
}
}
This gives me some very unusual results
struct ContentView: View {
#State var isPlaying: Bool = true
#State var timeRemaining: Int = 1800
#State var count = 1
#State var count2 = 10
#State var rate = 0.5
let timer = Timer.publish(every: 0.5, on: .main, in: .default).autoconnect()
var body: some View {
let timer2 = Timer.publish(every: rate, on: .main, in: .default).autoconnect()
VStack {
Text("\(count) \(count2)")
.padding()
}
.onReceive(timer) { _ in
if self.timeRemaining > 0 && isPlaying == true {
self.count += 1
}
}
.onReceive(timer2) { _ in
if self.timeRemaining > 0 && isPlaying == true {
self.count2 += 1
}
}
}
}
despite the intervals being the same, the counters increase at different rates, even randomly
Related
I have a demo SwiftUI for selected two color & value as below, I want to collect the color value when user select different color.
But they're always linked same value and I cannot separate the two 'Stepper' and got them into an Array.
import SwiftUI
struct StepColorChange: View {
#State private var value = 0
let colors: [Color] = [.orange, .red, .gray, .blue, .green, .purple, .pink]
func incrementStep() {
value += 1
if value >= colors.count { value = 0}
}
func decrementStep() {
value -= 1
if value < 0 {
value = colors.count - 1}
}
var body: some View {
// change color with value stepper
VStack {
Stepper {
Text("Value: \(value) Color: \(colors[value].description)")
} onIncrement: {
incrementStep()
} onDecrement: {
decrementStep()
}
.padding(5)
.background(colors[value])
Stepper {
Text("Value: \(value) Color: \(colors[value].description)")
} onIncrement: {
incrementStep()
} onDecrement: {
decrementStep()
}
.padding(5)
.background(colors[value])
}
}
}
Thanks a lot!
This is not rocket science here. Try something like this, using two values,
to change the stepper background color independently according each stepper value.
struct ContentView: View {
var body: some View {
StepColorChange()
}
}
struct StepColorChange: View {
#State private var value1 = 0
#State private var value2 = 0
let colors: [Color] = [.orange, .red, .gray, .blue, .green, .purple, .pink]
var body: some View {
VStack {
Stepper {
Text("Value: \(value1) Color: \(colors[value1].description)")
} onIncrement: {
value1 += 1
if value1 >= colors.count { value1 = 0}
} onDecrement: {
value1 -= 1
if value1 < 0 { value1 = colors.count - 1 }
}
.padding(5)
.background(colors[value1])
Stepper {
Text("Value: \(value2) Color: \(colors[value2].description)")
} onIncrement: {
value2 += 1
if value2 >= colors.count { value2 = 0}
} onDecrement: {
value2 -= 1
if value2 < 0 { value2 = colors.count - 1 }
}
.padding(5)
.background(colors[value2])
}
}
}
EDIT-1: condensed code
struct StepColorChange: View {
#State private var values = [0,0]
let colors: [Color] = [.orange, .red, .gray, .blue, .green, .purple, .pink]
func stepper(_ ndx: Int) -> some View {
Stepper {
Text("Value: \(values[ndx]) Color: \(colors[values[ndx]].description)")
} onIncrement: {
values[ndx] += 1
if values[ndx] >= colors.count { values[ndx] = 0}
} onDecrement: {
values[ndx] -= 1
if values[ndx] < 0 { values[ndx] = colors.count - 1}
}
.padding(5)
.background(colors[values[ndx]])
}
var body: some View {
VStack {
stepper(0)
stepper(1)
Text("stepper(0): \(values[0])")
Text("stepper(1): \(values[1])")
Text("color stepper(0): \(colors[values[0]].description)")
Text("color stepper(1): \(colors[values[1]].description)")
}
}
}
I've the following code:
VStack (spacing: 0) {
ForEach (0..<myMaze.height, id: \.self) { (y: Int) in
HStack (spacing: 0) {
ForEach (0..<myMaze.width, id: \.self) { (x: Int) in
DrawRoom(xPos: x, yPos: y)
} // ForEach x
} // HSTack
} // ForEach y
} // VSTack
and
struct DrawRoom: View {
#ObservedObject var myMaze = Maze.sharedInstance
var xPos: Int
var yPos: Int
var body: some View {
Square(value: myMaze.value)
.stroke(Color.gray, style: StrokeStyle(lineWidth: 3))
.frame(width: myMaze.sideLength, height: myMaze.sideLength)
}
init(xPos: Int, yPos: Int) {
self.xPos = xPos
self.yPos = yPos
myMaze.value = myMaze.mazeData[xPos][yPos]
print("DrawRoom: [\(xPos), \(yPos)]")
}
}
... with the following output in the console:
X, Y
DrawRoom: [0, 2]
DrawRoom: [1, 2]
DrawRoom: [2, 2]
DrawRoom: [0, 0]
DrawRoom: [1, 0]
DrawRoom: [2, 0]
DrawRoom: [0, 1]
DrawRoom: [1, 1]
DrawRoom: [2, 1]
Surprisingly the "Y" starts at 2 then 0 and finally 1 but it should be as follows:
X, Y
DrawRoom: [0, 0]
DrawRoom: [1, 0]
DrawRoom: [2, 0]
DrawRoom: [0, 1]
DrawRoom: [1, 1]
DrawRoom: [2, 1]
DrawRoom: [0, 2]
DrawRoom: [1, 2]
DrawRoom: [2, 2]
Can somebody explain me why, is there something wrong in my coding?
For info, "Square" draws lines according to the value of myMaze.mazeData[xPos][yPos]
See below the whole code:
//
// Maze.swift
// NewMaze
//
// Created by Philippe Lagarrigue on 26/12/2020.
//
import Foundation
import SwiftUI
let side = ["North", "East", "South", "West"]
let direction = [1, 2, 4, 8]
let wall = (North: 1, East: 2, South: 4, West: 8) // 15 means all walls
let exit = (toNorth: 16, toEast: 32, toSouth: 64, toWest: 128) // 0 means no exit
struct Room {
var x: Int
var y: Int
var roomsToExit: Int
}
class Maze: ObservableObject {
// Properties
static let sharedInstance = Maze()
var viewWidth = CGFloat(0.0) //{ didSet { update(flag: 2) } }
var viewHeight = CGFloat(0.0) //{ didSet { update(flag: 2) } }
var ready2Draw = false
var width = 0 // Number of rooms (horizontally)
var height = 0 // Number of rooms (vertically)
#Published var widthDouble = CGFloat(3.0) { didSet { ready2Draw = false } }
#Published var heightDouble = CGFloat(3.0) { didSet { ready2Draw = false } }
var oneWay = true
var exitRoom = Room(x: 0, y: 0, roomsToExit: 0)
var farestRoom = Room(x: 0, y: 0, roomsToExit: 0)
var currentPos = Room(x: 0, y: 0, roomsToExit: 0)
var mazeData = [[Int]]()
//var width: Int { return Int(widthDouble) } // Number of rooms (horizontally)
//var height: Int { return Int(heightDouble) } // Number of rooms (vertically)
var numberOfRooms: Int { return self.width * self.height }
var sideLength = CGFloat(40.0) //{ didSet { print("sideLength: \(sideLength)") } }
var value = 15 // { didSet { print("value: \(value)") } }
func defineExit() {
let choosenSide = Int.random(in: 0...3) // N, E, S or W
// Fill array with 15 which means that all walls are set
mazeData = Array(repeating: Array(repeating: 15, count: self.height), count: self.width)
//var count = 0
//for x in 0..<width {
// for y in 0..<height {
// print(x, y, count, count/height, count%height)
// mazeData[x][y] = 15
// count += 1
// }
//}
print("\nchoosenSide: \(side[choosenSide]) (\(choosenSide))")
if (choosenSide + 1) % 2 == 0 {
print("Axe: E-W")
exitRoom.x = choosenSide == 3 ? 0 : self.width - 1
exitRoom.y = Int.random(in: 1..<self.height)
} else {
print("Axe: N-S")
exitRoom.x = Int.random(in: 1..<self.width)
exitRoom.y = choosenSide == 2 ? self.height - 1 : 0
}
currentPos.x = exitRoom.x
currentPos.y = exitRoom.y
currentPos.roomsToExit = 1
//print("The exit room is at \(exitRoom.x), \(exitRoom.y)")
mazeData[currentPos.x][currentPos.y] = 15 - direction[choosenSide]
print("The exit room is at [\(exitRoom.x), \(exitRoom.y)] => \(mazeData[currentPos.x][currentPos.y])")
}
init() {
//buildMaze()
}
func update(flag: Int) {
if flag & 1 == 1 {
width = Int(widthDouble)
height = Int(heightDouble)
}
if flag & 2 == 2 {
let viewW = viewWidth/widthDouble
let viewH = viewHeight/heightDouble
let side = (viewW < viewH ? viewW : viewH) - 1.0
self.sideLength = side - side * 0.1
print("Maze init:", width, "x", height, "=", numberOfRooms)
}
ready2Draw = false
}
func dummy() {
let wd = widthDouble
let hd = heightDouble
widthDouble = 3
heightDouble = 3
update(flag: 3)
defineExit()
widthDouble = wd
heightDouble = hd
}
func buildMaze(){
dummy() // This is the only way I found to get the maze displayed correctly
update(flag: 3)
defineExit()
//var rooms = numberOfRooms
//while (rooms > 0) {
// newRoom()
// rooms -= 1
//}
ready2Draw = true
}
func newRoom() {
}
}
//
// ContentView.swift
// NewMaze
//
// Created by Philippe Lagarrigue on 26/12/2020.
//
import SwiftUI
import Combine // << needed for Just publisher below
struct ContentView: View {
#ObservedObject var myMaze: Maze = .init()
#State private var selectedView = 0
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
TabView(selection: $selectedView) {
NavigationView() .tabItem { Image("navigation"); Text("Navigation") }.tag(0)
.onReceive(timer) { input in selectedView = 1; self.timer.upstream.connect().cancel() }
SettingsView() .tabItem { Image("settings"); Text("Settings") }.tag(1)
MapView() .tabItem { Image("map"); Text("Map") }.tag(2)
}
//.onReceive(Just(selectedView)) { print($0) }
//.onLongPressGesture { myMaze.ready2Draw = true }
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct NavigationView : View {
#ObservedObject var myMaze = Maze.sharedInstance
var body : some View {
GeometryReader { geometry in
Path { path in
myMaze.viewWidth = geometry.size.width
myMaze.viewHeight = geometry.size.width
}
}
}
}
struct Square: Shape {
//#ObservedObject var myMaze = Maze.sharedInstance
var value: Int
func path(in rect: CGRect) -> Path {
var path = Path()
if (value & wall.North == wall.North) {
path.move(to: CGPoint(x: rect.minX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
}
if (value & wall.East == wall.East) {
path.move(to: CGPoint(x: rect.minX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
}
if (value & wall.South == wall.South) {
path.move(to: CGPoint(x: rect.maxX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
}
if (value & wall.West == wall.West) {
path.move(to: CGPoint(x: rect.maxX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.minX, y: rect.minY))
}
return path
}
init(value: Int) {
self.value = value
//if value != 15 {
// print("Square: \(value)")
//}
}
}
struct DrawRoom: View {
#ObservedObject var myMaze = Maze.sharedInstance
var xPos: Int
var yPos: Int
let numbers = false
var body: some View {
if numbers {
Text("\(myMaze.value, specifier: "%.02d")")
.font(.system(size: 18, design: .monospaced))
.foregroundColor(myMaze.value == 15 ? Color.black : Color.red)
} else {
Square(value: myMaze.value)
.stroke(Color.gray, style: StrokeStyle(lineWidth: 3))
.frame(width: myMaze.sideLength, height: myMaze.sideLength)
}
}
init(xPos: Int, yPos: Int) {
self.xPos = xPos
self.yPos = yPos
myMaze.value = myMaze.mazeData[xPos][yPos]
print("DrawRoom: [\(xPos), \(yPos)]")
}
}
struct MapView : View {
#ObservedObject var myMaze = Maze.sharedInstance
var body : some View {
if myMaze.ready2Draw {
VStack (spacing: 0) {
ForEach (0..<myMaze.height, id: \.self) { (y: Int) in
HStack (spacing: 0) {
ForEach (0..<myMaze.width, id: \.self) { (x: Int) in
DrawRoom(xPos: x, yPos: y)
} // ForEach x
} // HSTack
} // ForEach y
} // VSTack
} else {
Button(action: { action() }) { Text("Build maze") }
.padding()
.background(Color.red)
.cornerRadius(20)
.foregroundColor(.white)
.font(.title)
.shadow(color: .gray, radius: 20.0, x: 10, y: 10)
}
} // body
func action() {
myMaze.buildMaze()
}
}
struct SettingsView : View {
#ObservedObject var myMaze = Maze.sharedInstance
#State private var isEditing1 = false
#State private var isEditing2 = false
var body : some View {
HStack(alignment: VerticalAlignment.top) {
VStack(alignment: .leading) {
Text("Settings\n") .font(.title)
Text("Number of rooms (horizontally): \(myMaze.widthDouble, specifier: "%.0f")")
.foregroundColor(isEditing1 ? .red : .black)
Slider(value: $myMaze.widthDouble, in: 3...32, step: 1,
onEditingChanged: { editing in isEditing1 = editing })
Text("\nNumber of rooms (vertically): \(myMaze.heightDouble, specifier: "%.0f")")
.foregroundColor(isEditing2 ? .red : .black)
Slider(value: $myMaze.heightDouble, in: 3...32, step: 1,
onEditingChanged: { editing in isEditing2 = editing })
Text("\nIn a maze with only one way, you can find the exit by touching the wall or hedge with the hand nearest to it, left or right. Keep that same hand touching the wall and keep walking. This may take you on a horribly long route, but it will get you out.")
Toggle("One way", isOn: $myMaze.oneWay)
Spacer()
Button(action: { action() }) { Text("Build maze") }
.padding()
.background(Color.red)
.cornerRadius(20)
.foregroundColor(.white)
.font(.title)
.shadow(color: .gray, radius: 20.0, x: 10, y: 10)
Spacer()
} .padding()
}
}
func action() {
myMaze.buildMaze()
}
}
I'm new in swiftUI developing, and I have a little problem. I made a function using Timer to create a count up timer. I want to display the time updating in a TextField, but it doesn't work (with a Text it works). Here is the code:
import SwiftUI
struct ContentView: View {
#State var min: Int = 0
#State var sec: Int = 0
#State var time = ""
#State var timer: Timer? = nil
var body: some View {
NavigationView{
VStack{
TextField("Tempo (min o mm:ss)", text: $time)
HStack{
Button(action: {
self.start()
self.time = String(self.min)+":"+String(self.sec)
}){
Text("Start")
}
Button(action: {
self.stop()
}){
Text("Stop")
}
}
}//VStack
}//NavigationView
}//body
func start(){
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true){
temp in
self.sec = self.sec + 1
if(self.sec == 59){
self.sec = 0
self.min = self.min + 1
}
}
}
func stop(){
timer?.invalidate()
timer = nil
}
}//contetView
You don't see the change because you're not updating the time String in your timer loop.
Here's the fix:
func updateTimeString() {
self.time = String(format: "%02d:%02d", self.min, self.sec)
}
func start(){
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { temp in
self.sec = self.sec + 1
if self.sec == 59 {
self.sec = 0
self.min = self.min + 1
}
updateTimeString()
}
}
Notes:
I moved the code to set the time string to a separate function. You can then call this from your Button as well.
I used String(format:) to add formatting to your string to show 2 digits for the minutes and seconds.
I've been working on a truth or dare app, in which you spin a wheel and you randomly get a truth or dare, and I want to have a bunch of random truth questions and dares saved in a database.
I have it set up so when you press the spin button it randomly chooses a truth or dare out of an array.
i've tried it a little and couldn't seem to get it to work. so my question is would it be possible to do something like,
if self.number[0] == 0 || 2 || 3{ (this is where it would get the random truth)}
else{(this is where it would get the random dare)}
I don't know if it will help, but this is the rest of my code.
import SwiftUI
import CoreHaptics
class Colors{
static var drinkGradient = [LinearGradient(gradient: Gradient(colors: [Color("drinkcard"), Color("drinkcard2"), Color("drinkcard")]), startPoint: .top, endPoint: .bottom)]
static var truthGradient = [LinearGradient(gradient: Gradient(colors: [Color("truthcard"), Color("truthcard"), Color("truthcard")]), startPoint: .top, endPoint: .bottom)]
static var dareGradient = [LinearGradient(gradient: Gradient(colors: [Color("darecard"), Color("darecard"), Color("darecard")]), startPoint: .top, endPoint: .bottom)]
}
struct ContentView: View {
#State private var timer:Timer!
#State private var text = [String("Truth"), String("Dare"), String("Truth"), String("Dare"), String("Truth"), String("Dare"), String("Drink!!")]
#State private var foregrounds = [Color.white, Color.white, Color.white, Color.white, Color.white, Color.white, Color.black]
#State private var number = [0]
#State private var timeKeeper: Float = 0
#State private var angle: Double = 0
let generator = UINotificationFeedbackGenerator()
#State var backgrounds = [Colors.truthGradient[0], Colors.dareGradient[0], Colors.truthGradient[0], Colors.dareGradient[0], Colors.truthGradient[0], Colors.dareGradient[0], Colors.drinkGradient[0]]
var body: some View {
ZStack{
Rectangle()
.foregroundColor(Color("background"))
.edgesIgnoringSafeArea(.all)
VStack{
HStack {
Text("Truth, Dare or Drink")
.shadow(radius: 10)
.padding(.top, 20)
.foregroundColor(.white)
.font(.system(.largeTitle, design: .rounded) )
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 100)
.background(Color("banner"))
.edgesIgnoringSafeArea(.top)
.shadow(radius: 15)
}
Text("This is where the Truth or dare will go.")
.frame(width: 350, height: 200)
.foregroundColor(.white)
.font(.system(.headline, design: .rounded))
Spacer()
Text(text[number[0]])
.font(.system(.largeTitle, design: .rounded))
.foregroundColor(foregrounds[number[0]])
.frame(width: 250, height: 250)
.background(backgrounds[number[0]])
.clipShape(RoundedRectangle(cornerRadius: 15, style: .continuous))
.shadow(radius: 10)
.rotationEffect(.degrees(angle))
.animation(.easeIn)
Spacer()
Button(action: {
self.timer = Timer.scheduledTimer(withTimeInterval: 0.10, repeats: true, block: {_ in
self.timeKeeper += 0.10
if self.timeKeeper < 3{
if self.number[0] == self.text.count - 1{
self.number[0] = 0
self.angle += 360
let impactHeavy = UIImpactFeedbackGenerator(style: .heavy)
impactHeavy.impactOccurred()
}
else{
self.number[0] += 1
self.angle += 360
let impactHeavy = UIImpactFeedbackGenerator(style: .heavy)
impactHeavy.impactOccurred()
}
}
else{
self.number[0] = Int.random(in:
0...self.text.count - 1)
let impactHeavy = UIImpactFeedbackGenerator(style: .heavy)
impactHeavy.impactOccurred()
self.angle += 360
self.timeKeeper = 0
self.timer.invalidate()
}
})
}) {
Text("Spin")
.font(.system(.title, design: .rounded))
.foregroundColor(.white)
.frame(width: 250, height: 50)
.background(Color("button"))
.clipShape(RoundedRectangle(cornerRadius: 15, style: .continuous))
.shadow(radius: 15)
}
Spacer()
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
if (self.number[0] == 0 || self.number[0] == 2 || self.number[0] == 3) {
print("truth")
} else {
print("dare")
}
I am using a timer in a SwiftUI view as in code below. It works as expected BUT under some conditions I want to cancel/stop that timer. There seems to be no ".cancel" property or method on the timer var. How do I cancel this timer? Any ideas/tips?
import SwiftUI
struct ContentView: View {
#State private var selection = 2
#State private var rotation: Double = GBstrtest
let timer = Timer.publish (every: 0.8, on: .current, in: .common).autoconnect()
var body: some View {
TabView(selection: $selection){
Text("Settings")
.font(.title)
.tabItem {
VStack {
Image(systemName: "gear")
.font(Font.system(.title ))
Text("Settings")
}
}
.tag(0)
VStack {
Divider().padding(2)
ZStack {
Image("image1")
.resizable()
.aspectRatio(contentMode: .fit)
Image("image2")
.resizable()
.aspectRatio(contentMode:.fit)
.rotationEffect(.degrees(rotation))
.animation(.easeInOut(duration: 0.3) )
.padding(EdgeInsets(top: 0, leading: 50, bottom: 0, trailing: 50))
}
Spacer()
}
.tabItem {
VStack {
Image(systemName: "speedometer")
.font(Font.system(.title ))
Text("Read Meter")
}
}
.tag(1)
}
.onReceive(timer) {
_ in self.rotation = Double.random(in: 0 ... 200)
// How do I cancel timer HERE!?
}
}
}
Inside your conditional statement, use the following code:
self.timer.upstream.connect().cancel()
Full cycle goes like this:
struct MySwiftUIView : View {
...
#State var timer = Timer.publish (every: 1, on: .current, in: .common).autoconnect()
#State var timeRemaining = -1
var body: some View {
ZStack {
// Underlying shapes etc as needed
Text("\(timeRemaining)").
.opacity(timeRemaining > 0 ? 1 : 0)
.onReceive(timer) { _ in
if self.timeRemaining < 0 {
// We don't need it when we start off
self.timer.upstream.connect().cancel()
return
}
if self.timeRemaining > 0 {
self.timeRemaining -= 1
} else {
self.timer.upstream.connect().cancel()
// Do the action on end of timer. Text would have been hidden by now
}
}
.onTapGesture { // Or any other event handler that should start countdown
self.timeRemaining = Int(delayValue)
self.timer = Timer.publish (every: 1, on: .current, in:
.common).autoconnect()
}
}
Voila! A reusable timer, use it as many times as you'd like!