减少多行UILabel中最后一行的宽度

Pau*_*len 20 cocoa-touch objective-c uilabel ios

我正在实现一个"阅读更多"的功能,就像Apple的AppStore中那样.但是,我使用的是多线UILabel.看看Apple的AppStore,它们如何减少最后一条可见线的宽度以适应"更多"文本并仍然截断尾部(见图)?

来自AppStore的iBooks示例图片

rde*_*mar 13

这似乎有效,至少在我完成的测试数量有限的情况下.有两种公共方法.如果您有多个标签都具有相同的行数,则可以使用较短的标签 - 只需更改顶部的kNumberOfLines即可匹配您想要的标签.如果需要传递不同标签的行数,请使用较长的方法.请务必将您在IB中制作的标签类更改为RDLabel.使用这些方法而不是setText:.如果需要,这些方法会将标签的高度扩展为kNumberOfLines,如果仍然被截断,则会将其展开以适合触摸的整个字符串.目前,您可以触摸标签中的任何位置.改变它不应该太难,所以只能触及...... Mer会引起扩张.

#import "RDLabel.h"
#define kNumberOfLines 2
#define ellipsis @"...Mer ? "

@implementation RDLabel {
    NSString *string;
}

#pragma Public Methods

- (void)setTruncatingText:(NSString *) txt {
    [self setTruncatingText:txt forNumberOfLines:kNumberOfLines];
}

- (void)setTruncatingText:(NSString *) txt forNumberOfLines:(int) lines{
    string = txt;
    self.numberOfLines = 0;
    NSMutableString *truncatedString = [txt mutableCopy];
    if ([self numberOfLinesNeeded:truncatedString] > lines) {
        [truncatedString appendString:ellipsis];
        NSRange range = NSMakeRange(truncatedString.length - (ellipsis.length + 1), 1);
        while ([self numberOfLinesNeeded:truncatedString] > lines) {
            [truncatedString deleteCharactersInRange:range];
            range.location--;
        }
        [truncatedString deleteCharactersInRange:range];  //need to delete one more to make it fit
        CGRect labelFrame = self.frame;
        labelFrame.size.height = [@"A" sizeWithFont:self.font].height * lines;
        self.frame = labelFrame;
        self.text = truncatedString;
        self.userInteractionEnabled = YES;
        UITapGestureRecognizer *tapper = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(expand:)];
        [self addGestureRecognizer:tapper];
    }else{
        CGRect labelFrame = self.frame;
        labelFrame.size.height = [@"A" sizeWithFont:self.font].height * lines;
        self.frame = labelFrame;
        self.text = txt;
    }
}

#pragma Private Methods

-(int)numberOfLinesNeeded:(NSString *) s {
    float oneLineHeight = [@"A" sizeWithFont:self.font].height;
    float totalHeight = [s sizeWithFont:self.font constrainedToSize:CGSizeMake(self.bounds.size.width, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping].height;
    return nearbyint(totalHeight/oneLineHeight);
}

-(void)expand:(UITapGestureRecognizer *) tapper {
    int linesNeeded = [self numberOfLinesNeeded:string];
    CGRect labelFrame = self.frame;
    labelFrame.size.height = [@"A" sizeWithFont:self.font].height * linesNeeded;
    self.frame = labelFrame;
    self.text = string;
}
Run Code Online (Sandbox Code Playgroud)

  • 只是想指出,在较高的UILabel级别上执行此操作要比使用CoreText路径慢得多.我发现在做需要大量格式化的工作时,考虑CoreText并不是一个坏主意. (2认同)

Pau*_*Slm 5

由于这篇文章是从2013年开始的,我想让我的Swift实现@rdelmar非常好的解决方案.

考虑到我们使用的是UILabel的SubClass:

private let kNumberOfLines = 2
private let ellipsis = " MORE"

private var originalString: String! // Store the original text in the init

private func getTruncatingText() -> String {
    var truncatedString = originalString.mutableCopy() as! String

    if numberOfLinesNeeded(truncatedString) > kNumberOfLines {
        truncatedString += ellipsis

        var range = Range<String.Index>(
            start: truncatedString.endIndex.advancedBy(-(ellipsis.characters.count + 1)),
            end: truncatedString.endIndex.advancedBy(-ellipsis.characters.count)
        )

        while numberOfLinesNeeded(truncatedString) > kNumberOfLines {
            truncatedString.removeRange(range)

            range.startIndex = range.startIndex.advancedBy(-1)
            range.endIndex = range.endIndex.advancedBy(-1)
        }
    }

    return truncatedString
}

private func getHeightForString(str: String) -> CGFloat {
    return str.boundingRectWithSize(
        CGSizeMake(self.bounds.size.width, CGFloat.max),
        options: [.UsesLineFragmentOrigin, .UsesFontLeading],
        attributes: [NSFontAttributeName: font],
        context: nil).height
}

private func numberOfLinesNeeded(s: String) -> Int {
    let oneLineHeight = "A".sizeWithAttributes([NSFontAttributeName: font]).height
    let totalHeight = getHeightForString(s)
    return Int(totalHeight / oneLineHeight)
}

func expend() {
    var labelFrame = self.frame
    labelFrame.size.height = getHeightForString(originalString)
    self.frame = labelFrame
    self.text = originalString
}

func collapse() {
    let truncatedText = getTruncatingText()
    var labelFrame = self.frame
    labelFrame.size.height = getHeightForString(truncatedText)
    self.frame = labelFrame
    self.text = truncatedText
}
Run Code Online (Sandbox Code Playgroud)

与旧解决方案不同,这对任何类型的文本属性(如NSParagraphStyleAttributeName)都有效.

请随意评论和评论.再次感谢@rdelmar.