Truncate UILabel in specific location

后端 未结 3 1320
小蘑菇
小蘑菇 2021-01-20 03:18

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

相关标签:
3条回答
  • 2021-01-20 03:40

    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.

    0 讨论(0)
  • 2021-01-20 03:48

    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
    
    0 讨论(0)
  • 2021-01-20 04:03

    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

    0 讨论(0)
提交回复
热议问题