How to Create an Interactive Children’s Book for the iPad

Learn how to create your own beautiful animated interactive children’s book for the iPad! By Tammy Coron.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 5 of this article. Click here to view the first page.

Adding Sound to Your Story

There are several methods to add music and other sounds to your book, but in this tutorial you’ll just focus on two methods, one for adding the background music, and the other for adding the narration and sound effects.

Head back to Scene00.m and add the following import statement:

@import AVFoundation;

Add the following private instance variables to Scene00.m at the spot indicated by the comment /* set up your instance variables here */:

AVAudioPlayer *_backgroundMusicPlayer;
SKSpriteNode *_btnSound;
BOOL _soundOff;

Next, add the following two lines of code just before the line that creates background in locate initWithSize::

_soundOff = [[NSUserDefaults standardUserDefaults] boolForKey:@"pref_sound"];
[self playBackgroundMusic:@"title_bgMusic.mp3"];

This code retrieves the BOOL value that represents the user’s preference for having the sound on or off and assigns it to the _soundOff instance variable.

Now add the following block of code to playBackgroundMusic::

NSError *error;
NSURL *backgroundMusicURL = [[NSBundle mainBundle] URLForResource:filename withExtension:nil];
_backgroundMusicPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:backgroundMusicURL error:&error];
_backgroundMusicPlayer.numberOfLoops = -1;
_backgroundMusicPlayer.volume = 1.0;
[_backgroundMusicPlayer prepareToPlay];

playBackgroundMusic: plays the background music using a AVAudioPlayer object.

Note: AVAudioPlayer isn’t specific to Sprite Kit, so it won’t be covered in detail here. For more detail on AVAudioPlayer, check out the Audio Tutorial for iOS.

Okay — you have some code to handle the user preference for enabling or disabling sound in your book. You should probably add a control to let the user set this preference! :]

Still working in Scene00.m, add the code below to setUpSoundButton:

if (_soundOff)
{
  // NSLog(@"_soundOff");
		
  [_btnSound removeFromParent];
        
  _btnSound = [SKSpriteNode spriteNodeWithImageNamed:@"button_sound_off"];
  _btnSound.position = CGPointMake(980, 38);
       
  [self addChild:_btnSound];
  [_backgroundMusicPlayer stop];
}
else
{
  // NSLog(@"_soundOn");

  [_btnSound removeFromParent];
        
  _btnSound = [SKSpriteNode spriteNodeWithImageNamed:@"button_sound_on"];
  _btnSound.position = CGPointMake(980, 38);

  [self addChild:_btnSound];
  [_backgroundMusicPlayer play];
}

The above code sets the texture of the _btnSound SKSpriteNode to the appropriate image depending on the user’s stored preference. It then checks _soundOff and, regardless of its value, removes the sprite from the screen and initializes a new sprite with the appropriate on or off image. Then it sets the sprite’s position, adds it to the parent view, and finally plays or stops the music depending on the user’s preference.

There are a number of ways to handle changing a sprite’s image. The above method is only one way to accomplish this; the next section shows you another way to manage sprite images. The next section shows you another way to accomplish the same thing.

For now, build and run the app, and the background music should play. Tapping the button won’t do anything yet though.

Detecting Touch Events

The reason you can’t currently toggle the control is that the sprite doesn’t have any touch event handlers. So let’s add that next.

Add the following code to touchesBegan:withEvent: in Scene00.m:

/* Called when a touch begins */
for (UITouch *touch in touches)
{
  CGPoint location = [touch locationInNode:self];
  // NSLog(@"** TOUCH LOCATION ** \nx: %f / y: %f", location.x, location.y);

  if([_btnSound containsPoint:location])
  {
    // NSLog(@"xxxxxxxxxxxxxxxxxxx sound toggle");

    [self showSoundButtonForTogglePosition:_soundOff];
  }
}

touchesBegan:withEvent: is part of the UIResponder class which all SKNodes extend. It tells the receiver when one or more fingers begin touching the view. In this method you’re able to capture the location of the touch and respond to it accordingly.

