9

I am trying to create a karaoke-like application but I'm facing a problem.

I have a song as an mp3 and the corresponding lyrics shown in a UITextView. I want to highlight/underline (or something like this) the words that are heard from that mp3 file. I have the corresponding timing for each word (startTime, endTime, duration) but I have no idea how to get them highlighted.

I've searched on Stack Overflow but none of the already posted questions solved my problem.

I've seen that UITextView could be inappropriate for my requirement, but I have no idea what else to use.

I've seen something like this presented at WWDC 2011: a Core Animation "Bouncing Ball" demo, but I couldn't manage to implement it.

Please help me find a way to do this.

Carl Veazey
  • 18,392
  • 8
  • 66
  • 81
stonycis
  • 466
  • 4
  • 16
  • That may be possible with a single-line UILabel, with a colored view behind it, but I don't think you can do that with multiple lines... – jjv360 Sep 04 '12 at 20:47
  • ok, but this means the whole line will be highlighted or only those that overlay the view...I practically want to highlight each word for a specific time to match with the song and I think using a view and uilabel is not a very efficient way to do it... – stonycis Sep 04 '12 at 20:53
  • Yes, but you could write a function to work out the rect of the word, and then set the highlight view to that rect, and even animate it... Or you could wait for iOS 6, since it supports NSAttributedString... – jjv360 Sep 04 '12 at 20:57
  • how could I get the position of the corresponding word dynamically? – stonycis Sep 04 '12 at 20:59
  • if you watch the video under Core Animation Essentials ( https://developer.apple.com/videos/wwdc/2011/ ) around minute 29:30 they present a demo that does exactly what I need, but I couldn't find the sample code anywhere and also I didn't manage to implement it by myself unfortunately :( – stonycis Sep 04 '12 at 21:04
  • Someone actually did this [here](https://gist.github.com/1278483). You could get the rect of the first char of the word and the last, then create a rect that starts at the first one and is the width of the second one minus the first – jjv360 Sep 04 '12 at 21:10
  • `wordRect = CGRectMake(rect1.origin.x, rect1.origin.y, (rect2.origin.x - rect1.origin.x) + rect2.size.width, rect2.size.height);`... I think... :) – jjv360 Sep 04 '12 at 21:22
  • did you find a solution to do this? if so please share it :) thank you – Ushan87 Jul 10 '13 at 11:05
  • To solve this problem, I have used an UIWebView to show the lyrics and using a timer I have highlighted certain words with javascript. Just search for "uiwebview text highlighting using javascript" and you will find some tutorials. – stonycis Jul 10 '13 at 21:10

2 Answers2

7

As I see it you have two options:

1. Core Text Attributes

With Core Text, you can underline words and apply many other ornaments to them. Here's an example for underlining text:

//Create a font
CTFontRef sysUIFont = CTFontCreateUIFontForLanguage(kCTFontSystemFontType,
    24.0, NULL);

//Create a string
NSString *aString = @"Random text";

//Choose the color
CGColorRef color = [UIColor blueColor].CGColor;

//Single underline
NSNumber *underline = [NSNumber numberWithInt:kCTUnderlineStyleSingle];

//Pack the attributes into a dictionary
NSDictionary *attributesDict = [NSDictionary dictionaryWithObjectsAndKeys:
    (id)sysUIFont, (id)kCTFontAttributeName,
    color, (id)kCTForegroundColorAttributeName,
    underline, (id)kCTUnderlineStyleAttributeName, nil];

//Make the attributed string
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:string
    attributes:attributesDict];

Now this works fine for underlining text, but I'm not sure how to animate the underline moving from word to word as the lyrics progress.


2. Custom UIView Underline

Now your second option is to create a custom underline class that subclasses UIView. This would be easy to animate. You don't even have to create a separate class for the underline (though I recommend it); you could just create a UIView and animate it moving from word to word with an animation like the following:

CABasicAnimation *underlineMove = [CABasicAnimation animationWithKeyPath:@"position"];
underlineMove.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
endPosition = CGPointMake((float)nextWord.x, (float)nextWord.y);
underlineMove.toValue = [NSValue valueWithCGPoint:endPosition];
underlineMove.duration = currentWord.duration;
[underlineView addAnimation:underlineMove forKey:@"animation"];
pasawaya
  • 11,515
  • 7
  • 53
  • 92
  • how could I get the position of the next word dynamically? – stonycis Sep 04 '12 at 21:10
  • @stonycis - I'm not completely sure how to do this. You might have to manually place the text at a specified point and store this point to then pass into the animation. – pasawaya Sep 04 '12 at 23:09
  • @stonycis, I think you probably need to construct an array of ranges for each word in your lyrics, and loop through them at the appropriate times (the array would actually have to contain strings which you can get from NSStringFromRange() since you can't put ranges in an array). You could break your lyric string into individual words using the NSString methods, componentsSeparatedByString: or componentsSeparatedByCharactersInSet:. Loop through that array using rangeOfString: to get the range in your original lyric string, and add those ranges (converted to strings) to your array. – rdelmar Sep 04 '12 at 23:38
2

I found that the easiest way to do this would be using a uiwebview, insert the text in html format and underline/highlight it using javascript...hope it helps to everyone who want to do this :)

stonycis
  • 466
  • 4
  • 16