From 95facd91fdf384cc37092e4b06db09afa0558e3e Mon Sep 17 00:00:00 2001 From: xenodium Date: Tue, 25 Jan 2022 00:28:49 +0000 Subject: [PATCH 1/6] Adds KatexView (for SwiftUI support) and SPM's Package.swift --- KatexMathView.swift | 46 ++++++++++++++++++++++++--------------------- KatexView.swift | 31 ++++++++++++++++++++++++++++++ Package.swift | 33 ++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 21 deletions(-) create mode 100644 KatexView.swift create mode 100644 Package.swift 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..8fe037d --- /dev/null +++ b/KatexView.swift @@ -0,0 +1,31 @@ +import SwiftUI +import WebKit + +public struct KatexView: UIViewRepresentable { + private let latex: String + + private let uiView = KatexMathView() + + @Binding var contentHeight: CGFloat + + public init(latex: String, contentHeight: Binding) { + self.latex = latex + self._contentHeight = contentHeight + } + + public func katexMathView(_ katexMathView: (KatexMathView) -> Void) -> KatexView { + katexMathView(uiView) + return self + } + + public func makeUIView(context: Context) -> KatexMathView { + uiView.onLoaded = { height in + contentHeight = height + } + return uiView + } + + public func updateUIView(_ uiView: KatexMathView, context: Context) { + 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") + ] + ) + ] +) From 1755bcf056fcae43906d5bf0479d85e6101c427d Mon Sep 17 00:00:00 2001 From: xenodium Date: Tue, 25 Jan 2022 01:09:47 +0000 Subject: [PATCH 2/6] Disabling text-selection --- katex/katex.css | 6 +++++- katex/katex.min.css | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/katex/katex.css b/katex/katex.css index 19f30c4..f3d0f41 100644 --- a/katex/katex.css +++ b/katex/katex.css @@ -1022,5 +1022,9 @@ img { max-width:100% !important; width:100% !important; - +} + +* { + -webkit-touch-callout: none; + -webkit-user-select: none; } diff --git a/katex/katex.min.css b/katex/katex.min.css index b2d4dc3..20d20c8 100644 --- a/katex/katex.min.css +++ b/katex/katex.min.css @@ -2,5 +2,10 @@ img { max-width:100% !important; width:100% !important; - + +} +/* Disable touch and text-selection. */ +* { + -webkit-touch-callout: none; + -webkit-user-select: none; } From 2e2093b0987aa504654ab530f2b0170fe353c570 Mon Sep 17 00:00:00 2001 From: xenodium Date: Thu, 27 Jan 2022 19:49:00 +0000 Subject: [PATCH 3/6] Cache height to avoid SwiftUI height-flicker --- KatexView.swift | 51 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/KatexView.swift b/KatexView.swift index 8fe037d..612f2ee 100644 --- a/KatexView.swift +++ b/KatexView.swift @@ -1,31 +1,74 @@ import SwiftUI import WebKit -public struct KatexView: UIViewRepresentable { +public struct KatextView: View { + private let latex: String + + @State + private var contentHeight: CGFloat = 0 + + @State + private var isLoaded = false + + public init(latex: String) { + self.latex = latex + } + + 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 - public init(latex: String, contentHeight: Binding) { + init(latex: String, contentHeight: Binding, isLoaded: Binding) { self.latex = latex self._contentHeight = contentHeight + self._isLoaded = isLoaded } - public func katexMathView(_ katexMathView: (KatexMathView) -> Void) -> KatexView { + func katexMathView(_ katexMathView: (KatexMathView) -> Void) -> KatexMathViewRepresentable { katexMathView(uiView) return self } - public func makeUIView(context: Context) -> KatexMathView { + 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) } } From 192c1b34e95a0d174716cc264f0e35103c1df684 Mon Sep 17 00:00:00 2001 From: xenodium Date: Thu, 27 Jan 2022 19:50:12 +0000 Subject: [PATCH 4/6] Enable dark mode --- katex/katex.css | 4 ++++ katex/katex.min.css | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/katex/katex.css b/katex/katex.css index f3d0f41..c94f828 100644 --- a/katex/katex.css +++ b/katex/katex.css @@ -1028,3 +1028,7 @@ img { -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 20d20c8..c2f6a4f 100644 --- a/katex/katex.min.css +++ b/katex/katex.min.css @@ -9,3 +9,7 @@ img { -webkit-touch-callout: none; -webkit-user-select: none; } + +:root { + color-scheme: light dark; +} From 807ebdc52cbecb22d8bbf095df3965cfbc8afd51 Mon Sep 17 00:00:00 2001 From: xenodium Date: Thu, 27 Jan 2022 21:47:36 +0000 Subject: [PATCH 5/6] Fixes typo --- KatexView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KatexView.swift b/KatexView.swift index 612f2ee..5594a04 100644 --- a/KatexView.swift +++ b/KatexView.swift @@ -1,7 +1,7 @@ import SwiftUI import WebKit -public struct KatextView: View { +public struct KatexView: View { private let latex: String @State From c66e8ae305679902ff8237d78522f7254b83cdf6 Mon Sep 17 00:00:00 2001 From: xenodium Date: Thu, 27 Jan 2022 21:47:46 +0000 Subject: [PATCH 6/6] Adds height cache reset --- KatexView.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/KatexView.swift b/KatexView.swift index 5594a04..19fe901 100644 --- a/KatexView.swift +++ b/KatexView.swift @@ -14,6 +14,10 @@ public struct KatexView: View { self.latex = latex } + public static func resetHeightCache() { + KatexMathViewRepresentable.heightCache = [Int: CGFloat]() + } + public var body: some View { ZStack { KatexMathViewRepresentable(latex: latex, contentHeight: $contentHeight, isLoaded: $isLoaded)