TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

Updating a view in a loop

Forums > SwiftUI

The project i'm working on for this is here: https://github.com/johncwelch/SOS-Swift/tree/main/SOS-Swift

(I'm not sure how to create a "small" version of this, so hopefully I can explain.

I have a game grid that is anywhere between 3x3 and 10x10. THat works. The idea is to spell "SOS" first. That works.

part of the project is to, when enabled, record and playback moves. The record was easy, if recording is enabled, which runs this function:

func addRecordedMove(myGameRecord: [moveRecord], myCommittedButtonIndex: Int, myGridArray: Game, myCurrentPlayer: String) -> [moveRecord] {
    var tempGameRecord = myGameRecord
    let theMove = myGridArray.gridCellArr[myCommittedButtonIndex]
    let theTempMoveRecord = moveRecord(moveIndex: theMove.index, moveTitle: theMove.title, movePlayer: myCurrentPlayer, moveColor: theMove.backCol)
    //print("theTempMoveRecord: \(theTempMoveRecord)")
    tempGameRecord.append(theTempMoveRecord)
    //print("tempGameRecord: \(tempGameRecord)")
    return tempGameRecord
}

the moveRecord array is an array of structs:

struct moveRecord {
    var moveIndex: Int
    var moveTitle: String
    var movePlayer: String
    var moveColor: Color
}

myCommitedButtonIndex is the index of the button that was clicked in the game, myGridArray is a Game Class object:

@Observable class Game {
    //this sets the size of the grid, passsed when creating the array
    var gridSize: Int {
        didSet {
            //builds the array based on the int passed in which is set in the picker
            //note that buildStructArray is an external func
            gridCellArr = buildStructArray(theGridSize: gridSize)
        }
    }

    //create the array var
    var gridCellArr: [Cell] = []

    //initialization
    init(gridSize: Int) {
        self.gridSize = gridSize
        self.gridCellArr = buildStructArray(theGridSize: gridSize)
    }
}

that class contains an array of Cell:

@Observable
class Cell: Identifiable {
    let id = UUID()
    var title: String = ""
    var buttonDisabled: Bool = false
    var index: Int = 0
    var xCoord: Int = 0
    var yCoord: Int = 0
    var backCol: Color = .gray
}

and myCurrentPlayer is just a String with either "Blue" or "Red"

once the game is finished, it can be played back, and this is where I'm having the problem. Here's the button action code:

Button {
                //call newGame() to clear all the buttons
                //doesn't actually create a new game, but we want this for playback
                newGame(myGridArray: theGame)
                //disable all the buttons so you can't click on them during/after playback
                for i in 0..<theGame.gridCellArr.count {
                    theGame.gridCellArr[i].buttonDisabled = true
                }
                for i in 0..<gameMoveRecord.count {
                    sleep(1)
                    print("Calling function")
                    currentPlayer = playbackGame(myGameRecord: gameMoveRecord, myGridArray: theGame, myLoopCount: i)
                }
            } label: {
                Text("Playback")
            }

playBackGame():

func playbackGame (myGameRecord: [moveRecord], myGridArray: Game, myLoopCount: Int) -> String {
    var theIndex: Int
    var theTitle: String
    var thePlayer: String
    var theColor: Color

    theIndex = myGameRecord[myLoopCount].moveIndex
    theTitle = myGameRecord[myLoopCount].moveTitle
    thePlayer = myGameRecord[myLoopCount].movePlayer
    theColor = myGameRecord[myLoopCount].moveColor

    myGridArray.gridCellArr[theIndex].title = theTitle
    myGridArray.gridCellArr[theIndex].backCol = theColor

    return thePlayer
}

I think I know what the problem is, mainly that because of how swiftui works, the playback code isn't updating the button view until the button is "finished", i.e. the loop has completely run. If I comment out the sleep function, the result is the same, just much faster, i.e. nothing happens until the entire for loop has run.

I've tried wrapping the call to currentPlayer() in DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) and there's no difference.

how do I get the view to update after each run?

(I would add a screenshot of the game, but linking to an image is now literally awful.)

3      

Someone said that part of my issue was sleep(1) and that I should use a timer instead, but that creates another set of problems.

So let's say I have my struct:

struct moveStruct {
    var moveIndex: Int
    var moveTitle: String
    var movePlayer: String
    var moveColor: Color
}

and I have my main ContentView with some state vars:

@State var theCounter: Int = 0
@State var theMoveArray = [moveStruct]()
@State var theArrayCounter = 0

and we have our body code doing stuff:

var body: some View {
        HStack {
            var theMove = moveStruct(moveIndex: 0, moveTitle: "S", movePlayer: "Blue", moveColor: .blue)
            var theMove2 = moveStruct(moveIndex: 1, moveTitle: "O", movePlayer: "Red", moveColor: .red)

      Button {
                theMoveArray.append(theMove)
                theMoveArray.append(theMove2)
                theCounter = 0

        var theArrayBounds = theMoveArray.count - 1
        print("\(theMoveArray.count)") \\print1

        Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
                    print("\(theMoveArray.count)") \\print2
                    if theCounter <= (theArrayBounds) {
            print("Fired")
          } else {
                  timer.invalidate()
         }
         theCounter += 1
        }
       } label: {
                Text("The Button")
                    .frame(width: 100, height: 100)
            }
    }
  }

for print1, theMoveArray.count is 2 as expected, but for print2, theMoveArray.count is now 0, and I have NO idea what is causing that

3      

Hacking with Swift is sponsored by Blaze.

SPONSORED Still waiting on your CI build? Speed it up ~3x with Blaze - change one line, pay less, keep your existing GitHub workflows. First 25 HWS readers to use code HACKING at checkout get 50% off the first year. Try it now for free!

Reserve your spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.