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
код. (Другими словами, удалите эти строки и сопоставьте правильные линии фигурных скобок.) Тогда все будет просто выполняться в главной очереди, и нет никаких причин, почему это не должно происходить.