iOS 13 Swift 5 wkwebview - display image from documents directory into wkwebview real device - html

I have an app with downloaded images to be displayed in local html in WKWEBVIEW. Everything is working fine but it is not showing image on iPhone device
the html is
"\n<img src="images/d35fb6a3-8a21-4196-9616-ad2c6db60669/fd21b894-38c0-42c5-aa69-a938abe40e4b2467857252325869136.png">\n<img src="images/d35fb6a3-8a21-4196-9616-ad2c6db60669/c927a2a6-4ef0-481d-b27d-525cec7ed3814195490571216387044.png">\n"
matching to this HTML it is to be displayed in a wkwebview which gets displayed perfectly on simulator with the following code
let baseURL = Functions.FileIO.contentFolderURL()
static func contentFolderURL() -> URL? {
guard let folderURL = DataManager.shared.applicationDocumentsURL?.appendingPathComponent("content") else {
return nil
}
do {
let properties = try (folderURL as NSURL).resourceValues(forKeys: [URLResourceKey.isDirectoryKey])
if let isDirectory = properties[URLResourceKey.isDirectoryKey] as? Bool , isDirectory == false {
return nil
}
} catch let error as NSError where error.code != NSFileReadNoSuchFileError {
return nil
} catch {
// No folder, so we create it.
do {
try FileManager.default.createDirectory(atPath: folderURL.path, withIntermediateDirectories: true, attributes: nil)
} catch {
return nil
}
}
return folderURL
}
and then finally displaying
baseURL comes to be
Optional ▿ some :
file:///Users/paza/Library/Developer/CoreSimulator/Devices/D2204E03-8A8F-4EF4-8924-683CF519DD19/data/Containers/Data/Application/E4ED56CF-B247-4471-9F9B-23384FD6D6B3/Documents/content/
- _url : file:///Users/paza/Library/Developer/CoreSimulator/Devices/D2204E03-8A8F-4EF4-8924-683CF519DD19/data/Containers/Data/Application/E4ED56CF-B247-4471-9F9B-23384FD6D6B3/Documents/content/
self.webView?.loadHTMLString(HTML, baseURL: baseURL)
working in simulator below does not work in Device
as https://stackoverflow.com/a/52838445/3455426[ ]2
suggested I tried
self.webView?.loadFileURL(baseURL, allowingReadAccessTo: baseURL)
but the result this comes blank on real device
any leads will be appreciated , Thanks in advance

In WKWebview if we have to show HTML with Cached resources ,firstly we have to save the html string in HTML file in documents directory.
make sure ur cached resources path and the path of HTML file are same also , the cached resources are identified by the lastPathComponent only .
So the fault was the HTML which should have been
let HTML = "<html><body>\n<p><img src=\"fd21b894-38c0-42c5-aa69-a938abe40e4b2467857252325869136.png\"></p>\n<p><img src=\"c927a2a6-4ef0-481d-b27d-525cec7ed3814195490571216387044.png\"><br></p>\n</body></html>"
Specifically the last path component should be there .
Also I followed
https://en.it1352.com/article/9f185d4d185243cc92128148443ca660.html
here the explanation is almost perfect , I just saved in documents directory for swift.

Related

How to open another .html file inside WKWebView when a link gets tapped?

I am using SwiftUI along with UIViewRepresentable to instantiate a WKWebView to display my html content:
WebView
struct WebView : UIViewRepresentable {
let request: URLRequest
func makeUIView(context: Context) -> WKWebView {
return WKWebView()
}
func updateUIView(_ uiView: WKWebView, context: Context) {
uiView.load(self.request)
}
}
SwiftUI View
let localFilePath = Bundle.main.url(forResource: "EULA for ALP app", withExtension: "html")
VStack{
WebView(request: URLRequest(url: localFilePath!))
}.padding([.leading,.trailing],20)
HTML
When I tap the span nothing happens as if its not registering the hit, I want to use another local HTML file like the one I am using inside the WebView so when the span gets tapped the WebView displays the corresponding file. I would also like to mention that HTML gets displayed correctly just without the href working.
<span href="file://practitionerPolicy.html">Tap Me!</span>
I assume in this case, as this is your internal resources, relative path should work, like
<span href="practitionerPolicy.html">Tap Me!</span>

