adding ellipsis to NSString

ぃ、小莉子 提交于 2019-12-13 13:04:27

问题


I have the following code, which I am trying to draw using core text and that's why I can't clip the text like what UILabel does. in other words I have to figure out the ellipsis ('...') my self.

 CGSize commentSize = [[self.sizeDictionary_ valueForKey:commentSizeKey] CGSizeValue];
        CGSize actualSize = [[self.sizeDictionary_ valueForKey:actualCommentSizeKey] CGSizeValue];

 NSString *actualComment = self.highlightItem_.comment;
        if (actualSize.height > commentSize.height){
            actualComment = [self.highlightItem_.comment stringByReplacingCharactersInRange:NSMakeRange(68, 3) withString:@"..."];
        }

I am having a difficult time finding the range of where the '...' based on a CGSize. What would be the best way in figuring this out?

Here's how I am drawing it:

CFStringRef string = CFBridgingRetain(actualComment);
        CFMutableAttributedStringRef comment = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
        CFAttributedStringReplaceString (comment ,CFRangeMake(0, 0), string);

        CGColorRef blue = CGColorRetain([UIColor colorWithRed:131/255.f green:204/255.f blue:253/255.f alpha:1.0].CGColor);
        CGColorRef gray = CGColorRetain([UIColor colorWithWhite:165/255.f alpha:1.0].CGColor);

        CFAttributedStringSetAttribute(comment, CFRangeMake(0, [name length]),kCTForegroundColorAttributeName, blue);
        CFAttributedStringSetAttribute(comment, CFRangeMake([name length],  [self.highlightItem_.comment length] - [name length]),kCTForegroundColorAttributeName, gray);

        CGColorRelease (blue);
        CGColorRelease (gray);

        CTFontRef nameFont = CTFontCreateWithName(CFBridgingRetain(kProximaNovaBold), 13.0f, nil);
        CFAttributedStringSetAttribute(comment,CFRangeMake(0, [name length]),kCTFontAttributeName,nameFont);

        CTFontRef commentFont = CTFontCreateWithName(CFBridgingRetain(kProximaNovaRegular), 13.0f, nil);
        CFAttributedStringSetAttribute(comment, CFRangeMake([name length],  [self.highlightItem_.comment length] - [name length]),kCTFontAttributeName,commentFont);

        CGFloat commentYOffset = floorf((self.commentHeight_ - commentSize.height)/2);

        CGContextSaveGState(context);
        CGRect captionFrame = CGRectMake(0, 0, rect.size.width - 80, commentSize.height);
        CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(comment);
        CGMutablePathRef captionFramePath = CGPathCreateMutable();
        CGPathAddRect(captionFramePath, NULL, captionFrame);

        CTFrameRef mainCaptionFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), captionFramePath, NULL);

        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextSetTextMatrix(context, CGAffineTransformIdentity);
        CGContextTranslateCTM(context, self.buttonSize_ + 25, self.imageHeight_ + self.commentHeight_ + 6 - commentYOffset);
        CGContextScaleCTM(context, 1.0, -1.0);

        CTFrameDraw(mainCaptionFrame, context);
        CGContextRestoreGState(context);

回答1:


EDIT

(My original answer here wasn't useful; it didn't handle multiple lines. If anyone wants to see it for historical interest, look in the edit history. I've deleted it since it causes more confusion than it solves. The current answer is correct code.)

What you need to do is let CTFramesetter work out all the lines except the last one. Then you can truncate the last one by hand if required.

- (void)drawRect:(CGRect)rect
{
  CGContextRef context = UIGraphicsGetCurrentContext();
  CGContextSetTextMatrix(context, CGAffineTransformIdentity);

  CGRect pathRect = CGRectMake(50, 200, 200, 40);
  CGPathRef path = CGPathCreateWithRect(pathRect, NULL);

  CFAttributedStringRef attrString = (__bridge CFTypeRef)[self attributedString];

  // Create the framesetter using the attributed string
  CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);

  // Create a single frame using the entire string (CFRange(0,0))
  // that fits inside of path.
  CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);

  // Draw the lines except the last one
  CFArrayRef lines = CTFrameGetLines(frame);
  CFIndex lineCount = CFArrayGetCount(lines);
  CGPoint origins[lineCount]; // I'm assuming that a stack variable is safe here.
                              // This would be bad if there were thousdands of lines, but that's unlikely.
  CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins);
  for (CFIndex i = 0; i < lineCount - 1; ++i) {
    CGContextSetTextPosition(context, pathRect.origin.x + origins[i].x, pathRect.origin.y + origins[i].y);
    CTLineDraw(CFArrayGetValueAtIndex(lines, i), context);
  }

  ///
  /// HERE'S THE INTERESTING PART
  ///
  // Make a new last line that includes the rest of the string.
  // The last line is already truncated (just with no truncation mark), so we can't truncate it again
  CTLineRef lastLine = CFArrayGetValueAtIndex(lines, lineCount - 1);
  CFIndex lastLocation = CTLineGetStringRange(lastLine).location;
  CFRange restRange = CFRangeMake(lastLocation, CFAttributedStringGetLength(attrString) - lastLocation);
  CFAttributedStringRef restOfString = CFAttributedStringCreateWithSubstring(NULL, attrString, restRange);
  CTLineRef restLine = CTLineCreateWithAttributedString(restOfString);


  // We need to provide the truncation mark. This is an ellipsis (Cmd-semicolon).
  // You could also use "\u2026". Don't use dot-dot-dot. It'll work, it's just not correct.
  // Obviously you could cache this…
  CTLineRef ellipsis = CTLineCreateWithAttributedString((__bridge CFTypeRef)
                                                        [[NSAttributedString alloc] initWithString:@"…"]);

  // OK, now let's truncate it and draw it. I'm being a little sloppy here. If ellipsis could possibly
  // be wider than the path width, then this will fail and truncateLine will be NULL and we'll crash.
  // Don't do that.
  CTLineRef truncatedLine = CTLineCreateTruncatedLine(restLine,
                                                      CGRectGetWidth(pathRect),
                                                      kCTLineTruncationEnd,
                                                      ellipsis);
  CGContextSetTextPosition(context, pathRect.origin.x + origins[lineCount - 1].x, pathRect.origin.y + origins[lineCount - 1].y);
  CTLineDraw(truncatedLine, context);

  CFRelease(truncatedLine);
  CFRelease(ellipsis);
  CFRelease(restLine);
  CFRelease(restOfString);
  CFRelease(frame);
  CFRelease(framesetter);
  CGPathRelease(path);
}



回答2:


How about something like this...

- (NSString *)truncate:(NSString *)string toWidth:(CGFloat)width withFont:(UIFont *)font {

    CGSize size = [string sizeWithFont:font];
    if (size.width <= width) return string;

    NSString *truncatedString = [string copy];
    NSString *ellipticalString = [truncatedString stringByAppendingString:@"..."];
    size = [ellipticalString sizeWithFont:font];

    while (size.width > width && truncatedString.length) {
        truncatedString = [truncatedString substringToIndex:(truncatedString.length-1)];
        ellipticalString = [truncatedString stringByAppendingString:@"..."];
        size = [ellipticalString sizeWithFont:font];
    }
    return ellipticalString;
}



回答3:


The easiest and simplest way,

NSString *theText = @"bla blah bla bhla bla bla";
NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[style setLineBreakMode:NSLineBreakByTruncatingTail];
[theText drawInRect:dirtyRect withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:style, NSParagraphStyleAttributeName,nil]];

for more



来源:https://stackoverflow.com/questions/15043384/adding-ellipsis-to-nsstring

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!