Thursday, July 30, 2015

Word Highlighting in a read-to-me app

One of the common use case while developing a read-to-me book apps is highlight the word as it is read to the user. In this example we discuss how we can achieve it. The source code is at https://github.com/cspnanda/TextHighlight

We are going to use a UIWebView for this. UIWebView uses HTML so can be easily styled. I found it easier than using the NSAttributedString with a TextView. For this purpose we will need a audio file and a file containing beginning time offset and lengths of each word. 

The text we are going to display is "The quick brown fox jumps over the lazy dog". In the offset file you will need 9 value pairs, one for each word. I really want to know if there is a better way to solve it, but did not find any. To easily find the time offset, you can open your sound file in Audacity. The words are waveforms and the pause between the words are flat.



Then add these values to a plist file. The plist file now looks like
<dict>
        <key>offset</key>
        <array>
                <array>
                        <string>The</string>
                        <real>0.019</real>
                        <real>0.218</real>
                </array>
                <array>
                        <string>quick</string>
                        <real>0.237</real>
                        <real>0.283</real>
                </array>
                <array>
                        <string>brown</string>
                        <real>0.52</real>
                        <real>0.382</real>
                </array>

Then we need to have a timer which will fire at those specific intervals.

[NSTimer scheduledTimerWithTimeInterval:[[thisWord objectAtIndex:2] floatValue]
     target:self
     selector:@selector(highlightText)
     userInfo:nil
     repeats:NO];

In the highligtText function we have to set all the words except the current one in normal color and highlight the current word.

htmlString = [htmlString stringByAppendingString:@"<span class='highlight'> "];
htmlString = [htmlString stringByAppendingString:[words 
              objectAtIndex:currentWord]];
htmlString = [htmlString stringByAppendingString:@"</span> "];

Then we invalidate the current timer and schedule for the next word.

[webView loadHTMLString:htmlString baseURL:nil];
currentWord++;
[timer2 invalidate];
timer2 = Nil;
timer2 = [NSTimer scheduledTimerWithTimeInterval:([[thisWord objectAtIndex:2] floatValue])
    target:self
    selector:@selector(highlightText)
    userInfo:nil
    repeats:NO];

Now when you run the project, you get an output like


video



Sunday, July 5, 2015

Water fall animation using Particle System

Water falling is a cool piece of animation in iOS apps. I had to use particle system to create an animation where water hose is spraying water. Think of the real water hose.

1. You have a continuous flow of water which are collection of water drops. 
2. The shape in which water falls (you may have seen the settings on the hose - shower, mist, full etc).

The above two concepts translate exactly to the concepts of emitter cell and an emitter layer.




There is one more thing: 

In the real world, water will always fall down because of gravity. But in programming world water direction need to be controlled. This is controlled by a property called emissionLongitude.






The complete code is checked into https://github.com/cspnanda/ParticleSystem
Official apple API docs are here and here

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// Initialize the Emitter Cell. drop is a simple picture of water. From apple doc
// A layer can set this property to a CGImageRef to display the image as its contents.

    CAEmitterCell *emitterCell = [CAEmitterCell emitterCell];
    emitterCell.contents = (id) [[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"drop" ofType:@"png"]] CGImage];

    // Define the velocity range - The amount by which the velocity of the cell can vary. 
    emitterCell.velocityRange = 1;

    // The angle in which water will fall
    emitterCell.emissionRange = M_PI_2;

    // Set the x and y component of an acceleration vector
    emitterCell.xAcceleration=-10;
    emitterCell.yAcceleration=300;

    // Set the emissionLongitude
    emitterCell.emissionLongitude = longitude;


// Constructing emitterLayer

// Initialize the Layer
emitterLayer = (CAEmitterLayer *) self.layer;
// Set the emitter cell as part of emitterLayerContents
emitterLayer.emitterCells = [NSArray arrayWithObject: emitterCell];

// Add the emitter Layer to main view
[self.view addSubview:waterFall];

Voila. Now when you compile the code and run, you should see water hose spraying water.

Let us look at two properties of emitter cell. xAcceleration and yAcceleration. As the name xAcceleration is how the water travels in X axis and yAcceleration is how water travels in Y axis. In the segment control when we set the yAcceleration to 0 we see no particles fall in Y axis.