Swift - Switch multiple (x)html files in the same webview

I have one webview in my view controller that I want to use as a reader. How can I switch between multiple (x)html files in this webview? I retrieve them one by one with the following code:
apiClient.getData(forFile: file) { (data, error) in
if let error = error {
//show error
return
}
if let data = data {
self.data = data
self.webView.load(data, mimeType: mimeType, textEncodingName: "utf-8", baseURL: URL(string: "")!)
}
}
Right now When a user scrolls (horizontal) through the pages of an epub and transitions from one file to another, there is a short loading time (because it is retrieving the next xhtml from the server).
Right now, it is a jarring experience, preventing the user from scrolling forward. When the next file is loaded, the page jumps to the new one.

Edit and Load HTML from local file in Swift

I have a .html file stored in my project bundle. When I load it in WebView.(UIWebview/WKWebview) the data is loaded but the table structure in it isn't visible. The table borders, columns , rows. The values are just floating . In Chrome browser it opens properly.
let myURL = Bundle.main.url(forResource: "prescription", withExtension: "html")
let myURLRequest:URLRequest = URLRequest(url: myURL!)
webView.loadRequest(myURLRequest)
Chrome browser :
iOS App UIWebView :
The Html page is made responsive to fit in any sizes but I am not able to load in UIWebview properly as Web.
I need to store the file locally because I need to make changes to its values and show on webview.
Found the solution :
If you are applying some local css whose path is there in HTML file.
viz.
Add that css file "myDefault.css" to your xcode project directory.
Convert the HTML to string and replace the path of css in HTML file with your local path. :
Load the content of HTML to UIWebView with base url. (Providing local file path as baseUrl is important)
webView = UIWebView()
let pathToInvoiceHTMLTemplate = Bundle.main.path(forResource: "presc", ofType: "html")
let pathToHTMLCSS = Bundle.main.path(forResource: "myDefault", ofType: "css")
do {
var strHTMLContent = try String(contentsOfFile: pathToInvoiceHTMLTemplate!)
strHTMLContent = strHTMLContent.replacingOccurrences(of: "./assets/stylesheets/myDefault.css", with: pathToHTMLCSS!)
let pdfURL = URL(fileURLWithPath: pathToInvoiceHTMLTemplate!)
webView.loadHTMLString(strHTMLContent, baseURL: pdfURL)
}catch let err{
print(err)
}
When loading local content into the WKWebView, you shoudl use loadHTMLString(_:baseURL:) instead of loadRequest. The first function allows you to provide a base URL, which you will also let point into the bundle - so you clearly need to add all the relevant files to your application bundle.
guard let indexPath = Bundle.main.path(forResource: "index", ofType: "html"),
let content = try String(contentsOfFile: indexPath, encoding: .utf8),
let baseUrl = URL(fileURLWithPath: indexPath) else {
// Error handling
}
webView.loadHTMLString(content, baseURL: baseUrl)

go to the previous local html in UIWebView in iOS

In my project there are plenty of html files which through anchor links are connected to each other.
All of the html files are shown after selected in a UITableView, by an UIWebView. Once loaded the anchor links work and user can go to the chosen html.
Now the problem arises when want to go back, since whatever i do the back button in the navigation bar takes us to the tableView not the previous html.
How can i add a back button and how do i know that at any given time which html is being seen through UIWebView ?
import UIKit
class DisplayViewController: UIViewController, UIWebViewDelegate {
var articleName = “”
#IBOutlet var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
functionOfWebView()
}
func functionOfWebView()
{
let URL = Bundle.main.url(forResource: "\(articleName)", withExtension: "html")
let request = NSURLRequest(url: URL! as URL)
webView.loadRequest(request as URLRequest)
}
You can easily achieve this by linking the webView's "goBack" action to a UIButton.
I assume that you are navigating between the html files in the same webView.
From the storyboard, select the webView and then select "Connections Inspector":
Note that there is "goBack" option in the list of "Received Actions"; Drag from its circle to a button:
Now, instead of popping the current ViewController, the button should do the desired functionality to your case (back to the previous webpage in the webview).
In my case i did a VC's property isInitialWebPageLoaded which reflects whether or not webView is loaded from HTML string or not - which implicates that user did tap link or something else happened.
To get know when it happened VC need to conform to UIWebViewDelegate protocol and implement func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool like that:
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
isInitialWebPageLoaded = navigationType != .linkClicked
return true
}
After that with every back button action is being invoked i simply check:
if webView.canGoBack {
webView.goBack()
} else if isInitialWebPageLoaded == false {
webView.loadHTMLString(yourHTMLString, baseURL: nil)
} else {
navigationController?.popViewController(animated: true)
}
Hope it helped.
You must to add an unique ID in the HTML and later when go back with "goBack" action parse the html finding the ID .

