使用NSParagraphStyle从NSAttributedString计算高度

时间:2023-02-01 07:52:01

I want to calculate the height of a NSAttributedString with the NSParagraphStyle attribute.

我想用NSParagraphStyle属性计算NSAttributedString的高度。

I thought it will be easy to create a UILabel with higher spacing between the lines but i can't calculate the right height for my UITableViewCell.

我认为在线条之间创建一个间距较大的UILabel会很容易,但我无法为UITableViewCell计算正确的高度。

I tried to calculate it with boundingRectWithSize:options: but it's not working at all…

我尝试用boundingRectWithSize:options来计算它:但它根本不起作用......

2 个解决方案

#1


1  

I use NSLayoutManager's usedRectForTextContainer: with a TextView stack disconnected from a UITableView. I answered a similar Stack Overflow question and explained how to implement it.

我使用NSLayoutManager的usedRectForTextContainer:从UITableView断开TextView堆栈。我回答了类似的Stack Overflow问题并解释了如何实现它。

#2


0  

When the convinient methods from apple dont work, this category provides a good aproximation in most cases.

当苹果的方便方法不起作用时,这个类别在大多数情况下提供了良好的近似。

@implementation NSAttributedString (PixLib)

- (CGFloat)heightForWidth:(CGFloat)width {
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, NULL, CGRectMake(0, 0, width, 99999));
    CGFloat h = [self heightForPath:path];
    CGPathRelease(path);
    return h;
}

- (CGFloat)heightForPath:(CGPathRef)path {
    CGFloat height = 0;
    CTFrameRef frame =  [self cfframeForPath:path];
    if (frame != NULL) {
        NSArray* lines = (__bridge NSArray*)CTFrameGetLines(frame);

        int l = [lines count];
        if (l > 1) {
            CGPoint origins[l];

            CTFrameGetLineOrigins(frame, CFRangeMake(0, l), origins);

            CGFloat yFirst = origins[0].y;
            CGFloat yLast = origins[l-1].y;

            CGFloat ascent, descent, leading;
            CTLineGetTypographicBounds((__bridge CTLineRef)[lines objectAtIndex:l-1], &ascent, &descent, &leading);

            height = ceilf((ascent+descent+leading)*1.3) + yFirst-yLast;
        } else {
            if (l==1) {
                CGFloat ascent, descent, leading;
                CTLineGetTypographicBounds((__bridge CTLineRef)[lines objectAtIndex:0], &ascent, &descent, &leading);
                height = ceilf(ascent+descent+leading)*1.3;
            }
        }
        CFRelease(frame);
    }
    return height;
}

- (CTFrameRef)cfframeForPath:(CGPathRef)p {
    // hack to avoid bugs width different behavior in iOS <4.3 and >4.3
    CGMutablePathRef path = CGPathCreateMutable();
    CGRect r = CGPathGetBoundingBox(p);

    CGAffineTransform t = CGAffineTransformIdentity;

    t = CGAffineTransformTranslate(t, r.origin.x, r.origin.y);
    t = CGAffineTransformScale(t, 1, -1);
    t = CGAffineTransformTranslate(t, r.origin.x, - ( r.origin.y + r.size.height ));
    CGPathAddPath(path, &t, p);

    CGPathMoveToPoint(path, NULL, 0, 0);
    CGPathCloseSubpath(path);
    // hack end

    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)self);
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);

    CFRelease(framesetter);
    CGPathRelease(path);
    return frame;
}

@end

#1


1  

I use NSLayoutManager's usedRectForTextContainer: with a TextView stack disconnected from a UITableView. I answered a similar Stack Overflow question and explained how to implement it.

我使用NSLayoutManager的usedRectForTextContainer:从UITableView断开TextView堆栈。我回答了类似的Stack Overflow问题并解释了如何实现它。

#2


0  

When the convinient methods from apple dont work, this category provides a good aproximation in most cases.

当苹果的方便方法不起作用时,这个类别在大多数情况下提供了良好的近似。

@implementation NSAttributedString (PixLib)

- (CGFloat)heightForWidth:(CGFloat)width {
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, NULL, CGRectMake(0, 0, width, 99999));
    CGFloat h = [self heightForPath:path];
    CGPathRelease(path);
    return h;
}

- (CGFloat)heightForPath:(CGPathRef)path {
    CGFloat height = 0;
    CTFrameRef frame =  [self cfframeForPath:path];
    if (frame != NULL) {
        NSArray* lines = (__bridge NSArray*)CTFrameGetLines(frame);

        int l = [lines count];
        if (l > 1) {
            CGPoint origins[l];

            CTFrameGetLineOrigins(frame, CFRangeMake(0, l), origins);

            CGFloat yFirst = origins[0].y;
            CGFloat yLast = origins[l-1].y;

            CGFloat ascent, descent, leading;
            CTLineGetTypographicBounds((__bridge CTLineRef)[lines objectAtIndex:l-1], &ascent, &descent, &leading);

            height = ceilf((ascent+descent+leading)*1.3) + yFirst-yLast;
        } else {
            if (l==1) {
                CGFloat ascent, descent, leading;
                CTLineGetTypographicBounds((__bridge CTLineRef)[lines objectAtIndex:0], &ascent, &descent, &leading);
                height = ceilf(ascent+descent+leading)*1.3;
            }
        }
        CFRelease(frame);
    }
    return height;
}

- (CTFrameRef)cfframeForPath:(CGPathRef)p {
    // hack to avoid bugs width different behavior in iOS <4.3 and >4.3
    CGMutablePathRef path = CGPathCreateMutable();
    CGRect r = CGPathGetBoundingBox(p);

    CGAffineTransform t = CGAffineTransformIdentity;

    t = CGAffineTransformTranslate(t, r.origin.x, r.origin.y);
    t = CGAffineTransformScale(t, 1, -1);
    t = CGAffineTransformTranslate(t, r.origin.x, - ( r.origin.y + r.size.height ));
    CGPathAddPath(path, &t, p);

    CGPathMoveToPoint(path, NULL, 0, 0);
    CGPathCloseSubpath(path);
    // hack end

    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)self);
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);

    CFRelease(framesetter);
    CGPathRelease(path);
    return frame;
}

@end