I use a table view to show a list of books, where each cell has a UILabel
that shows the book\'s name and another UILabel
the shows the book\'s aut
This answer is based on https://stackoverflow.com/a/30813691/2123122. Here is the sample code.
@interface CustomLabel()
@property (nonatomic, retain) NSLayoutManager *layoutManager;
@property (nonatomic, retain) NSTextContainer *textContainer;
@property (nonatomic, retain) NSTextStorage *textStorage;
@end
@implementation CustomLabel
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self configureTextkitStack];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self configureTextkitStack];
}
return self;
}
- (void)configureTextkitStack {
_textContainer = [[NSTextContainer alloc] init];
_textContainer.lineFragmentPadding = 0;
_textContainer.maximumNumberOfLines = self.numberOfLines;
_textContainer.lineBreakMode = self.lineBreakMode;
_textContainer.widthTracksTextView = YES;
_layoutManager = [[NSLayoutManager alloc] init];
[_layoutManager addTextContainer:self.textContainer];
[_textContainer setLayoutManager:self.layoutManager];
_textStorage = [[NSTextStorage alloc] init];
[_textStorage addLayoutManager:self.layoutManager];
[self.layoutManager setTextStorage:_textStorage];
}
- (NSRange)rangeForTokenInsertion {
self.textContainer.size = self.bounds.size;
if (self.attributedText.length > 0 ){
[self.textStorage setAttributedString:[[NSMutableAttributedString alloc]initWithAttributedString:self.attributedText]];
}
if (self.text.length == 0) {
return NSMakeRange(NSNotFound, 0);
}
NSInteger glyphIndex = [self.layoutManager glyphIndexForCharacterAtIndex:self.textStorage.length - 1];
NSRange range = [self.layoutManager truncatedGlyphRangeInLineFragmentForGlyphAtIndex:glyphIndex];
return range;
}
Now you can use this as follows:
NSRange range = [self.label rangeForTokenInsertion];
NSString *token = @"...+2 authors";
if (range.location != NSNotFound ) {
range.length += token.length;
range.location -= token.length;
}
if (range.location != NSNotFound) {
NSString *finalString = [self.label.text stringByReplacingCharactersInRange:range withString:token];
self.label.text = finalString;
}
I have created a UILabel subclass called ResponsiveLabel which handles custom truncation token as well as applying styles to patterns like userhandles, URLs, hashtags etc.
Edit: Generalized the solution to work with any "truncation location" string. Previous version only truncated at instance of string @" +"
. Edit allows you to define where you want the truncation to happen.
I took my answer from this question (which was an answer modified from the answer on this site) and tailored it to fit your needs. Create a new NSString
interface where you can send your string to be custom-truncated.
NOTE: This solution is for iOS 7+ only. To use in iOS 6, use sizeWithFont:
instead of sizeWithAttributes:
in the NSString+TruncateToWidth.m file.
NSString+TruncateToWidth.h
@interface NSString (TruncateToWidth)
- (NSString*)stringByTruncatingAtString:(NSString *)string toWidth:(CGFloat)width withFont:(UIFont *)font;
@end
NSString+TruncateToWidth.m
#import "NSString+TruncateToWidth.h"
#define ellipsis @"…"
@implementation NSString (TruncateToWidth)
- (NSString*)stringByTruncatingAtString:(NSString *)string toWidth:(CGFloat)width withFont:(UIFont *)font
{
// If the string is already short enough, or
// if the 'truncation location' string doesn't exist
// go ahead and pass the string back unmodified.
if ([self sizeWithAttributes:@{NSFontAttributeName:font}].width < width ||
[self rangeOfString:string].location == NSNotFound)
return self;
// Create copy that will be the returned result
NSMutableString *truncatedString = [self mutableCopy];
// Accommodate for ellipsis we'll tack on the beginning
width -= [ellipsis sizeWithAttributes:@{NSFontAttributeName:font}].width;
// Get range of the passed string. Note that this only works to the first instance found,
// so if there are multiple, you need to modify your solution
NSRange range = [truncatedString rangeOfString:string];
range.length = 1;
while([truncatedString sizeWithAttributes:@{NSFontAttributeName:font}].width > width
&& range.location > 0)
{
range.location -= 1;
[truncatedString deleteCharactersInRange:range];
}
// Append ellipsis
range.length = 0;
[truncatedString replaceCharactersInRange:range withString:ellipsis];
return truncatedString;
}
@end
Using it:
// Make sure to import the header file where you want to use it
myLabel.text = [@"Benjamin Walker Jackson + 2 authors" stringByTruncatingAtString:@" +" toWidth:myLabel.frame.size.width withFont:myLabel.font];
// Sample Result: Benjamin Walte... + 2 authors
UILabel cannot handle such truncate function other than under iOS 7. either you should truncate the string according to the fixed(calculated with uilabel font size) length yourself but that's a headache, or you can use two UILabels. 1st uilabel with fixed size for first author. it will auto truncate at the end as you know already. then the 2nd uilabel should be drawn exactly on the righ side of first label with of " + 2 other authors".
Edit: take a look at @Stonz2 answer