Code a Measuring App With ARKit: Objects and Shadows

Along with many other tools which have been made obsolete by modern technology, it looks as if the common tape measure may be the next to go. In this tutorial, we’ll learn to bridge the gap between reality and technology by using augmented reality and the camera on your iOS device to create an app which will measure the distance between two points.

In this post we’ll start the app and code its main interface elements. Tomorrow, we’ll finish it off by measuring between two points in the AR scene.

If you’re just getting started with ARKit, be sure to check out my other tutorial, where you’ll learn to bring a planet into your home using augmented reality. 

Getting Started

Let’s begin making our virtual ruler, which will let us measure the distance between any two real-world points. If you think about it, you might not even need your measuring tape for day-to-day measurements anymore!

Create an ARKit App

New Project

To start, make sure you have a version of Xcode that supports ARKit. Then, you’ll need to make a new Xcode project. 

Go ahead and open Xcode and click Create a new Xcode project.

Figure 1 Create an Xcode Project

You may be used to making a Single View Application, but for this tutorial you will need to choose Augmented Reality App and then click Next.

Figure 2 Choose an Augmented Reality App

Gaming Frameworks

You can name your project anything you like, but I will be naming mine ARPlanets. You will also notice that there is an option at the bottom where you can select from SceneKit, SpriteKit, and Metal. These are all Apple’s gaming frameworks, and for the purpose of this tutorial, we’ll be making use of SceneKit

Go ahead and select SceneKit if it isn’t already selected. Your screen should look something like this:

Figure 3 Create your Project

Preparing for Testing

Connecting an iPhone

Since the Xcode Simulator doesn’t have a camera, you’ll need to plug in your iPhone. Unfortunately, if you don’t have an iPhone, you’ll need to borrow one to be able to follow along with this tutorial (and for any other camera- or AR-related apps). If you already have an iPhone connected to Xcode, you can skip ahead to the next step.

A nifty new feature in Xcode 9 is that you can wirelessly debug your app on a device, so let’s set that up now.

In the top menu bar, choose Window > Devices and Simulators. In the window that appears, make sure that Devices is selected at the top.

Now, plug in your device using a lightning cable. This should make your device appear in the left pane of the Devices and Simulators window. Simply click your device, and check the Connect via Network box.

Figure 4 Devices and Simulators

You will now be able to wirelessly debug on this iPhone for all future apps.

Complete Setup

Now your setup is complete. You should have a working ARKit app, and you can test it on the iPhone that you just connected. In the upper left of Xcode, next to the Run and Stop buttons, select your device from the simulator dropdown.

Figure 5 Select a Simulator

Now if you click run, you should see a virtual spaceship appear in your world!

An Augemented Reality spaceship

Our next step will be to remove the spaceship and start coding the interface for our measurement app.

The Measurement App

Great job on getting the ARKit app up and running! Now, let’s proceed to get rid of the spaceship and creating our distance-measuring application from scratch. Our app will work by letting the user place two spheres in the AR scene and then calculating the distance between them. As simple as that might sound, it does requires a fair bit of programming, so put on your thinking hat and let’s get to it!

I won’t be explaining the starter code in this tutorial, so if you don’t understand something, make sure you check out my tutorial on getting started with ARKit first.

Starting From Scratch

Removing the Spaceship

We’d rather not have a random spaceship showing up in our measurement app, so let’s remove it and its code from the project.

If you head to the Assets.xcassets folder in your project directory, you’ll notice two files which make up your spaceship: ship.scn and texture.png. These are the two files which tell Xcode exactly how the spaceship should look. In our app, though, we’ll be defining the spheres programmatically. If you have more complicated nodes to add, it’s okay to use the .scn method, but if you can, it’s always preferable to do things in code.

Removing the Sample Code

Now that you’ve removed the ship assets, you may be receiving errors at this point (if not, you will when the code runs). This is because there are still current references to the files we deleted. Don’t worry, though. We’ll get rid of that sample code so that we can start programming from scratch.

Head to the ViewController.swift file and remove the following two lines of code:

// Set the view's delegate
sceneView.delegate = self

// Show statistics such as fps and timing information
sceneView.showsStatistics = true

After you’ve done so, your viewDidLoad() method should look as follows:

override func viewDidLoad() {
    super.viewDidLoad()
    // Set the view's delegate
    sceneView.delegate = self
    
    // Show statistics such as fps and timing information
    sceneView.showsStatistics = true
}

We can still use these in our new app. The remaining starter code in the app is just boilerplate which does things like run the SceneKit view and other things of that sort.

Creating a Label

Of course, if we have a measuring app, we’ll need some way of telling the user what the final measurement is, and what better way is there than to use a label? For practice, we won’t be using the storyboard for our label, but instead we’ll add it programmatically.

Variable and Class Instance

