diff --git a/KatexMathView.swift b/KatexMathView.swift index efe6eb1..f296582 100644 --- a/KatexMathView.swift +++ b/KatexMathView.swift @@ -8,38 +8,42 @@ import UIKit import WebKit -class KatexMathView: WKWebView { - +public class KatexMathView: WKWebView { + public var onLoaded: (CGFloat)-> Void = { _ in } + func loadLatex(_ content: String ) { - +#if SPM_PACKAGE + guard let path = Bundle.module.url(forResource: "katex/index", withExtension: "html")?.path else { + fatalError() + } +#else guard let path = Bundle.main.path(forResource: "katex/index", ofType: "html") else { fatalError() } +#endif self.configuration.preferences.javaScriptEnabled = true - self.scrollView.isScrollEnabled = false - self.scrollView.bounces = false self.navigationDelegate = self - + self.isOpaque = false self.backgroundColor = UIColor.clear self.scrollView.backgroundColor = UIColor.clear - + let htmlContent = getHtml(content, path) - + self.loadHTMLString(htmlContent, baseURL: URL(fileURLWithPath: path)) } - + func getHtml(_ htmlContent: String, _ path: String) -> String { - + var htmlString = try! String(contentsOfFile: path, encoding: .utf8) - + var content = htmlContent let delimitter = "$" let startTexTag = "" let endTexTag = "" - + var first = true - + while content.contains(delimitter) { let tag: String = first ? startTexTag : endTexTag if let range = content.range(of: delimitter) { @@ -50,24 +54,24 @@ class KatexMathView: WKWebView { htmlString = htmlString.replacingOccurrences(of: "$LATEX$", with: content) return htmlString } - - - + + + } extension KatexMathView : WKNavigationDelegate { - + public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { self.evaluateJavaScript("document.readyState", completionHandler: { (complete, error) in if complete != nil { - self.evaluateJavaScript("document.body.scrollHeight", completionHandler: { (height, error) in + self.evaluateJavaScript("document.documentElement.scrollHeight", completionHandler: { (height, error) in self.frame.size.height = height as! CGFloat self.layoutIfNeeded() + self.onLoaded(self.frame.size.height) }) } - + }) } - -} +} diff --git a/KatexView.swift b/KatexView.swift new file mode 100644 index 0000000..19fe901 --- /dev/null +++ b/KatexView.swift @@ -0,0 +1,78 @@ +import SwiftUI +import WebKit + +public struct KatexView: View { + private let latex: String + + @State + private var contentHeight: CGFloat = 0 + + @State + private var isLoaded = false + + public init(latex: String) { + self.latex = latex + } + + public static func resetHeightCache() { + KatexMathViewRepresentable.heightCache = [Int: CGFloat]() + } + + public var body: some View { + ZStack { + KatexMathViewRepresentable(latex: latex, contentHeight: $contentHeight, isLoaded: $isLoaded) + .frame(height: contentHeight) + if !isLoaded { + ProgressView() + } + }.frame(maxWidth: .infinity) + } +} + +struct KatexMathViewRepresentable: UIViewRepresentable { + // Caching heights prevents jumpy UI since the height calculation is + // delayed only the first time the latex block is rendered. + static var heightCache = [Int: CGFloat]() + + private let latex: String + + private let uiView = KatexMathView() + + @Binding var contentHeight: CGFloat + @Binding var isLoaded: Bool + + init(latex: String, contentHeight: Binding, isLoaded: Binding) { + self.latex = latex + self._contentHeight = contentHeight + self._isLoaded = isLoaded + } + + func katexMathView(_ katexMathView: (KatexMathView) -> Void) -> KatexMathViewRepresentable { + katexMathView(uiView) + return self + } + + func makeUIView(context: Context) -> KatexMathView { + uiView.onLoaded = { height in + isLoaded = true + contentHeight = height + var hasher = Hasher() + hasher.combine(latex) + Self.heightCache[hasher.finalize()] = height + } + return uiView + } + + public func updateUIView(_ uiView: KatexMathView, context: Context) { + var hasher = Hasher() + hasher.combine(latex) + let hash = hasher.finalize() + + if let height = Self.heightCache[hash] { + DispatchQueue.main.async { + contentHeight = height + } + } + uiView.loadLatex(latex) + } +} diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..5cfbf87 --- /dev/null +++ b/Package.swift @@ -0,0 +1,33 @@ +// swift-tools-version:5.3 +// The swift-tools-version declares the minimum version of Swift required to build this package. +import PackageDescription + +let package = Package( + name: "KatexMathView", + platforms: [.iOS(.v14), .macOS(.v11)], + products: [ + .library( + name: "KatexMathView", + targets: ["KatexMathView"]) + ], + targets: [ + .target( + name: "KatexMathView", + path: ".", + exclude: [ + "Example", + "README.md", + ], + sources: [ + "KatexMathView.swift", + "KatexView.swift", + ], + resources: [ + .copy("katex") + ], + swiftSettings: [ + .define("SPM_PACKAGE") + ] + ) + ] +) diff --git a/katex/katex.css b/katex/katex.css index 19f30c4..c94f828 100644 --- a/katex/katex.css +++ b/katex/katex.css @@ -1022,5 +1022,13 @@ img { max-width:100% !important; width:100% !important; - +} + +* { + -webkit-touch-callout: none; + -webkit-user-select: none; +} + +:root { + color-scheme: light dark; } diff --git a/katex/katex.min.css b/katex/katex.min.css index b2d4dc3..c2f6a4f 100644 --- a/katex/katex.min.css +++ b/katex/katex.min.css @@ -2,5 +2,14 @@ img { max-width:100% !important; width:100% !important; - + +} +/* Disable touch and text-selection. */ +* { + -webkit-touch-callout: none; + -webkit-user-select: none; +} + +:root { + color-scheme: light dark; }