Skip to content

Commit 3d1888a

Browse files
authored
Merge pull request #16 from colinc86/develop
Develop
2 parents 3489517 + f2b3cc4 commit 3d1888a

File tree

11 files changed

+223
-139
lines changed

11 files changed

+223
-139
lines changed

.github/workflows/swift.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# This workflow will build a Swift project
22
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift
33

4-
name: Swift
4+
name: Unit Tests
55

66
on:
77
push:

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
A SwiftUI view that renders LaTeX equations.
44

5-
[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fcolinc86%2FLaTeXSwiftUI%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/colinc86/LaTeXSwiftUI) [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fcolinc86%2FLaTeXSwiftUI%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/colinc86/LaTeXSwiftUI)
5+
[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fcolinc86%2FLaTeXSwiftUI%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/colinc86/LaTeXSwiftUI) [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fcolinc86%2FLaTeXSwiftUI%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/colinc86/LaTeXSwiftUI) [![Unit Tests](https://github.com/colinc86/LaTeXSwiftUI/actions/workflows/swift.yml/badge.svg)](https://github.com/colinc86/LaTeXSwiftUI/actions/workflows/swift.yml)
66

77
<center><img src="./assets/images/device.png" width="362"></center>
88

@@ -23,7 +23,7 @@ A SwiftUI view that renders LaTeX equations.
2323
- [Format Equation Number](#format-equation-number)
2424
- [Unencode HTML](#🔗-unencode-html)
2525
- [Rendering Style](#🕶️-rendering-style)
26-
- [Animated](#🪩-animated)
26+
- [Rendering Animation](#🪩-animated)
2727
- [Caching](#🗄️-caching)
2828
- [Preloading](#🏃‍♀️-preloading)
2929

@@ -47,7 +47,7 @@ It won't
4747
Add the dependency to your package manifest file.
4848

4949
```swift
50-
.package(url: "https://github.com/colinc86/LaTeXSwiftUI", from: "1.2.1")
50+
.package(url: "https://github.com/colinc86/LaTeXSwiftUI", from: "1.2.2")
5151
```
5252

5353
## ⌨️ Usage
@@ -237,14 +237,14 @@ The view has four rendering styles. The `wait` style is the default style, and l
237237
| `wait` | No | *(default)* The view blocks the main queue until its finished rendering. |
238238

239239

240-
#### 🪩 Animated
240+
#### 🪩 Rendering Animation
241241

242-
The `animated` modifier applies to the view when using the asynchronous rendering styles `empty`, `original`, or `progress`.
242+
When using the asynchronous rendering styles `empty`, `original`, or `progress`, use this modifier to determine the animation applied to the transition between views. The default value is `none`.
243243

244244
```swift
245245
LaTeX(input)
246246
.renderingStyle(.original)
247-
.animated()
247+
.renderingAnimation(.easeIn)
248248
```
249249

250250
> In the above example, the input text will be displayed until the SVGs have been rendered at which point the rendered views will animate in to view.

Sources/LaTeXSwiftUI/Extensions/EnvironmentValues+Extensions.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ private struct RenderingStyleKey: EnvironmentKey {
7070
static let defaultValue: LaTeX.RenderingStyle = .wait
7171
}
7272

73-
private struct AnimatedKey: EnvironmentKey {
74-
static let defaultValue: Bool = false
73+
private struct RenderingAnimationKey: EnvironmentKey {
74+
static let defaultValue: Animation? = .none
7575
}
7676

7777
extension EnvironmentValues {
@@ -143,9 +143,9 @@ extension EnvironmentValues {
143143
}
144144

145145
/// Whether or not rendering should be animated.
146-
var animated: Bool {
147-
get { self[AnimatedKey.self] }
148-
set { self[AnimatedKey.self] = newValue }
146+
var renderingAnimation: Animation? {
147+
get { self[RenderingAnimationKey.self] }
148+
set { self[RenderingAnimationKey.self] = newValue }
149149
}
150150

151151
}

Sources/LaTeXSwiftUI/Extensions/View+Extensions.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,12 @@ public extension View {
154154
environment(\.renderingStyle, style)
155155
}
156156

157-
/// Sets whether or not the view will animate its render state changes.
157+
/// Sets the animation the view should apply to its rendered images.
158158
///
159-
/// - Parameter animate: Whether animations are enabled.
160-
/// - Returns: A view that animates its rendering.
161-
func animated(_ animate: Bool = true) -> some View {
162-
environment(\.animated, animate)
159+
/// - Parameter animation: The animation.
160+
/// - Returns: A view that animates its rendered state.
161+
func renderingAnimation(_ animation: Animation?) -> some View {
162+
environment(\.renderingAnimation, animation)
163163
}
164164

165165
}

Sources/LaTeXSwiftUI/LaTeX.swift

Lines changed: 90 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,8 @@ public struct LaTeX: View {
145145
/// The view's rendering style.
146146
@Environment(\.renderingStyle) private var renderingStyle
147147

148-
/// Whether or not rendering should be animated.
149-
@Environment(\.animated) private var animated
148+
/// The animation the view should apply to its rendered images.
149+
@Environment(\.renderingAnimation) private var renderingAnimation
150150

151151
/// The view's current display scale.
152152
@Environment(\.displayScale) private var displayScale
@@ -188,24 +188,27 @@ public struct LaTeX: View {
188188
// MARK: View body
189189

190190
public var body: some View {
191-
if renderState.rendered {
192-
bodyWithBlocks(renderState.blocks)
193-
}
194-
else {
195-
switch renderingStyle {
196-
case .empty:
197-
Text("")
198-
.task(render)
199-
case .original:
200-
Text(latex)
201-
.task(render)
202-
case .progress:
203-
ProgressView()
204-
.task(render)
205-
case .wait:
206-
bodyWithBlocks(syncBlocks)
191+
VStack(spacing: 0) {
192+
if renderState.rendered {
193+
bodyWithBlocks(renderState.blocks)
194+
}
195+
else {
196+
switch renderingStyle {
197+
case .empty:
198+
Text("")
199+
.task(render)
200+
case .original:
201+
Text(latex)
202+
.task(render)
203+
case .progress:
204+
ProgressView()
205+
.task(render)
206+
case .wait:
207+
bodyWithBlocks(syncBlocks)
208+
}
207209
}
208210
}
211+
.animation(renderingAnimation, value: renderState.rendered)
209212
}
210213

211214
}
@@ -229,7 +232,6 @@ extension LaTeX {
229232
/// Renders the view's components.
230233
@Sendable private func render() async {
231234
await renderState.render(
232-
animated: animated,
233235
unencodeHTML: unencodeHTML,
234236
parsingMode: parsingMode,
235237
font: font,
@@ -296,6 +298,58 @@ struct LaTeX_Previews: PreviewProvider {
296298
.previewLayout(.sizeThatFits)
297299
.previewDisplayName("Hello, LaTeX!")
298300

301+
VStack {
302+
LaTeX("Hello, $\\color{blue}\\LaTeX$")
303+
.imageRenderingMode(.original)
304+
305+
LaTeX("Hello, $\\LaTeX$")
306+
.imageRenderingMode(.template)
307+
}
308+
.previewDisplayName("Image Rendering Mode")
309+
310+
VStack {
311+
LaTeX("$\\asdf$")
312+
.errorMode(.error)
313+
314+
LaTeX("$\\asdf$")
315+
.errorMode(.original)
316+
317+
LaTeX("$\\asdf$")
318+
.errorMode(.rendered)
319+
}
320+
.previewDisplayName("Error Mode")
321+
322+
VStack {
323+
LaTeX("$x&lt;0$")
324+
.errorMode(.error)
325+
326+
LaTeX("$x&lt;0$")
327+
.unencoded()
328+
.errorMode(.error)
329+
}
330+
.previewDisplayName("Unencoded")
331+
332+
VStack {
333+
LaTeX("$a^2 + b^2 = c^2$")
334+
.parsingMode(.onlyEquations)
335+
336+
LaTeX("a^2 + b^2 = c^2")
337+
.parsingMode(.all)
338+
}
339+
.previewDisplayName("Parsing Mode")
340+
341+
VStack {
342+
LaTeX("Equation 1: $$x = 3$$")
343+
.blockMode(.blockViews)
344+
345+
LaTeX("Equation 1: $$x = 3$$")
346+
.blockMode(.blockText)
347+
348+
LaTeX("Equation 1: $$x = 3$$")
349+
.blockMode(.alwaysInline)
350+
}
351+
.previewDisplayName("Block Mode")
352+
299353
VStack {
300354
LaTeX("$$E = mc^2$$")
301355
.equationNumberMode(.right)
@@ -313,6 +367,23 @@ struct LaTeX_Previews: PreviewProvider {
313367
.formatEquationNumber { n in
314368
return "~[\(n)]~"
315369
}
370+
371+
VStack {
372+
LaTeX("Hello, $\\LaTeX$!")
373+
.renderingStyle(.wait)
374+
375+
LaTeX("Hello, $\\LaTeX$!")
376+
.renderingStyle(.empty)
377+
378+
LaTeX("Hello, $\\LaTeX$!")
379+
.renderingStyle(.original)
380+
.renderingAnimation(.default)
381+
382+
LaTeX("Hello, $\\LaTeX$!")
383+
.renderingStyle(.progress)
384+
.renderingAnimation(.easeIn)
385+
}
386+
.previewDisplayName("Rendering Style and Animated")
316387
}
317388

318389
}

Sources/LaTeXSwiftUI/Models/Component.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,26 @@ internal struct ComponentBlock: Hashable, Identifiable {
4444
components.count == 1 && !components[0].type.inline
4545
}
4646

47+
/// Creates the image view and its size for the given block.
48+
///
49+
/// If the block isn't an equation block, then this method returns `nil`.
50+
///
51+
/// - Parameter block: The block.
52+
/// - Returns: The image, its size, and any associated error text.
53+
@MainActor func image(
54+
font: Font,
55+
displayScale: CGFloat,
56+
renderingMode: Image.TemplateRenderingMode
57+
) -> (Image, CGSize, String?)? {
58+
guard isEquationBlock, let component = components.first else {
59+
return nil
60+
}
61+
return component.convertToImage(
62+
font: font,
63+
displayScale: displayScale,
64+
renderingMode: renderingMode)
65+
}
66+
4767
}
4868

4969
/// A LaTeX component.

Sources/LaTeXSwiftUI/Models/LaTeXRenderState.swift

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -59,27 +59,19 @@ extension LaTeXRenderState {
5959
/// Renders the views components.
6060
///
6161
/// - Parameters:
62-
/// - animated: The `animated` environment variable.
6362
/// - unencodeHTML: The `unencodeHTML` environment variable.
6463
/// - parsingMode: The `parsingMode` environment variable.
6564
/// - font: The `font environment` variable.
6665
/// - displayScale: The `displayScale` environment variable.
6766
/// - texOptions: The `texOptions` environment variable.
68-
func render(animated: Bool, unencodeHTML: Bool, parsingMode: LaTeX.ParsingMode, font: Font?, displayScale: CGFloat, texOptions: TeXInputProcessorOptions) async {
67+
func render(unencodeHTML: Bool, parsingMode: LaTeX.ParsingMode, font: Font?, displayScale: CGFloat, texOptions: TeXInputProcessorOptions) async {
6968
let isRen = await isRendering
7069
let ren = await rendered
7170
guard !isRen && !ren else {
7271
return
7372
}
7473
await MainActor.run {
75-
if animated {
76-
withAnimation {
77-
isRendering = true
78-
}
79-
}
80-
else {
81-
isRendering = true
82-
}
74+
isRendering = true
8375
}
8476

8577
let renderedBlocks = await Renderer.shared.render(
@@ -89,18 +81,9 @@ extension LaTeXRenderState {
8981
texOptions: texOptions)
9082

9183
await MainActor.run {
92-
if animated {
93-
withAnimation {
94-
blocks = renderedBlocks
95-
isRendering = false
96-
rendered = true
97-
}
98-
}
99-
else {
100-
blocks = renderedBlocks
101-
isRendering = false
102-
rendered = true
103-
}
84+
blocks = renderedBlocks
85+
isRendering = false
86+
rendered = true
10487
}
10588
}
10689

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//
2+
// ComponentBlockText.swift
3+
// LaTeXSwiftUI
4+
//
5+
// Copyright (c) 2023 Colin Campbell
6+
//
7+
// Permission is hereby granted, free of charge, to any person obtaining a copy
8+
// of this software and associated documentation files (the "Software"), to
9+
// deal in the Software without restriction, including without limitation the
10+
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11+
// sell copies of the Software, and to permit persons to whom the Software is
12+
// furnished to do so, subject to the following conditions:
13+
//
14+
// The above copyright notice and this permission notice shall be included in
15+
// all copies or substantial portions of the Software.
16+
//
17+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23+
// IN THE SOFTWARE.
24+
//
25+
26+
import SwiftUI
27+
28+
/// Displays a component block as a text view.
29+
internal struct ComponentBlockText: View {
30+
31+
/// The component blocks to display in the view.
32+
let block: ComponentBlock
33+
34+
// MARK: Private properties
35+
36+
/// The rendering mode to use with the rendered MathJax images.
37+
@Environment(\.imageRenderingMode) private var imageRenderingMode
38+
39+
/// What to do in the case of an error.
40+
@Environment(\.errorMode) private var errorMode
41+
42+
/// The view's font.
43+
@Environment(\.font) private var font
44+
45+
/// The view's current display scale.
46+
@Environment(\.displayScale) private var displayScale
47+
48+
/// The view's block rendering mode.
49+
@Environment(\.blockMode) private var blockMode
50+
51+
// MARK: View body
52+
53+
var body: Text {
54+
block.components.enumerated().map { i, component in
55+
return component.convertToText(
56+
font: font ?? .body,
57+
displayScale: displayScale,
58+
renderingMode: imageRenderingMode,
59+
errorMode: errorMode,
60+
blockRenderingMode: blockMode,
61+
isInEquationBlock: block.isEquationBlock)
62+
}.reduce(Text(""), +)
63+
}
64+
65+
}
66+
67+
struct ComponentBlockTextPreviews: PreviewProvider {
68+
static var previews: some View {
69+
ComponentBlockText(block: ComponentBlock(components: [
70+
Component(text: "Hello, World!", type: .text)
71+
]))
72+
}
73+
}

0 commit comments

Comments
 (0)