I have a UITextView
with some attributed text, where the textContainer.maximumNumberOfLine
is set (3 in this case).
I'd like to find the index of the ellipsis character within the character range of the attributed string.
E.g:
Original String:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit"
String as displayed, after truncation:
Lorem ipsum
dolor sit amet,
consectetur...
How do I determine the index of the ...
?
Here's an extension function to NSAttributedString which does the job. Works for single & multiline text.
This took me all of about 8 hours to figure out, so I thought I'd post it as a Q&A.
(Swift 2.2)
/**
Returns the index of the ellipsis, if this attributed string is truncated, or NSNotFound otherwise.
*/
func truncationIndex(maximumNumberOfLines: Int, width: CGFloat) -> Int {
//Create a dummy text container, used for measuring & laying out the text..
let textContainer = NSTextContainer(size: CGSize(width: width, height: CGFloat.max))
textContainer.maximumNumberOfLines = maximumNumberOfLines
textContainer.lineBreakMode = NSLineBreakMode.ByTruncatingTail
let layoutManager = NSLayoutManager()
layoutManager.addTextContainer(textContainer)
let textStorage = NSTextStorage(attributedString: self)
textStorage.addLayoutManager(layoutManager)
//Determine the range of all Glpyhs within the string
var glyphRange = NSRange()
layoutManager.glyphRangeForCharacterRange(NSMakeRange(0, self.length), actualCharacterRange: &glyphRange)
var truncationIndex = NSNotFound
//Iterate over each 'line fragment' (each line as it's presented, according to your `textContainer.lineBreakMode`)
var i = 0
layoutManager.enumerateLineFragmentsForGlyphRange(glyphRange) { (rect, usedRect, textContainer, glyphRange, stop) in
if (i == maximumNumberOfLines - 1) {
//We're now looking at the last visible line (the one at which text will be truncated)
let lineFragmentTruncatedGlyphIndex = glyphRange.location
if lineFragmentTruncatedGlyphIndex != NSNotFound {
truncationIndex = layoutManager.truncatedGlyphRangeInLineFragmentForGlyphAtIndex(lineFragmentTruncatedGlyphIndex).location
}
stop.memory = true
}
i += 1
}
return truncationIndex
}
Note that this has not been tested beyond some simple cases. There may be edge cases requiring some adjustments..
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