bitfool edited this page Jun 21, 2012 · 16 revisions

Welcome to the idiary wiki! This small documentation explains the basics of compiling, running and creating interactive books with the idiary framework. This documentation assumes that you are familiar with XCode, Objective-C and Cocos2D.

Compiling and running the example diary

Alice' diary

The source code is located under src and contains an XCode project file iDiary2.xcodeproj. Everything is ready to compile so you can start with the example diary Alice and play around with it.

Targets and schemes

For the example diary, a target named Alice was created. One needs a separate compile target for each diary/book application using this project. For each target there are two compile schemes in XCode: Release and Debug, respectively in this case Alice-Release and Alice-Debug. Use the first for distribution and the second one for testing.

Folder structure and naming conventions

The following gives an overview about the main folder structure. It is also reflected in the XCode project file groups.

_Documentation	- not much, yet. But everthing is in the wiki.
_Scripts		- contains the script for converting PSDs to code/sprites
src				- contains... the sources!
	iDiary2				- sources of the XCode project
		Audio			- Sound classes
		Ctrl			- Main controller classes
		libs			- Cocos2D, Box2D, etc.
		Model			- Data structure classes
		Pages			- Important! Pages for each diary/ebook
		Resources		- Important! Graphics, anim., videos for each diary/ebook
		Util			- Utility classes
		View			- Important! Classes for interactive objects on the pages (sprites, layers, etc.)
	iDiary2.xcodeproj	- XCode project file

The Pages and Resources folder

In the src/iDiary2/Pages folder, the implementations for each page of each diary/ebook are contained. The structure of the foldes and files is the following:


Important: Note that you should always use the file extension .mm for class implementations instead of .m because this causes XCode to compile the file as Objective-C++ file which is important because the code also includes C++-classes (for example from Box2D).

Also important: The class name should also be Page_<DIARY_NAME>_<PAGE_NAME>. See "Diary Definitions" and "Adding pages to a book" for more information.

In analogy to that, the resources for each page are located in the following folders:

Resources/PageContents/<DIARY_NAME>_<PAGE_NAME>/   - for page-specific content
Resources/PageContents/<DIARY_NAME>/               - for book-specific content

Diary definitions

Each book or diary has a special file diary definition file which is a header file containing information about which pages are available in which order for a book/diary. The file is located at Ctrl/DiaryDefinitions/Diaries_<DIARY_NAME>.h. For our example Alice, it is included with the compiler flag -DDIARIES_HEADER=\"Diaries_Alice.h\" in the Other C++ Flags setting of the XCode project build settings. For our example, the header file contains the following code:

