Using SceneKit and CoreMotion in Swift

This article describes how to use SceneKit and CoreMotion to create a 360º panoramic photo viewer that lets you explore the scene by moving your phone around. For the purposes of this article I’ll use the image below from Wikipedia.

360º panoramic view of Hellbrunn banqueting hall by MatthiasKabel

360º panoramic view of Hellbrunn banqueting hall by MatthiasKabel

Getting Started

To get started, download the template source code PanoView-1.0.zip. This is a simple, single view project with a SCNSceneView occupying the entire view and a suitably scaled version of the above photograph as a resource.

Loading Assets

The first step in our panoramic photo viewer is to load a photograph. This could come from the user’s camera roll or a URL, but for simplicity I am just going to load a photograph from the resource bundle.

Open the file ViewController.swift and add the code below to your viewDidLoad() method:

this finds the image file in the bundle and creates a UIImage from it. I found that using this two-step process allowed me to use bigger photographs than the simpler one-step method using UIImage(named:). I did not investigate why this was so.

Making a Scene

Everything in SceneKit belongs to a SCNScene. You could create all your objects first, then create your scene and them to it, but I prefer creating the scene first and adding the objects as I go along.

Add the following below the previous code:

This creates a scene and sets it on the sceneView property of the view controller. The sceneView property is an IBOutlet set up in the template project to reference the SCNSceneView in the storyboard. The showStatistics property controls the display of some interesting and useful performance information on the view and the allowsCameraControl allows the camera to be manipulated by touch gestures.

The way our panoramic photo viewer is going to work is by projecting the photo onto the inside of a sphere. The following code does that:

In SceneKit the scene is created as a hierarchy of SCNNodes. Nodes can contain geometry, lights, cameras, etc. Here we create a sphere geometry using the photo as the “material” to make it out of or, if you are familiar with Metal or OpenGL, we are using the photo as a texture for the sphere. We need to specify that material doubleSided so that it can be seen from the inside of the sphere. This newly created sphere is then wrapped in a node, positioned at the origin (0,0,0) and added to the scene.

At this point you can compile and run the project, but the results may not be what you were expecting…

Shows unexpected result of prematurely running tutorial. The image sphere is viewed from the outside.

While this is a very pretty marble, this is not quite what we are trying to achieve!

The problem here is that we are viewing the sphere from the outside. We want to view it from the inside.

Putting the Camera Inside the Sphere

To view the sphere from the inside, we create a camera node and position it inside the sphere. Recall we placed the sphere at the origin (0,0,0) so placing the camera there will center it in the sphere.

The following code does the trick.

If you now compile and run the project things are looking much better. You can use gestures to look around this magnificent hall.

An image showing that the updated code shows the view from inside the sphere.

The view from inside the sphere.

Controlling the Camera with Core Motion

So with a surprising small amount of code you’ve managed to get something pretty cool working, but it would be much cooler if the virtual camera would react to your phone’s movements. Well luckily with a little CoreMotion magic we can make that happen!

Since different devices have different capabilities the first order of business is to check that we will be able to monitor device motion.

Add the following code to your viewDidLoad after everything else:

The motionManager property is set in the template code around line 16 as follows:

Knowing that the capability is available we can request iOS periodically update us with the device’s orientation, or as CoreMotion refers to it, borrowing from aerospace terminology attitude.

The above code requests updates at 60fps by setting the deviceMotionUpdateInterval property to 1/60th of a second. It then starts updates on the main queue. These updates are processed by the trailing block which receives data and error arguments. If there is an error data would be nil and error would give further information. I choose, in rather cowardly fashion, to simply return if data was nil. You may want to do something more graceful.

The attitude field of CMDeviceMotion contains the device attitude in three different representations, Euler angles, a rotation matrix and a quaternion. I choose Euler angles because, quite honestly, I was a little rusty in how to use the other two. In the case of the iPhone, the three Euler angles roll, pitch and yaw refer to rotation around a axis running through the device from top to bottom, an axis running through the device from side to side and rotation around an axis passing perpendicularly through the center of the screen respectively, in radians. If the view controller was allowed to rotate in response to device orientation I found it quite tricky to get the camera to behave as I wanted, so I locked the app orientation to landscape left. With that in place, I found that the camera the assignments made in the above code work well. The 90º rotation on roll (π/2) was needed to ensure that when the phone was lying flat on a desk the view would be of the floor.

