Virtual objects appear in an augmented reality view when you specify their x, y, and z coordinates. Such virtual objects remain fixed in place unless the user resets the world origin so they appear based on the new world origin’s location.
However, sometimes you may not want to specify exact coordinates to display a virtual object. Instead, you might want to place one virtual object a specific distance based on the current position of another virtual object. Rather than define exact coordinates, you really want to define relative coordinates such as always placing a second virtual object a fixed distance to the left of another virtual object.
Often times you’ll need to combine multiple virtual objects to create a single image, such as a box with a pyramid on top to create a house image. When working with multiple virtual objects that create a single object, relative positioning makes it easy to display all virtual objects correctly so they create a unified visual appearance.
Defining Relative Positions
The rootnode appears at the world origin (0, 0, 0) so the addChildNode command simply adds a node based on its position of the rootnode.
- 1.
Start Xcode. (Make sure you’re using Xcode 10 or greater.)
- 2.
Choose File ➤ New ➤ Project. Xcode asks you to choose a template.
- 3.
Click the iOS category.
- 4.
Click the Single View App icon and click the Next button. Xcode asks for a product name, organization name, organization identifiers, and content technology.
- 5.
Click in the Product Name text field and type a descriptive name for your project such as Positioning. (The exact name does not matter.)
- 6.
Click the Next button. Xcode asks where you want to store your project.
- 7.
Choose a folder and click the Create button. Xcode creates an iOS project.
- 1.
Click the Info.plist file in the Navigator pane. Xcode displays a list of keys, types, and values.
- 2.
Click the disclosure triangle to expand the Required Device Capabilities category to display Item 0.
- 3.
Move the mouse pointer over Item 0 to display a plus (+) icon.
- 4.
Click this plus (+) icon to display a blank Item 1.
- 5.
Type arkit under the Value category in the Item 1 row.
- 6.
Move the mouse pointer over the last row to display a plus (+) icon.
- 7.
Click on the plus (+) icon to create a new row. A popup menu appears.
- 8.
Choose Privacy – Camera Usage Description.
- 9.
Type AR needs to use the camera under the Value category in the Privacy – Camera Usage Description row.
- 1.
Click on the ViewController.swift file in the Navigator pane.
- 2.Edit the ViewController.swift file so it looks like this:import UIKitimport SceneKitimport ARKitclass ViewController: UIViewController, ARSCNViewDelegate {let configuration = ARWorldTrackingConfiguration()override func viewDidLoad() {super.viewDidLoad()// Do any additional setup after loading the view, typically from a nib.}}

The user interface just needs a single ARSCNView
After you’ve added a single ARKit SceneView to the user interface, you need to put constraints on those user interface items. To add constraints, choose Editor ➤ Resolve Auto Layout Issues ➤ Reset to Suggested Constraints at the bottom half of the menu under the All Views in Container category.
- 1.
Click the Main.storyboard file in the Navigator pane.
- 2.
Click the Assistant Editor icon or choose View ➤ Assistant Editor ➤ Show Assistant Editor to display the Main.storyboard and the ViewController.swift file side by side.
- 3.
Move the mouse pointer over the ARSCNView, hold down the Control key, and Ctrl-drag under the class ViewController line.
- 4.
Release the Control key and the left mouse button. A popup menu appears.
- 5.Click in the Name text field and type sceneView, then click the Connect button. Xcode creates an IBOutlet, as shown here:@IBOutlet var sceneView: ARSCNView!
- 6.Edit the viewDidLoad function so it looks like this:override func viewDidLoad() {super.viewDidLoad()// Do any additional setup after loading the view, typically from a nib.sceneView.delegate = selfsceneView.showsStatistics = truesceneView.debugOptions = [ARSCNDebugOptions.showWorldOrigin, ARSCNDebugOptions.showFeaturePoints]showShape()}
- 7.Edit the viewWillAppear function so it looks like this:override func viewWillAppear(_ animated: Bool) {super.viewWillAppear(animated)sceneView.session.run(configuration)}
This viewDidLoad function calls on a showShape function. The showShape function displays a sphere.
- 8.Type the following underneath the viewWillAppear function:func showShape() {let sphere = SCNSphere(radius: 0.05)sphere.firstMaterial?.diffuse.contents = UIColor.orangelet node = SCNNode()node.geometry = spherenode.position = SCNVector3(0.2, 0.1, -0.1)sceneView.scene.rootNode.addChildNode(node)}
- 1.
Connect an iOS device to your Macintosh through its USB cable.
- 2.
Click the Run button or choose Product ➤ Run.
- 3.
When the app runs, an orange sphere appears.
- 4.
Click the Stop button or choose Product ➤ Stop.
Now let’s add a second virtual object, such as a green box, except we want it to appear 0.4 meters to the left (the x-axis), 0.3 meters lower (the y-axis), and 0.2 meters in front (the z-axis). To do this, we have to use math.
To make a virtual object appear 0.4 meters to the left of the first virtual object, we need to use a value of -.0.2 (0.2 – 0.4). To make it appear 0.3 meters lower, we need to use a value of -0.2 (0.1 – 0.3), and to make it appear 0.2 meters in front, we need to use a value of 0.1 (-0.1 + 0.2).
If you run this modified showShape function , your app should show an orange sphere and a green box. The huge problem with this method of positioning a second virtual object based on the position of another virtual object is that you must calculate the distances between the two virtual objects manually, which makes this method error-prone.
Rather than calculate distances manually and hope you did it right, a far better solution is to simply define the distance you want one virtual object to appear away from another one.
Rather than place a virtual object (node) in relation to the rootnode (world origin), you can just place a virtual object in relation to another virtual object. Doing so avoids the hassle of doing the math to determine x, y, and z coordinates solely based on the world origin.
This creates the exact same result of drawing a green box a specific distance from an orange sphere. The big difference is that instead of adding the green box to the rootnode and calculating its distance from the orange sphere, we just added the green box to the orange sphere and defined the distance along the x, y, and z axis to place the green box.
Relative positioning makes it easy to place virtual objects a fixed distance from another virtual object. Initially, you’ll need to place one virtual object relative to the rootnode (the world origin of 0, 0, 0), but after that, you can place virtual objects relative to the location of other virtual objects.
Combining Geometric Shapes
Once you know how to position virtual objects relative to one another, you can easily combine multiple geometric shapes to create interesting objects that you could never create using a single geometric shape. For example, you could create a pyramid on top of a box to create a house object, multiple cylinders with a sphere to create a stick figure, or a plane with a torus to create a basketball hoop.
To see how to use relative positioning to combine multiple geometric shapes together, let’s create a snowman. The snowman will consist of three spheres stacked on top of each other with a hat on top, which we’ll create with two cylinders.
First, we’ll need to create three spheres stacked on top of each other. Let’s start with one large sphere that will be positioned relative to the rootnode (the world origin at 0, 0, 0). Then we’ll add progressively smaller spheres on top using relative positioning.
This creates a red sphere positioned based on the rootnode, which appears at the world origin (0, 0, 0). Now let’s create a second sphere in blue that appears on top of the red sphere. Instead of positioning this blue sphere in the augmented reality view by the rootnode (world origin), we’ll use relative positioning and place it based on the location of the red sphere.
When placing virtual objects relative to another virtual object, you need to use trial and error to define specific distances until the virtual objects appear the way you want in relation to one another.
This code creates a sphere with a radius of 0.03 meters and colors it blue. Then it positions it at 0, 0.06, 0 based on the relative position of the red sphere. That means it appears 0.06 meters above the center of the red sphere.
The radius of the hat rim is 0.03 meters and its height is just 0.002 meters tall. This code colors the cylinder black and places it 0.016 meters above the center of the top sphere.
- 1.
Click the ViewController.swift file in the Navigator pane.
- 2.Edit the showShape function so it looks like this:func showShape() {let sphere = SCNSphere(radius: 0.04)sphere.firstMaterial?.diffuse.contents = UIColor.redlet node = SCNNode()node.geometry = spherenode.position = SCNVector3(0.05, 0.05, -0.05)sceneView.scene.rootNode.addChildNode(node)let middleSphere = SCNSphere(radius: 0.03)middleSphere.firstMaterial?.diffuse.contents = UIColor.bluelet middleNode = SCNNode()middleNode.geometry = middleSpheremiddleNode.position = SCNVector3(0, 0.06, 0)node.addChildNode(middleNode)let topSphere = SCNSphere(radius: 0.02)topSphere.firstMaterial?.diffuse.contents = UIColor.whitelet topNode = SCNNode()topNode.geometry = topSpheretopNode.position = SCNVector3(0, 0.04, 0)middleNode.addChildNode(topNode)let hatRim = SCNCylinder(radius: 0.03, height: 0.002)hatRim.firstMaterial?.diffuse.contents = UIColor.blacklet rimNode = SCNNode()rimNode.geometry = hatRimrimNode.position = SCNVector3(0, 0.016, 0)topNode.addChildNode(rimNode)let hatTop = SCNCylinder(radius: 0.015, height: 0.025)hatTop.firstMaterial?.diffuse.contents = UIColor.blacklet hatNode = SCNNode()hatNode.geometry = hatTophatNode.position = SCNVector3(0, 0.01, 0)rimNode.addChildNode(hatNode)}
- 3.
Connect an iOS device to your Macintosh through its USB cable.
- 4.
Click the Run button or choose Product ➤ Run. Notice that the virtual snowman appears in the augmented reality view, as shown in Figure 6-2.

Three spheres and two cylinders create a virtual snowman
- 5.
Click the Stop button or choose Product ➤ Stop
Summary
When placing virtual objects in an augmented reality view, each virtual object must be assigned to a node and that node needs x, y, and z coordinates to define its location. At least one virtual object needs to be located based on its distance from the rootnode, which represents the world origin (0, 0, 0). After placing at least one virtual object based on the rootnode, you can place additional virtual objects based on either the rootnode or any existing virtual objects.
Placing virtual objects based on the rootnode lets you define their location independent of any other virtual objects. However, if you want one virtual object to appear a fixed distance from a second virtual object, it’s easier to use relative positioning. Instead of defining a virtual object based on its location to the rootnode, you define it based on its location to another virtual object.
Without relative positioning, you would have to calculate distances based on the rootnode, which can be inaccurate and cumbersome to do. Relative positioning makes it easy to define virtual objects a fixed distance from one another.