SpriteKit
SKScene
iOS development
game development
user interface

Setting up buttons in SKScene

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In SpriteKit, buttons are usually regular nodes with touch handling rather than dedicated UIKit controls. A solid setup gives each button a clear node name, visual feedback, and a predictable action dispatch path. This keeps scene logic clean as your game menu grows.

Create Reusable Button Nodes

You can build buttons with SKShapeNode, SKSpriteNode, or SKLabelNode. SKShapeNode is convenient for a quick menu button without asset files.

swift
1import SpriteKit
2
3final class MenuScene: SKScene {
4    private func makeButton(text: String, name: String, y: CGFloat) -> SKNode {
5        let container = SKNode()
6        container.name = name
7        container.position = CGPoint(x: frame.midX, y: y)
8
9        let bg = SKShapeNode(rectOf: CGSize(width: 220, height: 56), cornerRadius: 10)
10        bg.fillColor = .systemBlue
11        bg.strokeColor = .white
12        bg.lineWidth = 2
13        bg.name = name
14
15        let label = SKLabelNode(text: text)
16        label.fontName = "AvenirNext-Bold"
17        label.fontSize = 24
18        label.verticalAlignmentMode = .center
19        label.name = name
20
21        container.addChild(bg)
22        container.addChild(label)
23        return container
24    }
25
26    override func didMove(to view: SKView) {
27        backgroundColor = .black
28        addChild(makeButton(text: "Play", name: "btn_play", y: frame.midY + 50))
29        addChild(makeButton(text: "Settings", name: "btn_settings", y: frame.midY - 30))
30    }
31}

Using the same name on child nodes lets you detect taps even when the user touches the label instead of the background shape.

Handle Touches and Dispatch Actions

Use touch location with nodes(at:), then walk up the node tree until you find a named button container. This prevents fragile hit logic tied to one node type.

swift
1import SpriteKit
2
3extension MenuScene {
4    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
5        guard let touch = touches.first else { return }
6        let point = touch.location(in: self)
7        let hitNodes = nodes(at: point)
8
9        for node in hitNodes {
10            if let buttonName = resolvedButtonName(from: node) {
11                runButtonAction(named: buttonName)
12                return
13            }
14        }
15    }
16
17    private func resolvedButtonName(from node: SKNode) -> String? {
18        var current: SKNode? = node
19        while let n = current {
20            if let name = n.name, name.hasPrefix("btn_") {
21                return name
22            }
23            current = n.parent
24        }
25        return nil
26    }
27
28    private func runButtonAction(named name: String) {
29        switch name {
30        case "btn_play":
31            print("Start game")
32        case "btn_settings":
33            print("Open settings")
34        default:
35            break
36        }
37    }
38}

This is simple, testable, and easy to extend with additional buttons.

Add Visual Feedback and Accessibility

Buttons should show pressed state so the interface feels responsive. You can scale or tint the button in touchesBegan and restore in touchesEnded or touchesCancelled. Keep feedback subtle and consistent.

swift
1override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
2    guard let touch = touches.first else { return }
3    let point = touch.location(in: self)
4    for node in nodes(at: point) {
5        if let name = resolvedButtonName(from: node), let target = childNode(withName: name) {
6            target.run(SKAction.scale(to: 0.96, duration: 0.05))
7        }
8    }
9}
10
11override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
12    enumerateChildNodes(withName: "btn_*") { node, _ in
13        node.run(SKAction.scale(to: 1.0, duration: 0.05))
14    }
15}

For larger projects, wrap this behavior in a ButtonNode subclass so scenes remain focused on game logic rather than input plumbing.

Scene Architecture Tips

As your game grows, route button actions through a small coordinator instead of embedding all behavior in one scene file. Keep rendering concerns separate from navigation and state transitions. This allows easier unit testing for action mapping and prevents long switch blocks from turning into fragile menu logic. A clear architecture also makes it safer to add tutorial overlays, disabled states, and platform-specific input paths without rewriting core touch handling.

Common Pitfalls

  • Assigning a name only to the background node, then missing taps on text nodes.
  • Putting scene transitions directly inside touch parsing code, which becomes hard to maintain.
  • Ignoring touchesCancelled, leaving buttons in a stuck pressed visual state.
  • Using exact node type checks instead of name based dispatch, which breaks when assets change.
  • Creating too many ad hoc actions per frame and causing avoidable animation overhead.

Summary

  • SpriteKit buttons are usually named nodes plus touch handling.
  • Reusable button builders keep menu code compact and consistent.
  • Resolve touches by walking parents and dispatch actions by button name.
  • Add pressed state feedback for clearer interaction.
  • Encapsulate button behavior early to keep scenes maintainable as complexity grows.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.