If you compile and run your project now you should be able to peer around the hall by simply moving your phone around!

Conclusion

I was fairly surprised when I started playing with SceneKit how little code was required to set up a scene. If you are familiar with Metal or OpenGL you will understand what I am talking about. While these two technologies are very powerful they require a lot of boilerplate just to get something to appear on the screen; SceneKit is much more forgiving.

This article has only scratched the surface of what can be done. To find out more about SceneKit I highly recommend Scene Kit Tutorial: Getting Started by Ricardo Rendon Cepeda at RayWendelich.com. For a deeper dive into CoreMotion see NSHipter’s CMDeviceMotion by Nate Cook.

The completed code for the tutorial can be found in the PanoView GitHub repository.

If you enjoyed this article please consider taking a moment to visit one of our sponsors!


Posted in Uncategorized | 2 Comments

Taming Foundation Constants into Swift Enums

For most of Cocoa, Apple has done a nice job of wrapping the APIs in native Swift entities. In particular, sets of constants that exist in NS_ENUM enumerations are imported as Swift enums with associated raw values. The situation is a little different when it comes to Foundation. In Foundation, many of the constants are not gathered into C enums and so they are not imported into Swift as nicely.

In this post I will look at what code is necessary to wrap up Foundation constants as a Swift enum and then I will introduce a simple tool to automate the code generation.

A Naïve First Attempt

I first became aware of this issue when I was writing some code to try to make the Keychain a little more palatable to use in Swift. I’ll use as an example throughout this post the manifest constants for the kSecAttrAccessible attribute in a keychain item. These are defined in SecItem.h as:

Although it is not apparent from these declarations, the underlying type of these constants is CFStringRef.

A first attempt to wrap these values in a Swift enum might look something like this.

Unfortunately this does not work, because the raw values of enum cases must be literals. I could have determined the values of the constants and explicitly equated these with the case values, but that would break in the (albeit unlikely) even of the value of a constant changing in some future version of the OS.

Conforming to RawRepresentable

A better solution is to have the enum conform to RawRepresentable. For Swift 1.1, the RawRepresentable protocol is defined as:

An initializer must be provided to create a enum from a rawValue and a computed property to perform the opposite conversion. In our example enum this would look something like this.

Other Nice Extensions

It can often be useful to be able to print the symbol for an enum (not it’s raw value) both during debugging and when presenting error information to the user. This can be achieved by extending the enum to conform to Printable.

Finally it would also be nice to be able to enumerate all the values of the enum. This can be done the extending the enum with an allValues array.

Automating the Procedure: enumtool

At this point we have a really nice Swift enum that wraps the Foundation constants, but what a lot of boilerplate code! Let’s face there is nothing more soul-destroying then spending hours cranking out boilerplate.

To automate this process, I created a little command line tool, written in swift and I have made it available on GitHub.

If the constants in the above example had been extracted, one per line, into a file called SecAttrAccessible.txt running enumtool as follows would create all the code in this article.

More information about enumtool can be found at the enumtool GitHub repository. I hope it may be of some use to you.


Posted in Swift | 1 Comment

Swift Parameter Labels

I am guessing early on in the development of the Swift language the relationship between a function declaration’s parameter labels and whether they need to be included in a call was much more forward.

Maybe it looked something like this.

Declaration Call Comment
func foo(x: Int) foo(2) Omit label
func foo(#x: Int) foo(x:2) Use label
func foo(_ x: Int) foo(2) or foo(x:2) Label optional
func foo(bar x: Int) foo(bar:2) Use external label

But then they decided that to make Swift play nice with Cocoa that labels would be required by default in classes, but not in standalone functions. As long as it wasn’t the first parameter in a member function, in which case the label was to be omitted. Unless it was an init in which case all labels were required. Oh, and if it had a default value, whether it is a standalone function or a member function the label was to be required.

When all was said and done, we were left the situation as depicted by the binary decision tree below.
Perhaps something more straightforward could have been devised?

labels_1.1


Posted in Swift | Leave a comment