Swiftui iOS-Swift 5-循环内标签文本未更新

Swiftui iOS-Swift 5-循环内标签文本未更新,swiftui,label,grand-central-dispatch,scenekit,Swiftui,Label,Grand Central Dispatch,Scenekit,我正在使用Scenekit,当我点击屏幕时,我试图从循环中更新UI上的标签文本属性。我已验证标签已连接到情节提要中,并且可以从ViewDidLoad更新标签。我还验证了循环中的文本值是否存在,因为我可以看到它在控制台中打印。手势识别器的完整代码如下所示,您可以在底部看到,我已经尝试了GCD,但没有成功。谢谢你的帮助 您没有解释问题是什么,但我猜什么也没有发生,然后标签会(最终)更新到最后一个值 这是因为sceneTapped是一个手势识别器,这意味着它在主线程上运行。(所有UI代码和事件处理都

我正在使用Scenekit,当我点击屏幕时,我试图从循环中更新UI上的标签文本属性。我已验证标签已连接到情节提要中,并且可以从ViewDidLoad更新标签。我还验证了循环中的文本值是否存在,因为我可以看到它在控制台中打印。手势识别器的完整代码如下所示,您可以在底部看到,我已经尝试了GCD,但没有成功。谢谢你的帮助


您没有解释问题是什么,但我猜什么也没有发生,然后标签会(最终)更新到最后一个值

这是因为
sceneTapped
是一个手势识别器,这意味着它在主线程上运行。(所有UI代码和事件处理都在主线程上运行。)

因此,
sceneTapped
在主线程上以循环的方式旋转,排队等待块在主线程上运行。但是主线程正忙于运行
sceneTapped
,因此这些块都不会执行

最后,
sceneTapped
返回,所有排队的块一个接一个地突然执行

如果这个
for
循环是一个长时间运行的循环,那么它需要在自己的线程中运行。通常,您永远不希望有任何代码在主线程上运行时需要花费大量时间来执行。任何阻止主线程的代码都会冻结UI和所有用户交互

所以基本上你应该有这样的东西:

@objc func sceneTapped(recognizer: UITapGestureRecognizer) {
    // ...
    DispatchQueue.global().async {
        // execute long running code in a background thread
        for i in 0...geodesic.count-1 {
            // ...

            //print(String("\(aircraft.position.x)"))
            DispatchQueue.main.async {
                //self.dataLabel.setNeedsDisplay()
                self.dataLabel.text =  String("\(aircraft.position.x)")
            }
        }
    }
}
您希望大部分操作在后台线程中运行,执行它的操作、计算内容、阻塞、等待等等

当它与UI有关时,它可以将块排队以在主线程上运行,主线程仍在(独立)运行,处理事件、更新视图、重新计算布局,并通常保持应用程序的响应性


最后,您必须确保后台线程中的所有内容都是线程安全的。

谢谢您,詹姆斯。标题中说明了这个问题。你的解决方案是正确的。“最后,您必须确保后台线程中的所有内容都是线程安全的。”我是编程新手——您能简要解释一下如何确保“线程安全”吗?遗憾的是,要完整地解释线程安全需要一整本书。概括地说,您希望确保所调用的API是线程安全的(可以从多个线程同时使用),或者必须确保仅从单个线程使用它们,并且只有少数API可以在主线程上使用。在您的情况下,似乎需要调查
SCNTransaction
,以确保它符合此标准。谷歌是你的朋友。再次感谢詹姆斯。是时候让我在线程上做一些谷歌搜索了!!
@objc func sceneTapped(recognizer: UITapGestureRecognizer) {
    // ...
    DispatchQueue.global().async {
        // execute long running code in a background thread
        for i in 0...geodesic.count-1 {
            // ...

            //print(String("\(aircraft.position.x)"))
            DispatchQueue.main.async {
                //self.dataLabel.setNeedsDisplay()
                self.dataLabel.text =  String("\(aircraft.position.x)")
            }
        }
    }
}