Note: As every SKNode extends UIResponder, you can implement touch handling directly in your sprites. This tutorial performs all touch event handling in the scene nodes simply to keep the number of classes more manageable. However, in a production app it would likely make more sense to create an SKSpriteNode subclass, such as SoundToggleButton, and have that subclass handle its own touch events.

Look at the if statement above; you’ll see that if the touch location is on the sound button, you call showSoundButtonForTogglePosition:.

Now would be a great time to write that method! :]

Add the following code to showSoundButtonForTogglePosition: in Scene00.m:

// NSLog(@"togglePosition: %i", togglePosition);
		
if (togglePosition)
{
  _btnSound.texture = [SKTexture textureWithImageNamed:@"button_sound_on"];
        
  _soundOff = NO;
  [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"pref_sound"];
  [[NSUserDefaults standardUserDefaults] synchronize];
        
  [_backgroundMusicPlayer play];
}
else
{
  _btnSound.texture = [SKTexture textureWithImageNamed:@"button_sound_off"];
        
  _soundOff = YES;
  [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"pref_sound"];
  [[NSUserDefaults standardUserDefaults] synchronize];
        
  [_backgroundMusicPlayer stop];
}

The method above sets the appropriate texture for the SKSpriteNode that is held in _btnSound — much as you did in setUpSoundButton. Once the texture has been set, you can store the user’s selection and either play or stop the music accordingly.

Note: An SKTexture object stores a reference to the graphical data in memory that a sprite renders.

Build and run, and now you should be able to tap the sound button to turn the background music on and off.

Adding the Next Scene

Add the following import to the top of Scene00.m:

#import "Scene01.h"

You’ll need this to present the next scene; this is Page 1 of the book and is controlled by the Scene01 class.

Still working in Scene00.m, add the following code to setUpBookTitle just after the line that creates actionMoveDownFast, but before the line that calls runAction: on bookTitle:

SKAction *wait = [SKAction waitForDuration:0.75];
SKAction *showButton = [SKAction runBlock:^{
     
  SKSpriteNode *buttonStart = [SKSpriteNode spriteNodeWithImageNamed:@"button_read"];
  buttonStart.name = @"buttonStart";
        
  buttonStart.position = CGPointMake(425,460);
  [self addChild:buttonStart];
        
  [buttonStart runAction:[SKAction playSoundFileNamed:@"thompsonman_pop.wav" waitForCompletion:NO]];
}];

The code above creates a new action block which creates a sprite for the start button and plays a sound. By putting the code to create the sprite and play a sound inside an action, you can easily make this code run in a particular point in a sequence of actions.

As you may have figured out by now, there is usually more than one way to skin a cat — or code your children’s book!

Replace the line in setUpBookTitle that calls runAction: on bookTitle with the following code:

[bookTitle runAction:[SKAction sequence:@[actionMoveDown, actionMoveUp, actionMoveDownFast, wait, showButton]]];

This simply adds the new action to the sequence that bookTitle runs when called.

The next step is to modify the touch handling in touchesBegan:withEvent:.

Add the following line to the top of touchesBegan:withEvent:

SKNode *startButton = [self childNodeWithName:@"buttonStart"];

This assigns the button – added earlier in the showButton action – to the node.

Add the following else if clause after the if statement in touchesBegan:withEvent::

else if([startButton containsPoint:location])
{
  [_backgroundMusicPlayer stop];

  // NSLog(@"xxxxxxxxxxxxxxxxxxx touched read button");
          
  Scene01 *scene = [[Scene01 alloc] initWithSize:self.size];
  SKTransition *sceneTransition = [SKTransition fadeWithColor:[UIColor darkGrayColor] duration:1];
  [self.view presentScene:scene transition:sceneTransition];
}

The above code adds a new scene and presents it with a new transition when it detects a user tap on the “Read Story” button.

Build and run your project. After the initial animation, you should see the new “Read Story” button, as shown below:

tc_spritekit_build3

Tap the “Read Story” button and the next scene presents itself on-screen. You won’t see a whole lot at this point — your next task is add some content to the first page.