3

H!我正在编写一个带有子视图剪辑功能的文本视图。这个想法是让所有文本围绕所有子视图绘制。问题是要获得它的内容高度。

由于缺乏文档,我决定 CTFramesetterSuggestFrameSizeWithConstraints 的属性字典与 CTFramesetterCreateFrame 的属性字典相同。

这是我的剪切路径代码:

-(CFDictionaryRef)clippingPathsDictionary{
if(self.subviews.count==0)return NULL;

NSMutableArray *pathsArray = [[NSMutableArray alloc] init];

CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformScale(transform, 1, -1);
transform = CGAffineTransformTranslate(transform, 0, -self.bounds.size.height);

for (int i=0; i<self.subviews.count; i++) {
    UIView *clippingView = self.subviews[i];
    CGPathRef clipPath = CGPathCreateWithRect(clippingView.frame, &transform);

    NSDictionary *clippingPathDictionary = [NSDictionary dictionaryWithObject:(__bridge id)(clipPath) forKey:(__bridge NSString *)kCTFramePathClippingPathAttributeName];
    [pathsArray addObject:clippingPathDictionary];
    CFRelease(clipPath);
}


int eFrameWidth=0;
CFNumberRef frameWidth = CFNumberCreate(NULL, kCFNumberNSIntegerType, &eFrameWidth);

int eFillRule = kCTFramePathFillEvenOdd;
CFNumberRef fillRule = CFNumberCreate(NULL, kCFNumberNSIntegerType, &eFillRule);

int eProgression = kCTFrameProgressionTopToBottom;
CFNumberRef progression = CFNumberCreate(NULL, kCFNumberNSIntegerType, &eProgression);


CFStringRef keys[] = { kCTFrameClippingPathsAttributeName, kCTFramePathFillRuleAttributeName, kCTFrameProgressionAttributeName, kCTFramePathWidthAttributeName};
CFTypeRef values[] = { (__bridge CFTypeRef)(pathsArray), fillRule, progression, frameWidth};




CFDictionaryRef clippingPathsDictionary = CFDictionaryCreate(NULL,
                                                             (const void **)&keys, (const void **)&values,
                                                             sizeof(keys) / sizeof(keys[0]),
                                                             &kCFTypeDictionaryKeyCallBacks,
                                                             &kCFTypeDictionaryValueCallBacks);
return clippingPathsDictionary;}

我用它来绘制文本,它工作正常。这是我的绘图代码:

- (void)drawRect:(CGRect)rect{
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformScale(transform, 1, -1);
transform = CGAffineTransformTranslate(transform, 0, -rect.size.height);


CGContextRef context = UIGraphicsGetCurrentContext();
CGContextConcatCTM(context, transform);

CFAttributedStringRef attributedString = (__bridge CFAttributedStringRef)self.attributedString;
CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString(attributedString);

CFDictionaryRef  attributesDictionary = [self clippingPathsDictionary];
CGPathRef path = CGPathCreateWithRect(rect, &transform);
CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, self.attributedString.length), path, attributesDictionary);
CFRelease(path);
CFRelease(attributesDictionary);

CTFrameDraw(frame, context);
CFRelease(frameSetter);
CFRelease(frame);

CGSize contentSize = [self contentSizeForWidth:self.bounds.size.width];

CGPathRef highlightPath = CGPathCreateWithRect((CGRect){CGPointZero, contentSize}, &transform);
CGContextSetFillColorWithColor(context, [UIColor colorWithRed:.0 green:1 blue:.0 alpha:.3].CGColor);
CGContextAddPath(context, highlightPath);
CGContextDrawPath(context, kCGPathFill);
CFRelease(highlightPath);

}

结果是这样的:

绘图结果 这正是我所期待的!

最后,这是检查高度的代码:

-(CGSize)contentSizeForWidth:(float)width{
CFAttributedStringRef attributedString = (__bridge CFAttributedStringRef)self.attributedString;
CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString(attributedString);

CFDictionaryRef attributesDictionary = [self clippingPathsDictionary];
CGSize size = CTFramesetterSuggestFrameSizeWithConstraints(frameSetter, CFRangeMake(0, self.attributedString.length),  attributesDictionary, CGSizeMake(width, CGFLOAT_MAX), NULL);
NSLog(@"%s: size = %@",__PRETTY_FUNCTION__, NSStringFromCGSize(size));
CFRelease(attributesDictionary);
CFRelease(frameSetter);
return size;

}

