How to Make a Narrated Book Using AVSpeechSynthesizer in iOS 7

Learn how to make Siri read you a bedtime story to you by using one of iOS 7’s newest features: AVSpeechSynthesizer. By .

Leave a rating/review
Save for later
Share

Your UI With Speech Control Buttons

Narrated Book with Speech Control

Your UI With Speech Control Buttons

With the introduction of Siri, Apple has taunted developers with the implication of dynamic spoken text, but with the release of iOS 7, Apple has finally opened the door.

Introducing, AVSpeechSynthesizer. Or Siri-Synthesizer for short :]

In this tutorial, you’ll create a narrated book. Each page of the book will display text while simultaneously speaking the text. Audio narration is a splendid way to make your book app stand out from all the others on iTunes, while also accommodating those with visual impairments. Offering an audio book app can also make your work more appealing to a broader audience, since they allow people to “read” while they exercise, cook or get a little work done.

As you create your narrated book, you’ll learn:

  • How to make an iOS device speak text using AVSpeechSynthesizer and AVSpeechUtterance.
  • How to make this synthesized speech sound more natural by modifying AVSpeechUtterance properties like pitch and rate.

AVSpeechSynthesizer may not win any awards for voice acting, but you can use it relatively easily to enhance functionality in apps you develop in the future.

Note: If you are interested in developing children’s books on the iPad using Sprite Kit, check out Tammy Coron’s excellent tutorial over here: How to Create an Interactive Children’s Book for the iPad

Note: If you are interested in developing children’s books on the iPad using Sprite Kit, check out Tammy Coron’s excellent tutorial over here: How to Create an Interactive Children’s Book for the iPad

Getting Started with AVSpeechSynthesizer

Start by downloading the Starter Project. Open the project in Xcode by navigating into the NarratedBookUsingAVSpeech Starter directory and double-clicking on the NarratedBookUsingAVSpeech.xcodeproj project file. You should see something similar to the image below:

First Open in Xcode

Build and run the project. You will see the following in the simulator:

First Run of Your App Page 1 - After Swiping Left-to-right

Your first book is a nursery rhyme about squirrels. It’s not exactly Amazon Top Selling material, but it will do for the purposes of this tutorial. Use your mouse to swipe from right-to-left in the simulator, and you’ll move to the next page as below.

Second Page of App

Use your mouse to swipe from left-to-right in the simulator, and you’ll return to the first page. Wow, you already have a functioning book. Nice work!

Understanding the Plumbing

Note: At the end of this tutorial, there are a few challenges for you. This next section covers the sample project so you can take those challenges, but if you are not interested, feel free to skip to the next section.

Note: At the end of this tutorial, there are a few challenges for you. This next section covers the sample project so you can take those challenges, but if you are not interested, feel free to skip to the next section.

The starter project has two important classes:

1. Models: These store your book as a single Book object and its collection of Page objects.
2. Presentation: These present your models on the screen and respond to user interaction (e.g. swipes).

If you choose to build on this project to make your own books, its important you understand how these work. Open RWTBook.h and examine its structure.

@interface RWTBook : NSObject

//1
@property (nonatomic, copy, readonly) NSArray *pages;

//2
+ (instancetype)bookWithPages:(NSArray*)pages;
//3
+ (instancetype)testBook;

@end
  1. The pages property stores an array of Page objects, each representing a single page in the book.
  2. bookWithPages: is a convenience method to initialize and return a book with the given array of Page objects.
  3. testBook creates your book for testing purposes. You’ll start writing and reading your own books soon enough, but testBook is a simple book that is perfect to get you started.

Open RWTPage.h and examine its structure.

//1
extern NSString* const RWTPageAttributesKeyUtterances;
extern NSString* const RWTPageAttributesKeyBackgroundImage;

@interface RWTPage : NSObject

//2
@property (nonatomic, strong, readonly) NSString *displayText;
@property (nonatomic, strong, readonly) UIImage *backgroundImage;

//3
+ (instancetype)pageWithAttributes:(NSDictionary*)attributes;
@end
  1. Accesses the constants for dictionary look-ups for each page. The RWTPageAttributesKeyUtterances constant corresponds to the text on each page of the book. The RWTPageAttributesKeyBackgroundImage constant returns each background image for the page
  2. The displayText property stores the text of the page that your book presents on-screen, and the backgroundImage stores the image behind the text.
  3. pageWithAttributes: initializes and returns a page with the given dictionary of attributes.

Finally, open RWTPageViewController.m and examine its structure:

#pragma mark - Class Extension

// 1
@interface RWTPageViewController ()
@property (nonatomic, strong) RWTBook *book;
@property (nonatomic, assign) NSUInteger currentPageIndex;
@end

@implementation RWTPageViewController

#pragma mark - Lifecycle

// 2
- (void)viewDidLoad
{
  [super viewDidLoad];

  [self setupBook:[RWTBook testBook]];

  UISwipeGestureRecognizer *swipeNext = [[UISwipeGestureRecognizer alloc]
                                          initWithTarget:self
                                                  action:@selector(gotoNextPage)];
  swipeNext.direction = UISwipeGestureRecognizerDirectionLeft;
  [self.view addGestureRecognizer:swipeNext];

  UISwipeGestureRecognizer *swipePrevious = [[UISwipeGestureRecognizer alloc]
                                              initWithTarget:self
                                                      action:@selector(gotoPreviousPage)];
  swipePrevious.direction = UISwipeGestureRecognizerDirectionRight;
  [self.view addGestureRecognizer:swipePrevious];
}

#pragma mark - Private

// 3
- (RWTPage*)currentPage
{
  return [self.book.pages objectAtIndex:self.currentPageIndex];
}

// 4
- (void)setupBook:(RWTBook*)newBook
{
  self.book = newBook;
  self.currentPageIndex = 0;
  [self setupForCurrentPage];
}

// 5
- (void)setupForCurrentPage
{
  self.pageTextLabel.text = [self currentPage].displayText;
  self.pageImageView.image = [self currentPage].backgroundImage;
}

// 6
- (void)gotoNextPage
{
  if ([self.book.pages count] == 0 || self.currentPageIndex == [self.book.pages count] - 1) {
    return;
  }

  self.currentPageIndex += 1;
  [self setupForCurrentPage];
}

// 7
- (void)gotoPreviousPage
{
  if (self.currentPageIndex == 0) {
    return;
  }

  self.currentPageIndex -= 1;
  [self setupForCurrentPage];
}
@end

Here’s what this code does:

  1. The book property stores the current book and, the currentPageIndex property stores the index of the current page in book.pages.
  2. Sets up the page display once your view loads, then adds gesture recognizers so you can swipe forwards and backwards through the book’s pages.
  3. Returns the current page within the current book.
  4. Sets the book property and makes sure you start at the first page.
  5. Set up the UI for the current page.
  6. Go to the next page, if applicable, and set it up. It’s invoked by the swipeNext gesture recognizer you created in viewDidLoad.
  7. Go to the previous page, if there is one, and set it up. This is invoked by the swipePrevious gesture recognizer you created in viewDidLoad.