简体   繁体   中英

UIWebview Screen Shot after executing injected JavaScript on the page

What is the best approach for handling the following. I have an HTML page with some JavaScript which I load into UIWebview control. I would like to invoke a js function on the page which mutates the DOM, and after the DOM mutation I would like to capture a screenshot of the webpage. The problem I am facing is that the image captured from the UIWebview does not represent the current state of the html page. It represents the state of the DOM prior to the invocation of the [myWebview stringbyevaluatingjavascriptfromstring:@"myHandle.func()"]; from below.

[myWebview stringbyevaluatingjavascriptfromstring:@"myHandle.func()"];
UIGraphicsBeginImageContext(myWebview .bounds.size);
[myWebview .layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

I believe that the problem is related to the fact that the Native UI updates are not updated until the end of the current Run Loop. Therefor the UIWebView does not reflect the current state of the DOM at the point of screen capture.

I have managed to get the functionality I seek working via the following two approaches. Calling both immediately after

[myWebview stringbyevaluatingjavascriptfromstring:@"myHandle.func()"];

1.

[self performSelector:@selector(createWebviewImage) withObject:nil afterDelay:0]

where createWebViewImage simply holds the following logic

UIGraphicsBeginImageContext(myWebview .bounds.size);
[myWebview .layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext(); 

2.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{    
   // Back to the main thread for a chunk of code
   dispatch_sync(dispatch_get_main_queue(), ^{
       [self createWebviewImage];
   });
});

Theses two methods do work in my small test case although I am extremely apprehensive about using them as I am afraid of a case where they may fail.

Thanks to question https://stackoverflow.com/a/26301724/680778

Below is the best approach I have been able to come up with

CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAfterWaiting, NO, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity)
{

     [self createWebviewImage];


});
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);

This approach adds an observer to the Main Run loop to be executed during the kCFRunLoopAfterWaiting stage which as mentioned in the reference all UI operations execute during the kCFRunLoopBeforWaiting stage. Hope this helps someone else.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM