在特定位置截断UILabel

时间:2014-07-16 14:17:26

标签: ios objective-c uilabel

我使用表格视图来显示图书清单,其中每个单元格都有一个显示图书名称的UILabel,另一个UILabel显示图书的作者(S)

我的问题是作者标签。一本书可以有多位作者,我希望它表现如下:

  • 如果图书有一位作者(' John Colman')标签应为:" John Colman"
  • 如果图书中有多位作者(' John Colman',' Bob Night',' Michael')标签应为:" John Colman +2位作者"

现在的问题是,我希望标签在' +'之前被截断。因此,例如,如果第一作者姓名很长,那么就说“本杰明·沃尔特·杰克逊”,我希望标签看起来像这样:

"Benjamin Walter Ja... +2 authors"

默认行为当然会截断最后的标签,所以它看起来像这样:

"Benjamin Walter Jackson +2 au..."  

如果我使用中间截断,则无法保证它会在正确的位置截断标签(在' +'之前)

我正在寻找一种尽可能高效的方法,而不会影响表格视图的滚动性能。

3 个答案:

答案 0 :(得分:5)

编辑:广泛使用任何“截断位置”字符串的解决方案。以前的版本仅在字符串@" +"的实例中截断。编辑允许您定义截断发生的位置。


我从this question(我在this site的答案中修改了答案)中得到了答案,并根据您的需求进行了调整。创建一个新的NSString接口,您可以在其中发送字符串以进行自定义截断。

注意:此解决方案仅适用于iOS 7+。要在iOS 6中使用,请在 NSString + TruncateToWidth.m 文件中使用sizeWithFont:代替sizeWithAttributes:

<强>的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

使用它:

// 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

答案 1 :(得分:0)

UILabel除了在iOS 7之外无法处理这样的截断功能。要么你应该根据自己固定的(用uilabel字体大小计算)长度截断字符串,但这很令人头疼,或者你可以使用两个UILabel。第一作者的固定大小的第一uilabel。如你所知,它会在最后自动截断。然后第二个uilabel应该在第一个标签的右边用“+ 2个其他作者”绘制。

编辑: 看看@Stonz2回答

答案 2 :(得分:0)

此答案基于https://stackoverflow.com/a/30813691/2123122。这是示例代码。

@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;
    }

现在你可以按如下方式使用它:

 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;
 }

我创建了一个名为ResponsiveLabel的UILabel子类,它处理自定义截断标记以及将样式应用于用户句柄,URL,主题标签等模式。