How can I create a math formula text view with LaTeX in SwiftUI?

Viewed 2042

I'm new to Swift programing and wish to create an app with SwiftUI. This is what I wish to achieve:

Desired result

that is, a text field that has real-time syntax highlighting for LaTeX, and then the content of which is being rendered by mathjax and shown in a web view.

I did some search on this site and on GitHub, only to find related codes are mostly in Objective-C or Swift 4.x (such as this), and none of them has interface made with SwiftUI. However, during my research I found one approach that might work. It seems that with the JavaScriptCore framework one may able to use of highlight.js to make syntax highlighting (like what Highlightr done there, but its codes are very complex). I'm convinced by this approach because if one can use highlight.js to achieve code highlighting, then in a similar way one should be able to use other JavaScripts like mathjax.js to achieve other features.

Unfortunately, since I'm new to Swift and hence SwiftUI, I have no idea where to begin with. Could anyone kindly offer me some hints? (Any help is welcome, not necessarily use JavaScriptCore)

2 Answers

Here is a possible solution using a great framework, RichTextView:

import RichTextView

struct ContentView : View {
    
    @State var string : String = "In physics, the mass-energy equivalence is stated by the equation [math]$E=mc^2$[/math], discovered in 1905 by Albert Einstein."
    
    var body : some View {
        TextField("Test", text: $string)
            .padding(.vertical, 32)
            .border(Color.red)
        
        TextView(string: $string)
            .padding(.horizontal, 16)
    }
}


struct TextView : UIViewRepresentable {
    
    @Binding var string : String
    
    func makeUIView(context: Context) -> RichTextView {
        let richTextView = RichTextView(
            input: string,
            latexParser: LatexParser(),
            font: UIFont.systemFont(ofSize: UIFont.systemFontSize),
            textColor: UIColor.black,
            frame: CGRect.zero,
            completion: nil
        )
        
        return richTextView
        
    }

   func updateUIView(_ uiView: RichTextView, context: Context) {
        uiView.update(
            input: string,
            latexParser: LatexParser(),
            font: UIFont.systemFont(ofSize: UIFont.systemFontSize),
            textColor: UIColor.black,
            completion: nil
        )
   }
}

You always have to start with [math] and end with [/math] but can use default Latex. More information on their documentation.

Here is my current solution, it uses the CodeViewer package for code highlighting, and mathjax.js to render maths.

import SwiftUI
import CodeViewer

struct LaTeXView: View {
    @State private var placeholderString: String = "Insert here"
    @State private var inputText: String = ""
    
    var body: some View {
        VStack {
            //MARK: Code editor
            CodeViewer(
                content: $inputText,
                mode: .latex,
                darkTheme: .solarized_dark,
                lightTheme: .solarized_light,
                isReadOnly: false,
                fontSize: 50
            )
            
            //MARK: for rendering math
            HTMLStringView(htmlContent: """
                <html>
                <head>
                  <meta charset="utf-8">
                  <meta name="viewport" content="width=device-width">
                  <title>MathJax example</title>
                  <script>MathJax = {
                        tex:{
                                inlineMath: [['$', '$'], ['\\(', '\\)']],
                                tags: 'ams'
                            },
                        svg:{
                                fontCache: 'global'
                            }
                        };
                  </script>
                  <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
                  <script id="MathJax-script" async
                          src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js">
                  </script>
                </head>
                <body>
                <p>
                \(inputText.replacingOccurrences(of: "\n\n", with: "<br>"))
                </p>
                </body>
                </html>
                """
            )
        }
    }
}

struct LaTeXView_Previews: PreviewProvider {
    static var previews: some View {
        LaTeXView()
    }
}

The result looks like this:

Result

Related