【问题标题】:UICollisionBehavior and UIGravityBehavior not working as expected with DispatchQueueUICollisionBehavior 和 UIGravityBehavior 无法与 DispatchQueue 一起按预期工作
【发布时间】:2019-09-04 19:03:34
【问题描述】:

我目前正在为给我的一个框架设计一个 UI,它可以播放 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() 时添加的两张光盘。这些不会掉落。

不管我尝试过什么

非常感谢任何反馈!

【问题讨论】:

  • 你为什么要在这里使用调度队列?为什么不直接删除所有调度队列调用并在主线程上做所有事情呢?这样做是否有一些问题?如果这样,那是什么?如果你这样做,这个特殊问题(有机会)会消失吗?
  • 这确实解决了我的问题。 playGame() 是我提供的一种方法,它最初将它们用于以下目的: DispatchQueue.main.async { self.gameLabel.text = printBoard("AlphaC4", [move.action], move.color == UIColor.红色?“X”:“O”)}。 printBoard() 是一个闭包,它返回一个代表棋盘状态的字符串。如您所见,视图中标签的文本被设置为代表板。这些 UI 更新必须在调度队列中而不是在主线程中发生,有什么理由吗?如果我删除它们,更新只会在最后发生。
  • “有什么理由让这些 UI 更新必须发生在调度队列中而不是主线程中?”我不确定你在问什么。所有 UI 更新必须 发生在主线程上。问题是你为什么要做一些如此小的事情,比如在一个后台线程。在我看来,您只是将自己的调度队列弄糊涂了;它们会导致代码无序运行,而这正是您所抱怨的。
  • 那么删除所有 DispatchQueue 内容是否正确?如果是的话,我只是给出它作为答案。如果它导致了新问题,也许您应该删除或编辑问题以询问该问题。
  • 是的,马特。这是正确的答案。谢谢。

标签: ios swift dispatch-queue uicollisionbehavior uigravitybehavior


【解决方案1】:

后台线程很难,只有在绝对必要时才应该使用(因为它是强加给您的,或者因为您有一个耗时的活动要执行并且您不想冻结界面,该界面由主线程)。这里似乎没有必要。你没有做任何耗时的事情。看起来DispatchQueue.global 的使用可能会让您感到困惑;后台调度队列会导致您的代码乱序运行,而您显然没有意识到这一点。

解决方案:只需删除所有 DispatchQueue.globalDispatchQueue.main 代码。 (换句话说,删除那些行并匹配右花括号行。)然后一切都将在主队列上运行,没有理由不这样做。

【讨论】:

猜你喜欢
  • 2012-02-19
  • 2019-12-14
  • 1970-01-01
  • 2021-06-01
  • 2021-09-06
  • 2016-05-15
  • 2022-01-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多