views:

46

answers:

2

My app is retrieving text from a web service in chunks. I am displaying the formatted text using a UIWebView. So far so good.

My problem is as each chunk arrives, I need to refresh the web view with the new content. I am using KVO to observe changes in the text, and when the text changes I'm calling

[self loadHTMLString:self.log.text baseURL:nil];

to redisplay the page with the updated text. Unfortunately, this reloads the whole page, resetting the users current position and zoom.

Is there any way to either request the web view to simply refresh the text (assuming it retains it), or to inject new next/html into the current web view?

+1  A: 

You can inject new content into a page using

(NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script

and a javascript function of some sort (like maybe innerHTML) to add content to the HTML.

Unfortunately I'm not enough of a javascript expert to provide an example for the javascript part. Anyone?

Nimrod
I don't know much javascript myself, but I have a few people around me who do. I'll have to modify my model a bit to make this work, but it'll probably be for the better anyway.
Ashton
When you find out what it is, please post it as a comment here. I'd be interested in knowing because I'll probably want to use this at some point.
Nimrod
I've posted my code in an answer below, with only minor modifications.
Ashton
Thanks for posting that!
Nimrod
+1  A: 

Ok, using Nimrod's suggestion, here is how I did it:

//Called on first display. Sets up basic page, and a javascript function to
//next log segments at the end of the a div
- (void) preparePage{
    NSString *basePage = 
        @"<html><body>"
        "<script type=\"text/javascript\">"
            "function appendMessage(msg) {"
                "var msgNode = document.createTextNode(msg);"
                "var logNode = document.getElementById(\"log\");"
                "logNode.appendChild(msgNode); }"
        "</script>"       
        "<div id=\"log\" style=\"width:80em; white-space:pre-wrap;\"></div>"
        "</body></html>";
    [self.logView loadHTMLString:basePage baseURL:nil];
    [self updateHTMLFromLog];
}

//Called when the log has changed. Keeps track of current position, and inserts
//new segments into the page using the javascript function created in preparePage
- (void) updateHTMLFromLog{
    // loop through new log chunks, using Javascript to inject them into the page
    while(self.logPosition < [self.log.segments count]){
        //escape double quotes to avoid breaking string
        NSString *message = [self.log.segments objectAtIndex:self.logPosition] stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
        NSString *jsString = [NSString stringWithFormat:@"message = \"%@\";appendMessage(message);", message];
        [self.logView stringByEvaluatingJavaScriptFromString:jsString];
        ++self.logPosition;
    }
}

I've made a few minor changes to my code for presentation, but it is basically the same as I'm using. The HTML string in preparePage could be moved out for production code.

Edit: a change I've made since which might be useful to others is modifying appendMessage(msg) to create new div elements, instead of text nodes. This is useful if the text you are injecting includes HTML that you want to preserve.

function appendMessage(msg) {
    var msgNode = document.createElement('div');
    msgNode.innerHTML = msg;
    var logNode = document.getElementById('log');
    logNode.appendChild(msgNode);
}
Ashton