To create a label, declare a variable at the top of your class like this:

var measurementLabel = UILabel()

Here, we just created an instance of the UILabel class, and in our viewDidLoad() method, we can configure its various attributes. 

Add the following to your viewDidLoad() method at the top to create a background:

Making a Background

measurementLabel.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: 100)
measurementLabel.backgroundColor = .white

The first line here sets a white background for the label so that it is visible and doesn’t blend in with the live view in the back. This background will be aligned to the top and will have a height of 100.

Alignment and Adding the Label

Next, we need to set the text as well as its alignment, and add the label to the view. To do this, add the following to your viewDidLoad() method:

measurementLabel.text = "0 inches"
measurementLabel.textAlignment = .center
view.addSubview(measurementLabel)

The first line of code makes the label’s default text say “0 inches.” Also, we want the app to look fairly refined, so we’ll center the label in the canvas. Lastly, we add the label to the view.

Creating a Sphere

Since our app will be based on placing two spheres and then measuring the distance between them, our obvious first step would be to learn how to create spheres, so let’s do that now.

Making a Reusable Method

We’re making the sphere multiple times, so it doesn’t make sense to code it in the viewDidLoad() method. Instead, let’s create a method that we can reuse and call from wherever we like. So paste the following function into your project:

func newSphere(at position: SCNVector3) -> SCNNode {
    // Your code goes here
}

As you can see, we are taking a position of type SCNVector3 and then returning a SCNNode, which would be the sphere. It doesn’t seem very important right now, but we’ll get back to why we’re taking in a SCNVector3 as a position later.

Creating an Actual Sphere

Let’s now begin actually creating the sphere. Add the following three lines into the newSphere() method that you just created:

// Creates an SCNSphere with a radius of 0.4
let sphere = SCNSphere(radius: 0.01)

// Converts the sphere into an SCNNode
let node = SCNNode(geometry: sphere)

// Positions the node based on the passed in position
node.position = position

The first line of code simply creates a sphere of type SCNSphere and sets its initial radius to a value of 0.01 meters in the SceneKit coordinate system, and this will be just the right size. If you want, though, you can experiment with different sizes of spheres.

The next line of code changes the geometry mesh which is of type SCNSphere into an actual object—something we can actually change and do other things with. The SCNNode, anyway, is going to be what we return to the function call.

Lastly, by taking the parameter of position from the function, we’re simply placing the node where it needs to be put based on the tap (which we haven’t created yet).

Lighting, Shadows, and Appearance

This part isn’t truly needed, but it’s always good to make your app look nice, and also, it will help you learn about lighting these artificial objects for future apps.

Start by creating an empty material:

// Creates a material that is recognized by SceneKit
let material = SCNMaterial()

Here we’re simply telling SceneKit that there will be a new material which we’ll add properties to later on. In other words, we just created an instance of SCNMaterial() and assigned it to a constant called material.

Next, set the material to be orange by doing this:

// Converts the contents of the PNG file into the material
material.diffuse.contents = UIColor.orange

Since we don’t have an image to wrap around the sphere, set its color to orange. Yes, just a plain orange color. If you were to use an image, though, you would need to do the same thing, but instead, set material.diffuse.contents to an image instead.

Lastly, set the lighting configuration and add the material to the sphere like this:

// Creates realistic shadows around the sphere
material.lightingModel = .blinn

// Wraps the newly made material around the sphere
sphere.firstMaterial = material

Now, what you’ll see here is us changing how the lights in the environment will affect the material that we created. By setting the model to the .blinn model, we’re calculating our highlights using a Blinn-Phong Formula. We don’t need to get into the details right now, but this specific lighting model will work best for our purposes. Lastly, we’re replacing the sphere’s current material with the one we just created.

If you’re curious about the lighting model we just used, here’s what Apple’s documentation says about the .blinn lighting model:

Shading that incorporates ambient, diffuse, and specular properties, where specular highlights are calculated using the Blinn-Phong formula.—Apple Documentation

Returning Our Sphere

Finally, after creating the sphere, our last step is to simply return the SCNNode typed sphere to the function call site. Just paste this line right before the ending curly-brace of your function:

return node

All done! This method is all geared up to make spheres and then return them. Remember, though, that the spheres won’t appear unless you actually call the method and then add them to the superview.

We’ve made a good start on our app. Check back tomorrow and I’ll show you how to add a tap gesture recognizer to let users place the spheres on the points they want to measure.

Conclusion

We’ve accomplished a lot already: we set up an ARKit app and built the basic interface elements for our distance measurement tool. Tomorrow, we’ll finish up by letting users place these spheres at points in the real world and then calculating the distance between them.

And while you’re here, check out our video course on coding for ARKit on iOS. In this course, you’ll see how to code an ARKit app from start to finish!

Leave a Reply

Your email address will not be published. Required fields are marked *