I am trying to get a UIWebView to update dynamically, sort of like a karaoke box, as it iterates through a while loop. The word that the tts engine is speaking should be in orange (or in html, .style2).
What is happening, however, is that the UIWebView doesn’t update its display through each iteration of the htmlKaraoke string, but only the last one in the while loop. UIWebView appears to be reloading in a lazy way, and waiting until the while loop is finished before updating it’s display. I’ve included in my code all the different ways I’ve tried to circumvent this (setNeedsDisplay, setNeedsLayout, reseting the htmlKaraoke string, reloading the UIWebView) but I can’t seem to get it to do what I want.
The html code is updating like it should (with the spoken word in orange), but the webview isn’t reflecting this.
How can I get the webView to update after every update of the htmlKaraoke string?
i = 0;
NSString *htmlHead = @"<html><head><style type='text/css'><!-- .style1 { font-size: 24px; font-weight: bold; font-family: Helvetica; } .style2 {color: #FF9900} --> </style></head><body><span class='style1'>";
NSString *htmlFoot = @"</span></body></html>";
NSString *htmlEmpHead = @"<span class='style2'>";
NSString *htmlEmpFoot = @"</span>";
NSMutableString *htmlKaraoke = [[NSMutableString alloc] init];
while(i <= wordWeAreUpToInt){
//wait until the tts is finished talking.
if(![[fliteEngine audioPlayer] isPlaying]){
//NSLog(@"flite is finished!");
//Add the word and highlight in the karaoke
/* Clear the html string
Make a for loop, where n = 0, n<i, n++
append black word
when for loop ends, and n == i, add an emph word (n==i when i is the currently spoken word).
*/
[karaokeWebView loadHTMLString:@" " baseURL:nil];
[karaokeWebView reload]; //doesn't do anything. These two lines are a waste of breath.
[htmlKaraoke setString:htmlHead];
for (int n = 0; n<i; n++) {
[htmlKaraoke appendString:[wordsArray objectAtIndex:n]];
[htmlKaraoke appendString:@" "];
}
[htmlKaraoke appendString:htmlEmpHead];
[htmlKaraoke appendString:[wordsArray objectAtIndex:i]];
[htmlKaraoke appendString:@" "];
[htmlKaraoke appendString:htmlEmpFoot];
NSLog(@"Emphasis: %@", [wordsArray objectAtIndex:i]);
[htmlKaraoke appendString:htmlFoot];
NSLog(@"htmlKaraoke: %@", htmlKaraoke);
[karaokeWebView loadHTMLString:htmlKaraoke baseURL:nil];
[karaokeWebView setNeedsDisplay];
[karaokeWebView setNeedsLayout]; //getting desperate....!
//speak the word
[self readSentence:[wordsArray objectAtIndex:i]];
i++;
}
}
[htmlKaraoke release];
wordWeAreUpToInt++;
}
I think your problem is that UIWebView only renders the view after control returns back to the main NSRunLoop / Event Loop. So, if you’re making all of your updates within the while loop above, then only the last string is getting loaded as it never got the chance to load the other ones (& so will just use the last load request).
You will need to return out of any code you’ve written (not just this method, but also whatever triggered this being called, etc.) and return control back to the OS, before the UIWebView will update with your changes.
Assuming that whatever object this code is in is still around after control returns, you could test this out by adding this to your class:
And then change your
-[loadHTMLString:baseURL:]calls into:There’s more on the main “Event Loop” in the “Application Life Cycle” section of the “iOS Application Programming Guide”.