ListenerApp/ListenerGS/RichText/Webview.swift

184 lines
7.5 KiB
Swift

//
// Webview.swift
//
//
// Created by (NuPlay) on 2021/07/26.
// https://github.com/NuPlay/RichText
import SwiftUI
import WebKit
import SafariServices
struct Webview: UIViewRepresentable {
@Binding var dynamicHeight: CGFloat
private var webview: WKWebView = WKWebView()
let html: String
let lineHeight: CGFloat
let imageRadius: CGFloat
let fontType: fontType
let colorScheme: colorScheme
let colorImportant: Bool
let linkOpenType: linkOpenType
let linkColor: ColorSet
public init(dynamicHeight: Binding<CGFloat>, webview: WKWebView = WKWebView(), html: String, lineHeight: CGFloat, imageRadius: CGFloat, fontType: fontType, colorScheme: colorScheme, colorImportant: Bool, linkOpenType: linkOpenType, linkColor: ColorSet) {
self._dynamicHeight = dynamicHeight
self.webview = webview
self.html = html
self.lineHeight = lineHeight
self.imageRadius = imageRadius
self.fontType = fontType
self.colorScheme = colorScheme
self.colorImportant = colorImportant
self.linkOpenType = linkOpenType
self.linkColor = linkColor
}
public class Coordinator: NSObject, WKNavigationDelegate {
var parent: Webview
init(_ parent: Webview) {
self.parent = parent
}
public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.evaluateJavaScript("document.documentElement.scrollHeight", completionHandler: { (height, _) in
DispatchQueue.main.async {
self.parent.dynamicHeight = height as! CGFloat
}
})
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if navigationAction.navigationType == WKNavigationType.linkActivated {
if let url = navigationAction.request.url {
let root = UIApplication.shared.windows.first?.rootViewController
switch self.parent.linkOpenType {
case .SFSafariView:
root?.present(SFSafariViewController(url: url), animated: true, completion: nil)
case .SFSafariViewWithReader:
let configuration = SFSafariViewController.Configuration()
configuration.entersReaderIfAvailable = true
root?.present(SFSafariViewController(url: url, configuration: configuration), animated: true, completion: nil)
case .Safari :
UIApplication.shared.open(url)
case .none :
print(url)
}
}
decisionHandler(WKNavigationActionPolicy.cancel)
return
}
decisionHandler(WKNavigationActionPolicy.allow)
}
}
public func makeCoordinator() -> Coordinator {
Coordinator(self)
}
public func makeUIView(context: Context) -> WKWebView {
webview.scrollView.bounces = false
webview.navigationDelegate = context.coordinator
webview.scrollView.isScrollEnabled = false
let htmlStart = """
<HTML>
<head>
<meta name='viewport' content='width=device-width, shrink-to-fit=YES, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no'>
</head>
"""
let htmlEnd = "</BODY></HTML>"
let htmlString = "\(htmlStart)\(css(colorScheme: self.colorScheme))\(html)\(htmlEnd)"
webview.loadHTMLString(htmlString, baseURL: nil)
webview.isOpaque = false
webview.backgroundColor = UIColor.clear
webview.scrollView.backgroundColor = UIColor.clear
//
return webview
}
public func updateUIView(_ uiView: WKWebView, context: Context) {
let htmlStart = """
<HTML>
<head>
<meta name='viewport' content='width=device-width, shrink-to-fit=YES, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no'>
</head>
"""
let htmlEnd = "</BODY></HTML>"
let htmlString = "\(htmlStart)\(css(colorScheme: self.colorScheme))\(html)\(htmlEnd)"
uiView.loadHTMLString(htmlString, baseURL: nil)
}
func css(colorScheme: colorScheme) -> String {
switch colorScheme {
case .light:
return """
<style type='text/css'>
img{max-height: 100%; min-height: 100%; height:auto; max-width: 100%; width:auto;margin-bottom:5px; border-radius: \(imageRadius)px;}
h1, h2, h3, h4, h5, h6, p, dl, ol, ul, pre, blockquote {text-align:left|right|center; line-height: \(lineHeight)%; font-family: '\(fontName(fontType: self.fontType))'; color: #000000 \(colorImportant == false ? "" : "!important"); }
iframe{width:100%; height:250px;}
a:link {color: \(linkColor.light);}
A {text-decoration: none;}
</style>
<BODY>
"""
case .dark :
return """
<style type='text/css'>
img{max-height: 100%; min-height: 100%; height:auto; max-width: 100%; width:auto;margin-bottom:5px; border-radius: \(imageRadius)px;}
h1, h2, h3, h4, h5, h6, p, dl, ol, ul, pre, blockquote {text-align:left|right|center; line-height: \(lineHeight)%; font-family: '\(fontName(fontType: self.fontType))'; color: #F2F2F2 \(colorImportant == false ? "" : "!important"); }
iframe{width:100%; height:250px;}
a:link {color: \(linkColor.dark);}
A {text-decoration: none;}
</style>
<BODY>
"""
case .automatic:
return """
<style type='text/css'>
@media (prefers-color-scheme: light) {
img{max-height: 100%; min-height: 100%; height:auto; max-width: 100%; width:auto;margin-bottom:5px; border-radius: \(imageRadius)px;}
h1, h2, h3, h4, h5, h6, p, dl, ol, ul, pre, blockquote {text-align:left|right|center; line-height: \(lineHeight)%; font-family: '\(fontName(fontType: self.fontType))'; color: #000000 \(colorImportant == false ? "" : "!important"); }
iframe{width:100%; height:250px;}
a:link {color: \(linkColor.light);}
A {text-decoration: none;}
}
@media (prefers-color-scheme: dark) {
img{max-height: 100%; min-height: 100%; height:auto; max-width: 100%; width:auto;margin-bottom:5px; border-radius: \(imageRadius)px;}
h1, h2, h3, h4, h5, h6, p, dl, ol, ul, pre, blockquote {text-align:left|right|center; line-height: \(lineHeight)%; font-family: '\(fontName(fontType: self.fontType))'; color: #F2F2F2 \(colorImportant == false ? "" : "!important"); }
iframe{width:100%; height:250px;}
a:link {color: \(linkColor.dark);}
A {text-decoration: none;}
}
</style>
<BODY>
"""
}
}
func fontName(fontType: fontType) -> String {
switch fontType {
case .system:
return "-apple-system"
case .monospaced:
return UIFont.monospacedSystemFont(ofSize: 17, weight: .regular).fontName
case .italic:
return UIFont.italicSystemFont(ofSize: 17).fontName
default :
return "-apple-system"
}
}
}