2

I'm programmatically generating a set of UILabels, attaching them to SCNNodes and then placing them in a scene.

The problem is that the text on some of the labels doesn't appear. This occurs (seemingly) randomly.

Here's the code:

var labels = [SCNNode]()

var index: Int
var x: Float
var y: Float

let N = 3
for i in 0 ... N-1 {
    for k in 0 ... N-1 {

        let node = label()
        labels.append(node)

        index = labels.count - 1
        x = Float(i) * 0.5 - 0.5
        y = Float(k) * 0.5 - 0.5

        sceneView.scene.rootNode.addChildNode(labels[index])

        labels[index].position = SCNVector3Make(x, y, -1)
    }
}

and the method to create the label node:

func label() -> SCNNode {

    let node = SCNNode()
    let label = UILabel(frame: CGRect(x: CGFloat(0), y: CGFloat(0), 
                           width: CGFloat(100), height: CGFloat(50)))

    let plane = SCNPlane(width: 0.2, height: 0.1)

    label.text = "test"
    label.adjustsFontSizeToFitWidth = true

    plane.firstMaterial?.diffuse.contents = label
    node.geometry = plane

    return node

}

The labels themselves always appear correctly, it's just that some of them are blank, with no text.

I've tried playing around with the size of the label, the size of the plane it is attached to, the font size etc - nothing seems to work.

I've also tried enclosing the label creation in DispatchQueue.main.async { ... }, which didn't help either.

I'm moderately new to Swift and iOS, so could easily have missed something very obvious.

Thanks in advance!

EDIT:

(1) Setting label.backgroundColor = UIColor.magenta makes it clear that in fact the label is not being created, but the node / plane is.

Some of the labels are left white (i.e only the SCNNode is being rendered), however after a short delay they sometimes then become magenta and the text will appear. Some of the labels will remain missing though.

(2) It further appears that it's related to the position and orientation of the node (label) relative to the camera. I created a large (10x10) grid of labels, then tested placing the camera at different initial positions in the grid. The likelihood that a node appeared seemed directly related to the distance of the node from the initial camera position. Those nodes directly in front of the camera were always rendered, and those far away almost never were.

(3) workaround / hack is to convert the labels to images, and use them instead - code is at https://github.com/Jordan-Campbell/uiimage-arkit-testing if anyone is interested.

Jordan
  • 481
  • 4
  • 14
  • 1
    Have you checked that you are not trying to place two nodes on the same position? I had a similar problem of some of them appearing and others not and it was because of that. – baldemora Jul 23 '18 at 20:06
  • 1
    Another thing that could be causing this is that it might lose the labels out of memory before it renders them, if you check Apple docs they recommend that your object is not declared inside of a function but on the memory cycle of a class. – baldemora Jul 23 '18 at 20:06
  • Hi, yeah rendering labels in AR isn't great - a lot of it actually depends on the angle that the label is positioned relative to the camera when it's rendered. I've found that the best thing is to draw stuff into a `UIImage` and then attach that to an `SCNPlane`. – Jordan Jul 25 '18 at 02:28

2 Answers2

1

If you are labeling things in AR, 99% of the time it is better to do so in "Screen Space" rather than in "Perspective".

Benefits of labels in Screen Space:

  1. ALWAYS readable, regardless of user's distance from the label
  2. You can use regular UILabels, no need to draw them to an image and then map the image to an SCNPlane.
  3. Your app will have a first party feel to it because Apple uses Screen Space for their labels in all of their AR apps (see Measure).
  4. You will be able to use standard animations on your UILabel, animations are much more complex to set up when working with content in Perspective.

If you are sold on Screen Space, let me know and I'll be happy to put up some code showing you the basics.

Greg
  • 121
  • 1
  • I would be interested in an example :) – BlackMirrorz Aug 11 '18 at 05:44
  • I think there are plenty of reasons to create labels in AR - especially given that one of the principle benefits of AR is that it opens up a whole lot of virtual real estate so you explicitly avoid filling the screen. Anyway the solution I posted as an edit to my question has now become the default standard by most people (who all discovered the solution independently). – Jordan Aug 11 '18 at 07:43
  • ( ... continuing my comment above) - your bullet points are certainly correct, however I don't think that discounts the benefit you gain from having information displayed in AR. Each have their merits at different times. – Jordan Aug 11 '18 at 07:46
  • Jordan - Screen Space is technically still displaying labels "in AR", the labels still have a spatial coordinate, however, it is displayed in a way that keeps them readable. – Greg Aug 11 '18 at 22:04
  • Here is a link to a full youtube tutorial I made on the topic: https://www.youtube.com/watch?v=itXUZmKCv_Q&t=4s – Greg Aug 11 '18 at 22:05
  • Jump to part 6 and 7 of the series to see things directly related to screen space – Greg Aug 11 '18 at 22:06
0
  1. Use main thread for creating and adding labels to scene. This will make things faster, and avoid coupling this addition to scene with plane detection this really slows down the rendering. this works at times.

DispatchQueue.main.async {

}

  1. Use a UIView SuperView as Parent to your label this would make things smoother.
Emu Malik
  • 101
  • 1
  • 8