UIMarkupTextPrintFormatter never renders base64 images

Im creating a pdf file out of html content in swift 3.0:
/**
*
*/
func exportHtmlContentToPDF(HTMLContent: String, filePath: String) {
// let webView = UIWebView(frame: CGRect(x: 0, y: 0, width: 694, height: 603));
// webView.loadHTMLString(HTMLContent, baseURL: nil);
let pdfPrinter = PDFPrinter();
let printFormatter = UIMarkupTextPrintFormatter(markupText: HTMLContent);
// let printFormatter = webView.viewPrintFormatter();
pdfPrinter.addPrintFormatter(printFormatter, startingAtPageAt: 0);
let pdfData = self.drawPDFUsingPrintPageRenderer(printPageRenderer: pdfPrinter);
pdfData?.write(toFile: filePath, atomically: true);
}
/**
*
*/
func drawPDFUsingPrintPageRenderer(printPageRenderer: UIPrintPageRenderer) -> NSData! {
let data = NSMutableData();
UIGraphicsBeginPDFContextToData(data, CGRect.zero, nil);
printPageRenderer.prepare(forDrawingPages: NSMakeRange(0, printPageRenderer.numberOfPages));
let bounds = UIGraphicsGetPDFContextBounds();
for i in 0...(printPageRenderer.numberOfPages - 1) {
UIGraphicsBeginPDFPage();
printPageRenderer.drawPage(at: i, in: bounds);
}
UIGraphicsEndPDFContext();
return data;
}
Everything is rendered fine except my base64 encoded images. The HTML content itself in a webview or inside safari or chrome browser is presented correctly and is showing all images correctly. But the images are never rendered into the pdf.
Why are the images not rendered and how can I get them to be rendered?
This happens because WebKit first parses the HTML into a DOM, and renders content on multiple event loop cycles. You therefore need to wait for not just the page DOM to be ready but for the resource loading to be complete. As you also suggest, you need to refactor your code such that the webview gets loaded first, and you only then export its contents.
To determine the correct time to fire the export, you can observe for the state of the DOM document in the web view. There are multiple ways to do this, but the most readable option I find is a port of an answer to a related Objective-C question: in your UIWebViewDelegate implementation, implement webViewDidFinishLoad in the following way to monitor document.readyState:
func webViewDidFinishLoad(_ webView: UIWebView) {
guard let readyState = webView.stringByEvaluatingJavaScript(from: "document.readyState"),
readyState == "complete" else
{
// document not yet parsed, or resources not yet loaded.
return
}
// This is the last webViewDidFinishLoad call --> export.
//
// There is a problem with this method if you have JS code loading more content:
// in that case -webViewDidFinishLoad can get called again still after document.readyState has already been in state 'complete' once or more.
self.exportHtmlContentToPDF(…)
}
I found the solution!
The export to PDF happens before the rendering process is finished. If you put in a very small picture it is showing up in the PDF. If the picture is too big the rendering process takes too much time but the PDF export isnt waiting for the rendering to finish.
So what I did to make it work is the following:
Before I export to PDF I show the Result of the HTML in a WebView. The WebView is rendering everything correctly and now when I press on export to PDF the PDF is showing up correctly with all images inside.
So I guess this is a huge lag that there is no way to tell the PDF Exporter to wait for the rendering process to finish.