88import UIKit
99
1010final class NitroTextImpl {
11- private let nitroTextView : NitroTextView
11+ private weak var nitroTextView : NitroTextView ?
1212 private var currentTextAlignment : NSTextAlignment = . natural
1313 private var currentTransform : TextTransform = . none
1414 private var currentEllipsize : NSLineBreakMode = . byTruncatingTail
@@ -18,14 +18,13 @@ final class NitroTextImpl {
1818 }
1919
2020 func setSelectable( _ selectable: Bool ? ) {
21- nitroTextView. isSelectable = selectable ?? true
21+ nitroTextView? . isSelectable = selectable ?? true
2222 }
2323
2424 func setNumberOfLines( _ value: Double ? ) {
2525 let n = Int ( value ?? 0 )
26- nitroTextView. textContainer. maximumNumberOfLines = n
27- nitroTextView. textContainer. lineBreakMode = effectiveLineBreakMode ( forLines: n)
28- nitroTextView. setNeedsLayout ( )
26+ nitroTextView? . textContainer. maximumNumberOfLines = n
27+ nitroTextView? . textContainer. lineBreakMode = effectiveLineBreakMode ( forLines: n)
2928 }
3029
3130 func setEllipsizeMode( _ mode: EllipsizeMode ? ) {
@@ -37,9 +36,8 @@ final class NitroTextImpl {
3736 default : currentEllipsize = . byTruncatingTail
3837 }
3938 // Re-apply to container based on current numberOfLines
40- let n = nitroTextView. textContainer. maximumNumberOfLines
41- nitroTextView. textContainer. lineBreakMode = effectiveLineBreakMode ( forLines: n)
42- nitroTextView. setNeedsLayout ( )
39+ guard let n = nitroTextView? . textContainer. maximumNumberOfLines else { return }
40+ nitroTextView? . textContainer. lineBreakMode = effectiveLineBreakMode ( forLines: n)
4341 }
4442
4543 private func effectiveLineBreakMode( forLines n: Int ) -> NSLineBreakMode {
@@ -51,20 +49,24 @@ final class NitroTextImpl {
5149 switch currentEllipsize {
5250 case . byClipping:
5351 return . byClipping
54- default :
52+ case . byTruncatingHead:
53+ return . byTruncatingHead
54+ case . byTruncatingMiddle:
5555 return . byTruncatingMiddle
56+ default :
57+ return . byTruncatingTail
5658 }
5759 }
5860
5961 func setText( _ attributedText: NSAttributedString ) {
60- if let storage = nitroTextView. tkStorage ?? nitroTextView. layoutManager. textStorage {
62+ if let storage = nitroTextView? . tkStorage ?? nitroTextView? . layoutManager. textStorage {
6163 storage. beginEditing ( )
6264 storage. setAttributedString ( attributedText)
6365 storage. endEditing ( )
6466 } else {
65- nitroTextView. attributedText = attributedText
67+ nitroTextView? . attributedText = attributedText
6668 }
67- nitroTextView. setNeedsLayout ( )
69+ nitroTextView? . setNeedsLayout ( )
6870 }
6971
7072 func setPlainText( _ value: String ? ) {
@@ -80,7 +82,7 @@ final class NitroTextImpl {
8082 case . some( . left) : currentTextAlignment = . left
8183 default : currentTextAlignment = . natural
8284 }
83- nitroTextView. textAlignment = currentTextAlignment
85+ nitroTextView? . textAlignment = currentTextAlignment
8486 }
8587
8688 func setTextTransform( _ transform: TextTransform ? ) {
@@ -94,12 +96,12 @@ final class NitroTextImpl {
9496
9597 func setFragments( _ fragments: [ Fragment ] ? ) {
9698 guard let fragments = fragments, !fragments. isEmpty else {
97- nitroTextView. attributedText = nil
99+ nitroTextView? . attributedText = nil
98100 return
99101 }
100102
101103 let result = NSMutableAttributedString ( )
102- let defaultColor = nitroTextView. textColor ?? UIColor . label
104+ let defaultColor = nitroTextView? . textColor ?? UIColor . label
103105
104106 for fragment in fragments {
105107 guard let rawText = fragment. text else { continue }
@@ -135,7 +137,7 @@ final class NitroTextImpl {
135137 private func makeFont( for fragment: Fragment ) -> ( value: UIFont , isItalic: Bool ) {
136138 let resolvedSize : CGFloat = {
137139 if let s = fragment. fontSize { return CGFloat ( s) }
138- if let current = nitroTextView. font? . pointSize { return current }
140+ if let current = nitroTextView? . font? . pointSize { return current }
139141 return 14.0
140142 } ( )
141143 let weightToken = fragment. fontWeight ?? FontWeight . normal
@@ -177,7 +179,7 @@ final class NitroTextImpl {
177179 para. alignment = currentTextAlignment
178180 }
179181
180- let n = nitroTextView. textContainer. maximumNumberOfLines
182+ guard let n = nitroTextView? . textContainer. maximumNumberOfLines else { return para }
181183 para. lineBreakMode = effectiveLineBreakMode ( forLines: n)
182184 return para
183185 }
@@ -213,6 +215,54 @@ final class NitroTextImpl {
213215
214216
215217extension NitroTextImpl {
218+ struct FragmentTopDefaults {
219+ let fontSize : Double ?
220+ let fontWeight : FontWeight ?
221+ let fontColor : String ?
222+ let fontStyle : FontStyle ?
223+ let lineHeight : Double ?
224+ let textAlign : TextAlign ?
225+ let textTransform : TextTransform ?
226+ }
227+
228+ func apply( fragments: [ Fragment ] ? , text: String ? , top: FragmentTopDefaults ) {
229+ // Fast path: no fragments, but we have plain text
230+ guard let fragments, !fragments. isEmpty else {
231+ if let t = text {
232+ let single = Fragment (
233+ fontSize: top. fontSize,
234+ fontWeight: top. fontWeight,
235+ fontColor: top. fontColor,
236+ fontStyle: top. fontStyle,
237+ lineHeight: top. lineHeight,
238+ text: t,
239+ numberOfLines: nil ,
240+ textAlign: top. textAlign,
241+ textTransform: top. textTransform
242+ )
243+ setFragments ( [ single] )
244+ } else {
245+ setFragments ( nil )
246+ }
247+ return
248+ }
249+
250+ var merged : [ Fragment ] = [ ]
251+ merged. reserveCapacity ( fragments. count)
252+
253+ for var frag in fragments {
254+ if frag. text == nil { frag. text = " " }
255+ if frag. fontSize == nil , let v = top. fontSize { frag. fontSize = v }
256+ if frag. fontWeight == nil , let v = top. fontWeight { frag. fontWeight = v }
257+ if frag. fontStyle == nil , let v = top. fontStyle { frag. fontStyle = v }
258+ if frag. lineHeight == nil , let v = top. lineHeight, v > 0 { frag. lineHeight = v }
259+ if frag. fontColor == nil , let v = top. fontColor, !v. isEmpty { frag. fontColor = v }
260+ if frag. textAlign == nil , let v = top. textAlign { frag. textAlign = v }
261+ if frag. textTransform == nil , let v = top. textTransform { frag. textTransform = v }
262+ merged. append ( frag)
263+ }
264+ setFragments ( merged)
265+ }
216266 private static func fontWeightFromString( _ s: FontWeight ) -> UIFont . Weight {
217267 switch s {
218268 case . ultralight:
0 commit comments