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
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 to CTFramesetterSuggestFrameSizeWithConstraints
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:
@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
And here is the result with contentInset
set to UIEdgeInsetsMake(20, 200, 0, 35)
:
I used your code for drawing the green rectangle around the text.