My App has one ViewController, one UIWebView and 4 UIImageView
The control flow in my app:
-> Capture a Image from camera and store into UIImage variable, this happens in didFinishPickingMediaWithInfo function. Do some processing on the Image.
-> From didFinishPickingMediaWithInfo function, load UIWebView with an inline html/css code i.e.
webview.hidden = false
WebView.loadHTMLString(htmlString, baseURL: nil)
-> After the above html is loaded into UIWebView, delegate function webViewDidFinishLoad is called. From webViewDidFinishLoad, take a snapshot of whatever is loaded in UIWebView into a Image with the code below:
var image:UIImage?
autoreleasepool{
UIGraphicsBeginImageContextWithOptions(WebView.frame.size, false, 1.0)
WebView.layer.renderInContext(UIGraphicsGetCurrentContext()!)
image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
-> Store the captured image into a UIImage variable.
-> Load one more different html/css code into the same UIWebView ( I am still in the webViewDidFinishLoad function): the html loading code is same as above i.e.
WebView.loadHTMLString(htmlString1, baseURL: nil)
-> webViewDidFinishLoad is called again because of new html code loaded in above step, and I do the same thing i.e. take a snapshot of the UIWebView content into UIImage variable and load new html pattern
-> I do this 4 times i.e. in the end I have 4 Images captured in 4 UIImage variables. I load all these 4 images into the 4 UIImageView on my storyboard.
-> Then I dismiss imagepicker
imagePicker.dismissViewControllerAnimated(true, completion: nil)
Here is the issue I am seeing in the end:
-> Sometimes, Image1 is same as Image 2, and sometimes all Images are the same. This happens randomly. I know for sure that all these 4 images should be unique. Because I load different html code in each step.
What Am I doing wrong in the sequence above?
A couple things you might try:
1) Don't reload webView in webViewDidFinishLoad. Instead wait until the next run loop on main thread (allowing iOS to fully finish). In objective C, my code would look like
dispatch_async (dispatch_get_main_queue(), ^{
// call method to load new html
});
2) I've had issues with webView not being refreshed by the time webViewDidFinishLoad was called. I solved this by adjusting the webView frame. Goofy, yes. This was back in iOS 6, so I have no idea if it makes any difference anymore or would affect what you are doing.
[webView loadHTMLString: html baseURL: nil];
// Play with frame to fix refresh problem
CGRect frame = webView.frame;
webView.frame = CGRectZero;
webView.frame = frame;
Related
i create a html string with 500 p tag with timestamp in it
i use UIWebView and WKWebView loadHTMLString:baseURL: to load it,and wkWebView is slower about 50% than UIWebVIew. why?
UIWebView:0.1681529879570007
WKWebView:0.3913570046424866
WKWebView is faster for displaying html from Strings. However, there is a bug that makes UIWebView faster by default, which is the phone number detection.
Running a viewController with the following code, webView being respectively a UIWebView and WKWebView instance and keeping everything else identical I found WKWebView to take up to 2 seconds to load, while UIWebView loads almost instantly.
webView.loadHTMLString(HtmlStringInstance, baseURL: nil)
I'm by far not the only one to find this:
when I use wkwebview to load local html,I find it renders slower than uiwebview
UIWebView delay in loading local content
Slow loading UIWebView from string
Delay in loading a HTML string into a UIWebView
Why UIWebView work so slowly when loadHTMLString with UIWebView?
However, the solution is easy: Disable phone number detection for WKWebView and poof. There you go.
For me, creating static variable to avoid creating WKWebView multiple times worked. Objective-C example:
- (WKWebView *)webHeaderView
{
static WKWebView *_webHeaderView = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (!_webHeaderView)
{
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
_webHeaderView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration];
}
});
return _webHeaderView;
}
WKWebView rendering performance is noticeable in WebGL games and something that runs complex JavaScript algorithms which UIWebView lacks.
You check the performance both in the Github.
To make WKWebView faster, disabling WKWebView's data detectors worked for me. Swift version:
let webViewCofig = WKWebViewConfiguration()
webViewCofig.dataDetectorTypes = []
webView = WKWebView(frame: view.frame, configuration: webViewCofig)
To enable specific data detector, pass the specific type as .address,.link etc while setting dataDetectorTypes:
config.dataDetectorTypes = [.address]
When loading the pdf in UIWebview it loads properly at first.But when zoomed in and out for sometime, the pdf is not rendered properly. ie in certain zoom level some of the tile area [Rectangular area] appears black and it persist in the same state afterwards.
Please see the image below
Any idea how to make this proper ?
I face same issue like this, i tried to read local pdf using WKWebview and UIWebView. Somehow after I zoom in and zoom out in iPad the view become black like your view and I think its because memory issue and there is problem when rendering the html.
If you want to open pdf better just call UIDocumentInteractionController or PDFKit for above iOS 11
func showPDF(){
let documentController = UIDocumentInteractionController.init(url: localUrl!)
documentController.delegate = self
documentController.presentPreview(animated: true)
}
And you need implement the delegate method
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
return self
}
Is it possible to perform a screenshot of the current visible zone of the webview in Safari from a Share Extension? I could use windows, but UIApplication isn't supported on extensions so I can't access to that window.
You can't since UIApplication can't be reached from an extension. You cannot get the first UIWindow, which is the Safari layer, so you have to play with the Javascript preprocessing file that the extensions have. So just create a Javascript file that, when sent to Safari, generates a base64 string with the current visible zone image data. Take that string through the kUTTypePropertyList identifier in your extension. Since that should be NSData, generate the UIImage from there, by using +imageWithData. That is what you're looking for, without having to load the page again, preventing a second load and a bad image if the webpage requires of a login.
As far as I know, you can't unless you invoke the API you need dynamically, and even so you might run into context permission issues and app store approval issues.
An alternative might be passing the current Safari URL to your extension, load it using a hidden UIWebView and render this view into an UIImage but you will loose the current visible zone information...
Edit: So the below works in the Simulator but does not work on the device. I'm presently looking for a solution as well.
You can't get just the visible area of Safari, but you can get a screenshot with a little ingenuity. The following method captures a screenshot from a ShareViewController.
func captureScreen() -> UIImage
{
// Get the "screenshot" view.
let view = UIScreen.mainScreen().snapshotViewAfterScreenUpdates(false)
// Add the screenshot view as a subview of the ShareViewController's view.
self.view.addSubview(view);
// Now screenshot *this* view.
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, false, 0);
self.view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Finally, remove the subview.
view.removeFromSuperview()
return image
}
This is the approved way to capture the screenshot of a webpage in a share extension:
for (NSExtensionItem *item in self.extensionContext.inputItems) {
for (NSItemProvider *itemProvider in item.attachments) {
[itemProvider loadPreviewImageWithOptions:#{NSItemProviderPreferredImageSizeKey: [NSValue valueWithCGSize:CGSizeMake(60.0f, 60.0f)]} completionHandler:^(UIImage * item, NSError * _Null_unspecified error) {
// Set the size to that desired, however,
// Note that the image 'item' returns will not necessarily by the size that you requested, so code should handle that case.
// Use the UIImage however you wish here.
}];
}
}
I try to take screenshot of uiwebview and send it with observer to another UIImageView in another class.
I using this method to take screenshot:
-(UIImage*)takeScreenshoot{
#autoreleasepool {
UIGraphicsBeginImageContext(CGSizeMake(self.view.frame.size.width,self.view.frame.size.height));
CGContextRef context = UIGraphicsGetCurrentContext();
[self.webPage.layer renderInContext:context];
UIImage *__weak screenShot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return screenShot;
}
}
But then I have problem. Everytime I take screenshot with this method, memory rate grows about 10-15mb and it's not realising it. And if I take screenshot in every webviewdidfinishload, you can imagine how much it can take memory!
How can I fix that issue?
If possible try to use [UIScreen snapshotViewAfterScreenUpdates] which returns UIView .
This is the snapshot of currently displayed content (snapshot of app).
Even apple also says " this method is faster than trying to render the contents of the screen into a bitmap image yourself."
According to your code, you are passing this bitmap image to just display in some other UIImageView. so i think using UIScreen method is appropriate here.
To display UIWebView part only->
Create another UIView instance. Set its frame to the frame of your webView.
Now add this screenShot view as subView to createdView and set its frame such that webView portion will be displayed.
Try calling CGContextRelease(context); after you have got your screen shot.
Or as #Greg said, remove the line and use UIGraphicsGetCurrentContext() directly
I'm trying to generate thumbnails of several pages that will be displayed through a UIWebView. The problem is all of the images come out as gray or as the background color I set for the UIWebView. Could it be that I am not allowing the web view enough time to load the page?
I believe I want to do the following:
Create a UIWebView that is not visible on the screen
Setup graphics context
For each page:
Call loadRequest on the web view
Render web view in graphics context
capture screenshot
End graphic context
Here is the code I have for a single image:
UIWebView *screenWebView = _screenshotWebView;
UIView *screenView = _screenshotView;
if ([UIScreen instancesRespondToSelector:#selector(scale)] && [[UIScreen mainScreen] scale] == 2.0f) {
UIGraphicsBeginImageContextWithOptions(screenView.bounds.size, NO, 2.0f);
} else {
UIGraphicsBeginImageContext(screenView.bounds.size);
}
[screenWebView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:path ofType:#"html"]isDirectory:NO]]];
[screenView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
I defined _screenshotWebView and _screenshotView within a XIB file because I thought I may not have been initializing them correctly.
Any thoughts? Thanks!
Edit: Here is some code that implements this functionality in case someone else needs it: https://github.com/rmcl/webview_screenshot
As you theorized, there isn't time for the UIWebView to load the page. That method will asynchronously start the page load; the UIWebView probably won't get a chance to do any work until you've returned to the run loop.
Try implementing webViewDidFinishLoad: and do your rendering after it has completed.