Exploring SpriteKit: SwiftUI and SKScene
Book

Exploring Freelancing

Navigate freelancing as a developer; find clients, manage contracts, ensure timely payment, and learn from experiences!

SpriteKit is a powerful game development framework for creating 2D games on Apple platforms such as iOS, macOS, tvOS, and watchOS. With SpriteKit, you can easily create stunning 2D graphics, animations, and effects. It also provides a range of built-in physics simulations and particle systems, making creating dynamic and interactive game experiences easy.

This article introduces working with SpriteKit and SwiftUI, but mostly the basics of adding a SKScene in a SwiftUI view.

Creating a SpriteView

Apple introduced the SpriteView in iOS 14 and above to work directly with SKScene in SwiftUI. It is a SwiftUI view that renders a SpriteKit scene. The simplest initializer takes a SKScene, a transition if the scene is paused, and the preferred frames per second:

init(scene: SKScene, transition: SKTransition? = nil, isPaused: Bool = false, preferredFramesPerSecond: Int = 60)

Rendering a scene is as easy as:

struct ContentView: View {
  var body: some View {
    SpriteView(scene: SpriteKitScene())
  }
}

Creating a SKScene

For example, here is a class that displays four circles with numbers written inside them and a border. The circle, label, and border are SKLabelNode and SKShapeNode.

final class SpriteKitScene: SKScene {
  required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  override init(size: CGSize) {
    super.init(size: size)
    createNodes(for: size)

    scaleMode = .fill
    backgroundColor = .secondarySystemBackground
  }

  private func createNodes(for size: CGSize) {
    let width = size.width * 0.8
    let height = size.height * 0.8

    for count in 1...4 {
      let multiplier = Double(count) / 4
      let position = CGPoint(x: width * multiplier, y: height * multiplier)
      let node = createCircleNode(position: position, count: count)

      addChild(node)
    }
  }

  private func createCircleNode(position: CGPoint, count: Int) -> SKNode {
    let node = SKShapeNode(circleOfRadius: 50)
    node.fillColor = .white
    node.position = position

    let countLabelNode = createLabelNode(text: "\(count)")
    node.addChild(countLabelNode)

    let borderNode = createBorderNode(radius: 50)
    node.addChild(borderNode)

    return node
  }

  private func createLabelNode(text: String) -> SKLabelNode {
    let node = SKLabelNode(text: text)
    node.fontColor = .black
    node.verticalAlignmentMode = .center
    return node
  }

  private func createBorderNode(radius: CGFloat) -> SKShapeNode {
    let node = SKShapeNode(circleOfRadius: radius)
    node.fillColor = .clear
    node.strokeColor = .black
    node.lineWidth = 2
    return node
  }
}

Filling the Screen

To make sure that the scene is populated across the whole screen while ignoring safe areas, you can take advantage of GeometryReader to get the height and width:

struct ContentView: View {
  var body: some View {
    GeometryReader { geometry in
      let size = geometry.size

      SpriteView(scene: SpriteKitScene(size: size))
        .frame(width: size.width, height: size.height)
    }
    .ignoresSafeArea()
  }
}

This solution is better than using UIScreen.main.bounds as this solution respects screen orientation as well. Running the view:

Whether you’re an experienced game developer or just getting started, SpriteKit is a powerful tool for creating engaging and visually stunning 2D games on Apple platforms.

Book

Exploring Freelancing

Navigate freelancing as a developer; find clients, manage contracts, ensure timely payment, and learn from experiences!

Written by

Rudrank Riyam

Hi, my name is Rudrank. I create apps for Apple Platforms while listening to music all day and night. Author of "Exploring MusicKit". Apple WWDC 2019 scholarship winner.