static NSArray *diaryAlice = [NSArray arrayWithObjects:
  @"Intro",            // 0
  @"Example2",         // 1
  @"Example8",         // 7

So this array basically defines the order of the pages of a book/diary. The classes that implement a diary page are automatically instantiated by using this information. So the first page that is rendered in this case must be implemented in the class Page_Alice_Intro (in file Page_Alice_Intro.h/.mm). See "Adding pages to a book" on how to implement such a PageLayer class.

Creating a new book

When you want to create another book from the same sources, you don't need to copy the whole project and restart from scratch. Just create a new compile target and its compile schemes plus the needed folder structure and a diary definition and there you go.

Creating a new compile target Bob and its schemes

At first, you should duplicate the existing target Alice and rename it to Bob. Also rename the Product name in the Build settings of the target. Then you can rename the default build scheme and add a Bob-Release scheme if you want.

Creating a new diary definition

Change the compiler flag -DDIARIES_HEADER=\"Diaries_Alice.h\" to use the file Diaries_Bob.h in the Other C++ Flags setting of the target's build settings. Duplicate the Diaries_Alice.h file and rename it to Diaries_Bob.h. Change the content of the file to:

// These Arrays define the pages
static NSArray *diaryBob = [NSArray arrayWithObjects:
    @"Intro",            // 0
// This dictionary associates the diary-arrays with the persons
static NSDictionary *diaryPages = [NSDictionary dictionaryWithObjectsAndKeys:
    diaryBob, @"Bob",

// These Arrays define meta data for diaries, such as the position of the diary in the startscreen, page offset, anim corner size
static NSArray *metaDataBob = [NSArray arrayWithObjects:
    [NSValue valueWithCGPoint:ccp(395, 768-417)],       // position of the diary in the startscreen
    [NSValue valueWithCGPoint:ccp(-1.5, 0)],            // page offset
    [NSValue valueWithCGSize:CGSizeMake(242, 200)],     // page corner animation size
    [NSValue valueWithCGPoint:ccp(-1, 1)],              // page corner offset
    [NSValue valueWithCGPoint:ccp(0, 0)],       // disclamer coordinates

// This dictionary associates the diary-metadata-arrays with the persons    
static NSDictionary *diaryMetaData = [NSDictionary dictionaryWithObjectsAndKeys:
    metaDataAlice, @"Bob",

// global settings
static NSMutableDictionary *globalSettings = [NSMutableDictionary dictionaryWithObjectsAndKeys:
    [NSNumber numberWithBool:NO], @"controlElemShown",

Now we have a diary definition that tells the system to load a class Page_Bob_Intro as the first page of Bob's diary. We will implement this class later.

Setting up the folder structure

We basically need to have a folder for our page class implementations and one for the resources that are used on the pages. The first, our implementations, should go in a folder Pages/Bob. Create it and add it to the XCode project. For our resources we create the folders Resources/PageContents/Bob and Resources/PageContents/Bob_Intro. The first for general resources for all of Bob's pages and the latter for Bob's first diary page. Add both of them to the XCode project.

Adding pages to a book

Now we will set up our first page for Bob: Bob_Intro. We defined Intro to be the first of Bob's pages in the diary definition Diaries_Bob.h before (under "Creating a new diary definition"). At first we create a new class with XCode inside the previously created folder/group Pages/Bob/ and name it after the convention Page_<DIARY_NAME>_<PAGE_NAME> Page_Bob_Intro. Take care that the file is associated with the compile target Bob. A valid diary page must be a subclass of PageLayer and thus the header for our freshly created page looks like this:

#import "PageLayer.h"

@interface Page_Bob_Intro : PageLayer {



To display something fancy on the page, it is wise to have a look at the example pages that are shipped with the code. For Bob's first page, we just display a small text:

#import "Page_Bob_Intro.h"

@implementation Page_Bob_Intro

- (id)init {
    self = [super init];
    if (self) { // we don't do custom initializations for this simple page
    return self;

- (void)dealloc {
    // we didn't allocate anything, so we don't need to clean anything up
    [super dealloc];

// This is where we can define the page's contents
- (void)loadPageContents {
    // set individual properties
    pageBackgroundImg = @"alice_seiten_hintergrund.png"; // define the background page for this page
    // add individual media objects for this page here
    MediaDefinition *mDefWelcomeText = [MediaDefinition mediaDefinitionWithText:@"Hello, I'm Bob!"
                                                                           font:@"Courier New"
                                                                         inRect:CGRectMake(60, 700, 350, 100)];
    [mediaObjects addObject:mDefWelcomeText];

    // common media objects will be loaded in the PageLayer
    [super loadPageContents];


iDiary often makes use of inheritance. In this example the most important method is the PageLayer's loadPageContents method which defines the content that is displayed on the page. This is done by creating MediaDefinitions and adding them to PageLayer's mediaObjects array.

MediaDefinitions and coordinates

A MediaDefinition object contains information about a single content item on a page. The following properties are stored in such an object:

  • type - NSNumber with media type which can be one of the following:
  • MEDIA_TYPE_PICTURE - an image that is rendered as sprite
  • MEDIA_TYPE_AUDIO - an audio object that is played when touching a thumbnail
  • MEDIA_TYPE_VIDEO - a video object that is played when touching a thumbnail
  • MEDIA_TYPE_TEXT - a text that is rendered as sprite
  • MEDIA_TYPE_ANIM - a sprite animation
  • value - NSString with file name or text
  • attributes - NSMutableDictionary with different properties depending on the media type. common properties are:
  • posX
  • posY
  • sizeW
  • sizeH
  • isInteractive
  • isMovable

To create a MediaDefinition object, there are several initializers and static constructors defined in the class' interface. Common parameters for these constructors are often:

  • value - file name or text (for MEDIA_TYPE_TEXT)
  • rect - rectangle that describes the centered x- and y-coordinates of the object and the dimensions
  • The coordinates are in Cocos2d screen space from 0,0 (lower left) to 1024,768 (upper right) on an iPad 1 and 2

Here are some examples on how to create different MediaDefinition objects for different types of content:

    // create black text:
    MediaDefinition *text = [MediaDefinition mediaDefinitionWithText:@"Black text."
                                                                font:@"Courier New" fontSize:18
                                                              inRect:CGRectMake(60, 660, 350, 100)];

    // create an image:
    MediaDefinition *img = [MediaDefinition mediaDefinitionOfType:MEDIA_TYPE_PICTURE
                                                           inRect:CGRectMake(231, 422, 200, 201)];

    // create an video:
    MediaDefinition *vid = [MediaDefinition mediaDefinitionWithVideo:@"alice_example_video.mov"
                                                              inRect:CGRectMake(750, 400.5, 76, 77)];

    // create a "progress image" that is gradually displayed in a certain direction
    MediaDefinition *progImg = [MediaDefinition mediaDefinitionWithProgress:@"alice_example2__silhouette.png"
                                                                     inRect:CGRectMake(500, 98, 970, 86)
                                                                   duration:2.0 // takes 2 sec. to build up
                                                                 startTime:1.0]; // start delay

These are some examples of which kinds of media you can embed in a page. There are more examples in the provided example book Alice' diary.

Generating page code and graphics from a Photoshop PSD file

It is often very time-consuming to arrange all images and objects on a page by using coordinates in the rect parameter. Using the Photoshop-script in _Scripts/export_layers_png24.jsx can save a lot of time. If you have designed a page in Photoshop you can load this script by running it via File > Script > Browse... in Photoshop. It will produce a series of PNG images with one image per layer. Additionally it will output a file index.txt containing the code that you can copy and paste into the loadPageContents method of a diary page implementation. The code contains a list of MediaDefinition instructions with all coordinates and image dimensions.

Important: You will need to change the dimension units in Photoshop settings to pixels to get the right coordinates in the produces code.

Also important: If you want nice file names, put all layers in your Photoshop file in a layer folder that is named for example bob_intro. This will be used as a file prefix. Please note that due to an error only one layer folder can be used otherwise the PNG output becomes strange.