Skip to content

Commit 763668d

Browse files
authored
Fix attachment size caching (#13)
1 parent a03c1e1 commit 763668d

2 files changed

Lines changed: 36 additions & 16 deletions

File tree

Examples/TextualDemo/TextualDemo/InlineTextDemo.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,16 @@ struct InlineTextDemo: View {
2323
**Working late on the new feature** has been surprisingly fun—_even when the build \
2424
fails_ :confused_dog:, a quick refactor usually gets things back on track :doge:, \
2525
and when it doesn’t, I just roll with it :dogroll: until the solution finally \
26-
clicks (though sometimes I still end up a bit :sad_dog:).
26+
clicks (though sometimes I still end up a bit **:confused_dog:** or _small \
27+
:confused_dog:_... plus another :confused_dog: for good measure).
2728
""",
2829
patternOptions: .init(emoji: .mastoEmoji)
2930
)
31+
.textual.inlineStyle(
32+
InlineStyle()
33+
.strong(.bold, .fontScale(1.3))
34+
.emphasis(.italic, .fontScale(0.85))
35+
)
3036
}
3137
Section("Custom Inline Style") {
3238
InlineText(

Sources/Textual/Internal/TextFragment/TextBuilder.swift

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import SwiftUI
77
// resize. When the container size changes, attachment sizes are recomputed and the cache
88
// is consulted. If the new sizes hash to the same key, the cached Text is reused.
99
//
10-
// The cache key is derived from the hash of [AnyAttachment: CGSize]. Since attachment
10+
// The cache key is derived from the hash of [AttachmentKey: CGSize]. Since attachment
1111
// sizes often remain constant or repeat during incremental resize (e.g., window resizing),
1212
// this compact key enables effective caching without storing the full proposal or
1313
// attributed string. The cache has a count limit of 10 to prevent unbounded growth.
@@ -21,7 +21,7 @@ extension TextFragment {
2121
var text: Text
2222

2323
@ObservationIgnored private let content: Content
24-
@ObservationIgnored private let cache: NSCache<KeyBox<[AnyAttachment: CGSize]>, Box<Text>>
24+
@ObservationIgnored private let cache: NSCache<KeyBox<[AttachmentKey: CGSize]>, Box<Text>>
2525

2626
init(_ content: Content, environment: TextEnvironmentValues) {
2727
let attachmentSizes = content.attachmentSizes(for: .unspecified, in: environment)
@@ -61,24 +61,26 @@ extension TextFragment {
6161
extension Text {
6262
fileprivate init(
6363
attributedString: some AttributedStringProtocol,
64-
attachmentSizes: [AnyAttachment: CGSize],
64+
attachmentSizes: [AttachmentKey: CGSize],
6565
in environment: TextEnvironmentValues
6666
) {
6767
let textValues = attributedString.runs.map { run in
6868
var text: Text
6969

70-
if let attachment = run.textual.attachment,
71-
let size = attachmentSizes[attachment]
72-
{
73-
// Create placeholder
74-
var environment = environment
75-
environment.font = run.font ?? environment.font
70+
var runEnvironment = environment
71+
runEnvironment.font = run.font ?? environment.font
72+
73+
let key = run.textual.attachment.map {
74+
AttachmentKey(attachment: $0, font: runEnvironment.font)
75+
}
7676

77+
if let key, let size = attachmentSizes[key] {
78+
// Create placeholder
7779
text = Text(placeholderSize: size)
78-
.baselineOffset(attachment.baselineOffset(in: environment))
80+
.baselineOffset(key.attachment.baselineOffset(in: runEnvironment))
7981
.customAttribute(
8082
AttachmentAttribute(
81-
attachment,
83+
key.attachment,
8284
presentationIntent: run.presentationIntent
8385
)
8486
)
@@ -107,16 +109,28 @@ extension Text {
107109
extension AttributedStringProtocol {
108110
fileprivate func attachmentSizes(
109111
for proposal: ProposedViewSize, in environment: TextEnvironmentValues
110-
) -> [AnyAttachment: CGSize] {
112+
) -> [AttachmentKey: CGSize] {
111113
Dictionary(
112-
uniqueKeysWithValues: self.runs.compactMap { run in
114+
self.runs.compactMap { run in
113115
guard let attachment = run.textual.attachment else {
114116
return nil
115117
}
116118
var environment = environment
117119
environment.font = run.font ?? environment.font
118-
return (attachment, attachment.sizeThatFits(proposal, in: environment))
119-
}
120+
return (
121+
AttachmentKey(
122+
attachment: attachment,
123+
font: environment.font
124+
),
125+
attachment.sizeThatFits(proposal, in: environment)
126+
)
127+
},
128+
uniquingKeysWith: { existing, _ in existing }
120129
)
121130
}
122131
}
132+
133+
private struct AttachmentKey: Hashable {
134+
let attachment: AnyAttachment
135+
let font: Font?
136+
}

0 commit comments

Comments
 (0)