'SCNMaterial not changing color when setting 'diffuse.contents'

I have a bunch of cubes (SCNBox for the geometry of an SCNNode).

The cubes each have a single SCNMaterial (a UIColor) assigned to their respective materials array.

Here's what that looks like:

let box = SCNBox(width: 3.0, height: 3.0, length: 3.0, chamferRadius: 0.2)
let cubeNode = SCNNode(geometry: box)
let material = SCNMaterial()

material.diffuse.contents = UIColor.red

box.materials = [material]

(self.view as! SCNView).scene?.rootNode.addChildNode(cubeNode)

When you tap on one of these cubes, I try to change their color to green by doing the following:

material.diffuse.contents = UIColor.green

But this doesn't always work! Sometimes the cube turns green as desired, and sometimes it stays red.

I've tested some theories, and I've established the following:

  1. The code that sets the cube's color to green does, indeed, run.
  2. The material is, indeed, set to green (I inspected material.diffuse.contents via print()), and I can see that the color was set correctly.
  3. The code that determines which cube was tapped works correctly. So, it's not a matter of hit testing or anything like that.
  4. The problem happens on both Simulator and device.

For lighting, I'm simply using scnView.autoenablesDefaultLighting = true. No additional lights involved.

Question: Has anyone else experienced this problem? Am I doing something wrong, or is this a legitimate bug?

EDIT: Here's a bit more information about how I'm trying to set the color of the cubes when tapped:

@objc
func handleTap(_ gestureRecognize: UIGestureRecognizer) {
    let scnView = self.view as! SCNView
    let p = gestureRecognize.location(in: scnView)
    let hitResults = scnView.hitTest(p, options: [:])

    if hitResults.count > 0 {
        let result = hitResults[0]
        let material = result.node.geometry?.firstMaterial

        if let material = material {
            print("GREEN") //Prints "GREEN" every time, as expected -- but sometimes the cube turns green and sometimes it does not.
            material.diffuse.contents = UIColor.green
        }
    }

}

EDIT #2: I've managed to get this working, but I'm left with more questions than answers.

Instead of updating the cubes' color by setting material.diffuse.contents, I tried using material.emission.contents. For whatever reason, this works 100% of the time, as desired.

The downside is that the visual appearance of the cubes when setting material.emission.contents is a bit odd, and not necessarily what I'm looking for. It's green, yes -- but it's different somehow. But that's another matter.

Feel free to post an answer to this question if you can explain these behaviors.



Solution 1:[1]

I tried your sample code in Xcode 13.3 on macOS 12.3.1, everything works fine (app and simulator).

Make sure you're using UITapGestureRecognizer, not UIPanGestureRecognizer:

let recognizer = UITapGestureRecognizer(target: self, action: #selector(tap))
(self.view as! SCNView).addGestureRecognizer(recognizer)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1