I would like to display math terms inside a text, in particular in an inline mode, i.e. inside a sentence.
Using LaTeX, this would for example look like: "Given a right triangle having catheti of length \(a\) resp. \(b\) and a hypotenuse of length \(c\), we have \[a^2 + b^2 = c^2.\] This fact is known as the Pythagorean theorem."
Does anybody know how this can be achieved in Swift?
(I know that this example may be achieved in Swift without LaTeX-like tools. However, the expressions in my mind are in fact more complex than in this example, I do need the power of LaTeX.)
The optimal way would be a UITextView
-like class which recognizes the math delimiters \(,\) resp. \[,\], recognizes LaTeX code inside these delimiters, and formats the text accordingly.
In the Khan Academy app, this problem seems to be solved as the screenshots in the Apple App Store/Google Play Store show inline (LaTeX) math.
I’ve found the package iosMath which provides a UILabel
-like class MTMathUILabel
. As this class can display solely formulas, this seems to be not good enough for my purpose, except if there was a method which takes a LaTeX source text such as in the example above, formats expressions such as \(a\) into tiny MTMathUILabel
s and sets these labels between the other text components. As I am new to Swift, I do not know whether and how this can be achieved. Moreover, this seems to be very difficult from a typographical point of view as there will surely occur difficulties with line breaks. And there might occur performance issues if there are a large number of such labels on the screen at the same time?
It is possible to achieve what I want using a WKWebView
and MathJax or KaTeX, which is also a hack, of course. This leads to other difficulties, e.g. if one wants to set several of these WKWebView
s on a screen, e.g. inside UITableViewCell
s.
Using iosMath, my solution on how to get a UILabel to have inline LaTeX is to include LATEX and ENDLATEX markers with no space. I replaced all ranges with an image of the MTMathUILabel, going from last range to first range so the positions don't get screwed up (This solution allows for multiple markers). The image returned from my function is flipped so i used .downMirrored orientation, and i sized it to fit my text, so you might need to fix the numbers a little for the flip scale of 2.5 and the y value for the attachment.bounds.
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()
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With