UICollisionBehavior и UIGravityBehavior не работают должным образом с DispatchQueue

В настоящее время я разрабатываю пользовательский интерфейс для инфраструктуры, которую мне дали, которая воспроизводит connect 4. Эта инфраструктура инкапсулирована в класс GameSession. Я не буду описывать тонкости его API и его работу. Я не верю, что это важно.

Я полагаю, что я неправильно понимаю очереди рассылки и неправильно их использую. Но я искал бесконечно и не нашел ничего, что указывало бы на решение моей проблемы.

Вот краткое объяснение того, что происходит. Метод, который контролирует ходы, это playGame(botStarts: Bool, atColumn: Int). В этом методе граница столкновения добавляется в правильной строке и столбце с помощью метода addBoundary(atRow: Int, atColumn: Int). Затем диск создается с помощью метода dropDisc(atColumn: Int, color: UIColor). Этот метод создает пользовательский UIView, добавляет его к представлению на экране и добавляет поведение при столкновении и гравитации. Он падает, пока не достигнет ранее добавленной границы.

В playGame() я сбрасываю диски на экран, используя DispatchMain.Queue.async{dropDisc()}. Но каждый раз, когда я вызываю playGame() во второй раз и далее, пользовательские диски рисуются в верхней части экрана, но они не падают. На первой итерации они рисуются и падают, как и ожидалось.

Ниже приведены функции, на которые я ссылался выше.

private func playGame(botStarts: Bool, dropPieceAt: Int) {
        DispatchQueue.global(qos: .userInitiated).async {
            if botStarts {
                if let move = gameSession.move {
                    let column = move.action % self.gameSession.boardLayout.columns
                    let row = move.action / self.gameSession.boardLayout.columns
                    self.addBoundary(atRow: row, atColumn: column)
                    DispatchQueue.main.async {
                        self.dropDisc(atColumn: column, color: move.color)
                    }
                }
            } else {
                let column = dropPieceAt
                if self.gameSession.userPlay(at: column) {
                    if let move = self.gameSession.move {
                        print(move)
                        let column = move.action % self.gameSession.boardLayout.columns
                        let row = move.action / self.gameSession.boardLayout.columns
                        self.addBoundary(atRow: row, atColumn: column)
                        DispatchQueue.main.async {
                            self.dropDisc(atColumn: column, color: move.color)
                        }
                    }
                    if let move = self.gameSession.move {
                        let column = move.action % self.gameSession.boardLayout.columns
                        let row = move.action / self.gameSession.boardLayout.columns
                        self.addBoundary(atRow: row, atColumn: column)
                        DispatchQueue.main.async {
                            self.dropDisc(atColumn: column, color: move.color)
                        }
                    }
                }
                if self.gameSession.done {
                    if let outcome = self.gameSession.outcome {
                        DispatchQueue.main.async {
                            self.gameLabel.text = outcome.message + "\n Winning pieces \(outcome.winningPieces)"
                        }
                    }
                }
            }
        }
    }


 private func dropDisc(atColumn: Int, color: UIColor) {
        var frame = CGRect()
        frame.origin = CGPoint.zero
        frame.size = Constants.bubbleSize
        let x = CGFloat(39) + CGFloat(47 * atColumn)
        frame.origin.x = x
        let bubbleView = DiscView(frame: frame, color: color)
        gameView.addSubview(bubbleView)
        collider.addItem(bubbleView)
        gravity.addItem(bubbleView)
    }



    // Adds a boundary using the row and column obtained from game session.
    private func addBoundary(atRow: Int, atColumn: Int) {
        let fromCoordX = CGFloat(16 + (boardView.initialX-boardView.radius)) + CGFloat(47 * atColumn)
        let toCoordX = fromCoordX + CGFloat(24)
        let coordY =  CGFloat(198.5 + (boardView.initialY+boardView.radius)) + CGFloat(45 * atRow)
        let fromPoint = CGPoint(x: fromCoordX, y: coordY+1)
        let toPoint = CGPoint(x: toCoordX, y: coordY+1)
        self.collider.addBoundary(withIdentifier: "boundary" as NSCopying, from: fromPoint, to: toPoint)
        self.drawLineFromPoint(start: fromPoint, toPoint: toPoint, ofColor: UIColor.red, inView: self.gameView)

    }

Вот скриншот моего экрана: https://imgur.com/7M3fklo.

В нижнем ряду вы можете увидеть диск пользователя (желтый) и диск ботов (красный). Они были добавлены при первом вызове playGame(). Но сверху вы можете увидеть два диска, которые были добавлены при втором вызове playGame(). Эти не падают.

Что бы я ни пробовал

Любая обратная связь высоко ценится!

1 ответ

Решение

Фоновая многопотоковость сложна и должна использоваться только тогда, когда она абсолютно необходима (потому что она навязана вам, или потому что у вас есть трудоемкое действие, и вы не хотите останавливать интерфейс, который принадлежит основному потоку). Это не кажется необходимым здесь. Вы не делаете ничего трудоемкого. И это похоже на использование DispatchQueue.global может сбить вас с толку; фоновая очередь отправки заставляет ваш код работать не по порядку, и, очевидно, вы не знали об этом.

Решение: просто избавьтесь от всех ваших DispatchQueue.global а также DispatchQueue.main код. (Другими словами, удалите эти строки и сопоставьте правильные линии фигурных скобок.) Тогда все будет просто выполняться в главной очереди, и нет никаких причин, почему это не должно происходить.

Другие вопросы по тегам