H! I'm writing a textview with subviews clipping features. The idea is to make all text draw around all subviews. The problem is to get it's content height.
Due to the lack of documentation, I decided that the attributes dictionary for CTFramesetterSuggestFrameSizeWithConstraints is the same as for the CTFramesetterCreateFrame.
Here is my clipping paths code:
-(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;}
I use it to draw text and it works ok. Here is my drawing code:
- (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);
}
That's the result:
That is exactly what I was expecting!
Finally, here is the code to check the height:
-(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;
}
Output looks like this:
Core Text Demo[2222:a0b] -[DATextView contentSizeForWidth:]: size = {729.71484, 0}
Does someone have any solution? Thanks for attention:)
This seems to me like an iOS7 bug. I have been tinkering around, and under iOS6,
CTFramesetterSuggestFrameSizeWithConstraints
returns a size with height larger than 0. Same code under iOS7 returns a height of 0.CTFramesetterSuggestFrameSizeWithConstraints
is known for its buggy and undocumented behavior. For example, your code under iOS6 returns an incorrect height due toCTFramesetterSuggestFrameSizeWithConstraints
performing an incorrect calculation, and the last line is omitted from the calculation. Here is your code running under iOS6:You should open a bug report for these issues, as well as request documentation enhancements, at: https://bugreport.apple.com
Under iOS7, with TextKit, you can achieve the same result much quicker and more elegantly:
And here is the result with
contentInset
set toUIEdgeInsetsMake(20, 200, 0, 35)
:I used your code for drawing the green rectangle around the text.