3

I just started out using SceneKit in my UIKit app with the aim of displaying and manipulating some 3D models. I need to show a sphere with some short text written across it. I am rendering the sphere like this:

let sphereGeometry = SCNSphere(radius: 1)
let sphereNode = SCNNode(geometry: sphereGeometry)
sphereNode.position = SCNVector3(x: -1, y: 0, z: 8)
sphereGeometry.firstMaterial?.diffuse.contents = UIColor.cyan

self.rootNode.addChildNode(sphereNode)

I tried using a CATextLayer to achieve what I need but I've had little luck. What is the proper way to do this?

Youssef Moawad
  • 2,846
  • 5
  • 28
  • 50

1 Answers1

10

You can wrap text around the surface of an object by creating an image containing text, for example,

enter image description here

and then loading and assigning the image to the diffuse's contents property by

    let sphereGeometry = SCNSphere(radius: 1)
    let sphereNode = SCNNode(geometry: sphereGeometry)
    sphereNode.position = SCNVector3(x: 0, y: 0, z: 0)

    if let textImage = UIImage(named:"TextImage") {
        sphereGeometry.firstMaterial?.diffuse.contents = textImage
    }

    scene.rootNode.addChildNode(sphereNode)

enter image description here

Alternatively, you can programmatically create an image with text by

func imageWithText(text:String, fontSize:CGFloat = 150, fontColor:UIColor = .black, imageSize:CGSize, backgroundColor:UIColor) -> UIImage? {

    let imageRect = CGRect(origin: CGPoint.zero, size: imageSize)
    UIGraphicsBeginImageContext(imageSize)

    defer {
        UIGraphicsEndImageContext()
    }

    guard let context = UIGraphicsGetCurrentContext() else {
        return nil
    }

    // Fill the background with a color
    context.setFillColor(backgroundColor.cgColor)
    context.fill(imageRect)

    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.alignment = .center

    // Define the attributes of the text
    let attributes = [
        NSFontAttributeName: UIFont(name: "TimesNewRomanPS-BoldMT", size:fontSize),
        NSParagraphStyleAttributeName: paragraphStyle,
        NSForegroundColorAttributeName: fontColor
    ]

    // Determine the width/height of the text for the attributes
    let textSize = text.size(attributes: attributes)

    // Draw text in the current context
    text.draw(at: CGPoint(x: imageSize.width/2 - textSize.width/2, y: imageSize.height/2 - textSize.height/2), withAttributes: attributes)

    if let image = UIGraphicsGetImageFromCurrentImageContext() {
        return image
    }
    return nil
}

and apply the image to the sphere with

    let sphereGeometry = SCNSphere(radius: 1)
    let sphereNode = SCNNode(geometry: sphereGeometry)
    sphereNode.position = SCNVector3(x: 0, y: 0, z: 0)

    if let image = imageWithText(text: "Hello, World!", imageSize: CGSize(width:1024,height:1024), backgroundColor: .cyan) {
        sphereGeometry.firstMaterial?.diffuse.contents = image
    }

    scene.rootNode.addChildNode(sphereNode)
0x141E
  • 12,613
  • 2
  • 41
  • 54
  • 1
    Worked brilliantly! Thank you! – Youssef Moawad Jun 18 '17 at 22:35
  • When the attributes of the text to be displayed are specified, is there a way to make it such that it renders the text as HTML? I am doing something similar for UILabel with a custom `setHTML` method that sets the `documentType` attribute. – Youssef Moawad Jun 18 '17 at 22:40
  • I couldn't find anything that would allow you to do that. Perhaps you can accomplish the same or something similar with an `NSAttributedString`. – 0x141E Jun 21 '17 at 04:31