首页 > 解决方案 > Swift:内联显示(LaTeX)数学表达式

问题描述

我想在文本中显示数学术语,特别是在内联模式下,即在句子中。

例如,使用 LaTeX,这看起来像:“给定一个直角三角形,其长度为 \(a\),分别为 \(b\),斜边的长度为 \(c\),我们有 \[a^2 + b^2 = c^2.\] 这个事实被称为勾股定理。”

有人知道如何在 Swift 中实现这一点吗?

(我知道这个例子可以不用类似 LaTeX 的工具在 Swift 中实现。但是,我脑海中的表达其实比这个例子更复杂,我确实需要 LaTeX 的力量。)

最佳方式是一个UITextView类,它可以识别数学分隔符 \(,\) resp。\[,\] 识别这些分隔符内的 LaTeX 代码,并相应地格式化文本。

在可汗学院应用程序中,这个问题似乎得到了解决,因为 Apple App Store/Google Play Store 中的屏幕截图显示了内联 (LaTeX) 数学。

我找到了包 iosMath ,它提供了一个 -like UILabelclass MTMathUILabel。由于此类只能显示公式,因此这似乎不足以满足我的目的,除非有一种方法采用 LaTeX 源文本(例如上面的示例),将诸如 \(a\) 之类的表达式格式化为 tiny MTMathUILabels并在其他文本组件之间设置这些标签。由于我是 Swift 新手,我不知道这是否以及如何实现。此外,从印刷的角度来看,这似乎非常困难,因为换行肯定会出现困难。如果屏幕上同时出现大量此类标签,可能会出现性能问题?

可以使用 a 和 MathJax 或 KaTeX 来实现我想要的WKWebView,当然这也是一种 hack。这会导致其他困难,例如,如果想要WKWebView在屏幕上设置几个这样的 s,例如在UITableViewCells 内。

标签: iosswiftmathlatex

解决方案


在此处输入图像描述 使用 iosMath,我关于如何让 UILabel 具有内联 LaTeX 的解决方案是包含没有空格的 LATEX 和 ENDLATEX 标记。我用 MTMathUILabel 的图像替换了所有范围,从最后一个范围到第一个范围,这样位置就不会搞砸(这个解决方案允许多个标记)。从我的函数返回的图像被翻转,因此我使用了 .downMirrored 方向,并调整了它的大小以适合我的文本,因此您可能需要稍微修正一下翻转比例为 2.5 的数字和 attachment.bounds 的 y 值。

import UIKit
import iosMath

let question = UILabel()
let currentQuestion = "Given a right triangle having catheti of length LATEX(a)ENDLATEX resp. LATEX(b)ENDLATEX and a hypotenuse of length LATEX(c)ENDLATEX, we have LATEX[a^2 + b^2 = c^2]ENDLATEX. This fact is known as the Pythagorean theorem."
question.text = currentQuestion

if (question.text?.contains("LATEX"))! {
        let tempString = question.text!
        let tempMutableString = NSMutableAttributedString(string: tempString)
        let pattern = NSRegularExpression.escapedPattern(for: "LATEX")
        let regex = try? NSRegularExpression(pattern: pattern, options: [])
        if let matches = regex?.matches(in: tempString, options: [], range: NSRange(location: 0, length: tempString.count)) {
               var i = 0
               while i < matches.count {
                    let range1 = matches.reversed()[i+1].range
                    let range2 = matches.reversed()[i].range
                    let finalDistance = range2.location - range1.location + 5
                    let finalRange = NSRange(location: range1.location, length: finalDistance)
                    let startIndex = String.Index(utf16Offset: range1.location + 5, in: tempString)
                    let endIndex = String.Index(utf16Offset: range2.location - 3, in: tempString)
                    let substring = String(tempString[startIndex..<endIndex])
                    var image = UIImage()
                    image = imageWithLabel(string: substring)
                    let flip = UIImage(cgImage: image.cgImage!, scale: 2.5, orientation: .downMirrored)
                    let attachment = NSTextAttachment()
                    attachment.image = flip
                    attachment.bounds = CGRect(x: 0, y: -flip.size.height/2 + 10, width: flip.size.width, height: flip.size.height)
                    let replacement = NSAttributedString(attachment: attachment)
                    tempMutableString.replaceCharacters(in: finalRange, with: replacement)
                    question.attributedText = tempMutableString
                    i += 2
               }
        }
}

func imageWithLabel(string: String) -> UIImage {
        let label = MTMathUILabel()
        label.latex = string
        label.sizeToFit()
        UIGraphicsBeginImageContextWithOptions(label.bounds.size, false, 0)
        defer { UIGraphicsEndImageContext() }
        label.layer.render(in: UIGraphicsGetCurrentContext()!)
        return UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
}

推荐阅读