Taking advantage of SpriteKit in Cocoa Touch

Milton Moura

May 06th, 2016

Since Apple announced SpriteKit at WWDC 2013 along with iOS 7, it has been promoted as a framework for building 2D games with high-performance graphics and engaging gameplay. However, as I will show you in this post, by taking advantage of some of its features in your UIKit-based application, you'll be able to add some nice visual effects to your user interface without pulling too much muscle. We will use the latest stable Swift version along with Xcode 7.1 for our code examples. All the code in this post can be found in this github repository.

SpriteKit's infrastructure

SpriteKit provides an API to manipulate textured images (sprites), including animations and applying image filters with optional physics simulation and sound playback. Although Cocoa Touch also provides other frameworks for these things, such as Core Animation, UIDynamics, and AV Foundation, SpriteKit is especially optimized to perform these operations in batch and performs them on a lower lever, transforming all graphics operations directly into OpenGL commands.

The top-level user interface object for SpriteKit are SKViews, which can be added to any application view controller and are then used to present scene objects of the SKScene type, composed of possibly multiple nodes with content that will render seamlessly with other layers or views that might also be contained in the application's current view hierarchy.

This allows us to add smooth and optimized graphical effects to our application UI, enriching the user experience and keeping our refresh rate at 60hz.

Our sample project

To show how to combine typical UIKit controls with SpriteKit, we'll build a sample login screen composed of UITextFields, UIButtons, and UILabels for our wonderful new WINTER app. However, instead of a boring, static background, we'll add an animated particle effect to simulate falling snow and apply a Core Image vignette filter to mask them under a niffy spotlight-type effect.

1. Creating the view hierarchy

We'll start with a brand new Swift Xcode project, selecting the iOS > Single View Application template and opening Main Storyboard.

In the existing View Controller scene, we will add a new UIView that anchors to its parent view's sides, top, and bottom and change its class from UIView, which is the default, to SKView. Also, make sure the background color for this view is dark so that the particles that we'll add later have a nice contrast.

Now, we'll add UITextFields, UILabels, and UIButtons to replicate the following login screen. Also, we need IBOutlet for our SKView. Let's call it sceneView. This is the SpriteKit view, in which we will add SKScene with the particle and image filter effect:

2. Adding a Core Image filter

We're done with UIKit for now. We currently have a fully (well, not really) functional login screen, and it's time to make it more dynamic. The first thing we need is a scene, so we'll add a new Swift class called ParticleScene.

In order to use SpriteKit's objects, let's not forget to add an import statement for this and declare that our class is SKScene. Take a look at the following code:

import SpriteKit

    class ParticleScene : SKScene
    {
        ...
    }

The way we initialize a scene in SpriteKit is by overriding the didMoveToView(_:) method, which is called when a scene is added to SKView. So, let's do this and set up the Core Image filter. If you are not familiar with Core Image, it is a powerful image-processing framework that provides over 90 filters that can be applied in real time to images, videos, and, coincidentally, to SpriteKit nodes of the SKNode type. SKNode is the basic unit of content in SpriteKit, and our SKScene is a big node to render. Actually, SKScene is a type of SKEffectNode, which is a special type of node that allows its content to be post-processed using Core Image filters. In the following snippet, we will add a CIVignetteEffect filter centered on our scene, with a radius equal to the width of our view frame:

override func didMoveToView(view: SKView) {
        scaleMode = .ResizeFill

        // initialize the Core Image filter
        if let filter = CIFilter(name: "CIVignetteEffect") {
            // set the default input parameter values
                filter.setDefaults()
            // make the vignette center be the center of the view
                filter.setValue(CIVector(CGPoint: view.center), forKey: "inputCenter")
            // set the radius to be equal to the view width
                filter.setValue(view.frame.size.width, forKey: "inputRadius")

            // apply the filter to the current scene
                self.filter = filter
                self.shouldEnableEffects = true
            }

            presentingView = view
        }

If you run the application as is, you'll notice a nice spotlight effect behind our login form. However, we're not done yet.

3. Adding a particle system

As this is a WINTER app, let's add some falling snowflakes in the background. Add a new SpriteKit Particle File to the project and select the Snow template. Next, we will add a method to set up our particle node emitter, SKEmitterNode, which hides all the complexity of a particle system. Take a look at the following code:

 

func startEmission() {
        // load the snow template from the app bundle
        emitter = SKEmitterNode(fileNamed: "Snow.sks")
        // emit particles from the top of the view
        emitter.particlePositionRange = CGVectorMake(presentingView.bounds.size.width, 0)
        emitter.position = CGPointMake(presentingView.center.x, presentingView.bounds.size.height)
        emitter.targetNode = self

        // add the emitter to the scene
        addChild(emitter)
}

To finish things off, let's create a new property to hold our particle scene in ViewController and start the particle in the viewDidAppear() method, as follows:

 	class ViewController: UIViewController
{
    ...
    let emitterScene = ParticleScene()
    ...

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        emitterScene.startEmission()
    }
}

And we're done! We now have a nice UIKit login form with an animated background that is much more compelling than a simple background color, gradient, or texture.

Where to go from here

You can explore more Core Image filters to add stunning effects to your UI, but be warned that some are not prepared for real-time full-frame rendering. Indeed, SpriteKit is very powerful, and you can even use OpenGL shaders in nodes and particles.

You are welcome to take a look at the source code for this article, and you'll see that it has a little extra Core Motion trick that shifts the direction of the falling snow according to the position of your device.

About the author

Milton Moura (@mgcm) is a freelance iOS developer based in Portugal. He has worked professionally in several industries, from aviation to telecommunications and energy, and is now fully dedicated to creating amazing applications using Apple technologies. With a passion for design and user interaction, he is also very interested in new approaches to software development. You can find out more about him here.

comments powered by Disqus