输出如下所示:

Core Text Demo[2222:a0b] -[DATextView contentSizeForWidth:]: size = {729.71484, 0}

有人有什么解决办法吗?感谢关注:)

4

1 回答 1

10

在我看来,这就像一个 iOS7 错误。我一直在修修补补,在iOS6下,CTFramesetterSuggestFrameSizeWithConstraints返回一个高度大于0的大小。iOS7下的相同代码返回一个高度为0。

CTFramesetterSuggestFrameSizeWithConstraints以其错误和无证的行为而闻名。例如,您在 iOS6 下的代码由于CTFramesetterSuggestFrameSizeWithConstraints执行了不正确的计算而返回了不正确的高度,并且在计算中省略了最后一行。这是您在 iOS6 下运行的代码:

在此处输入图像描述

您应该打开有关这些问题的错误报告以及请求文档增强功能,网址为:https ://bugreport.apple.com

在 iOS7 下,使用 TextKit,你可以更快更优雅地达到同样的效果:

@interface LeoView : UIView

@property (nonatomic, assign) UIEdgeInsets contentInset;
@property (nonatomic, retain) NSLayoutManager* layoutManager;
@property (nonatomic, retain) NSTextContainer* textContainer;
@property (nonatomic, retain) NSTextStorage* textStorage;

@end

@implementation LeoView

-(void)awakeFromNib
{
    //Leo: This should not be done here. Just for example. You should do this in the init.

    self.textStorage = [[NSTextStorage alloc] initWithString:@"Lorem ipsum dolor [...]"];

    self.layoutManager = [[NSLayoutManager alloc] init];
    [self.textStorage addLayoutManager:self.layoutManager];

    self.textContainer = [[NSTextContainer alloc] initWithSize:self.bounds.size];
    [self.layoutManager addTextContainer:self.textContainer];
}

- (void)layoutSubviews
{
    [super layoutSubviews];

    //Leo: At this point the user may have set content inset, so need to take into account.
    CGSize size = self.bounds.size;
    size.width -= (self.contentInset.left + self.contentInset.right);
    size.height -= (self.contentInset.top + self.contentInset.bottom);

    self.textContainer.size = size;

    NSMutableArray* exclussionPaths = [NSMutableArray new];
    for (UIView* subview in self.subviews)
    {
        if(subview.isHidden)
            continue;

        CGRect frame = subview.frame;
        //Leo: If there is content inset, need to compensate.
        frame.origin.y -= self.contentInset.top;
        frame.origin.x -= self.contentInset.left;

        [exclussionPaths addObject:[UIBezierPath bezierPathWithRect:frame]];
    }
    self.textContainer.exclusionPaths = exclussionPaths;

    [self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect
{
    [self.layoutManager drawGlyphsForGlyphRange:[self.layoutManager glyphRangeForTextContainer:self.textContainer] atPoint:CGPointMake(self.contentInset.left, self.contentInset.top)];

    //Leo: Move used rectangle according to content inset.
    CGRect usedRect = [self.layoutManager usedRectForTextContainer:self.textContainer];
    usedRect.origin.x += self.contentInset.left;
    usedRect.origin.y += self.contentInset.top;

    CGAffineTransform transform = CGAffineTransformIdentity;
    transform = CGAffineTransformScale(transform, 1, -1);
    transform = CGAffineTransformTranslate(transform, 0, -rect.size.height);

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextConcatCTM(context, transform);
    CGPathRef highlightPath = CGPathCreateWithRect(usedRect, &transform);
    CGContextSetFillColorWithColor(context, [UIColor colorWithRed:.0 green:1 blue:.0 alpha:.3].CGColor);
    CGContextAddPath(context, highlightPath);
    CGContextDrawPath(context, kCGPathFill);
    CFRelease(highlightPath);
}

@end

这是contentInset设置为的结果UIEdgeInsetsMake(20, 200, 0, 35)

在此处输入图像描述

我使用您的代码在文本周围绘制绿色矩形。

于 2013-10-19T22:42:10.187 回答