I'd like to flow the text in UILabel
into a circle (instead of rect).
I did some experiments with NSLayoutManager
, NSTextContainer
and NSTextStorage
but it does not seem to work.
The example below is supposed to flow the text into a smaller rect of 40x40 (label is 120x120) but does not seem to have any effect.
UIFont *font = [UIFont fontWithName:@"HelveticaNeue" size:12];
NSTextStorage *ts = [[NSTextStorage alloc] initWithString:multiline.title attributes:@{NSFontAttributeName:font}];
NSLayoutManager *lm = [[NSLayoutManager alloc] init];
NSTextContainer *tc = [[NSTextContainer alloc] initWithSize:CGSizeMake(40, 40)];
[lm addTextContainer:tc];
[ts addLayoutManager:lm];
self.label.attributedText = ts;
Ides?
This appeared to be a very simple solution. NSTextContainer
has an exclusionPaths
property. What you can do is to create two Bezier paths that will define areas that should be excluded.
So I did that and here is my method:
- (void)setCircularExclusionPathWithCenter:(CGPoint)center radius:(CGFloat)radius textView:(UITextView *)textView
{
UIBezierPath *topHalf = [UIBezierPath bezierPath];
[topHalf moveToPoint:CGPointMake(center.x - radius, center.y + radius)];
[topHalf addLineToPoint:CGPointMake(center.x - radius, center.y)];
[topHalf addArcWithCenter:center radius:radius startAngle:M_PI endAngle:0.0f clockwise:NO];
[topHalf addLineToPoint:CGPointMake(center.x + radius, center.y + radius)];
[topHalf closePath];
UIBezierPath *bottomHalf = [UIBezierPath bezierPath];
[bottomHalf moveToPoint:CGPointMake(center.x - radius, center.y - radius)];
[bottomHalf addLineToPoint:CGPointMake(center.x - radius, center.y)];
[bottomHalf addArcWithCenter:center radius:radius startAngle:M_PI endAngle:0 clockwise:YES];
[bottomHalf addLineToPoint:CGPointMake(center.x + radius, center.y - radius)];
[bottomHalf closePath];
textView.textContainer.exclusionPaths = @[bottomHalf, topHalf];
}
Example usage:
[self setCircularExclusionPathWithCenter:CGPointMake(160.0f, 200.0f)
radius:100.0f
textView:_textView];
And a result of my experiments:
Of course you will have to use a UITextView instead of UILabel but I hope it helps :)
You can't do this in a UILabel because it doesn't give you access to the TextKit stack. What I do is to build my own TextKit stack and subclass NSTextContainer:
-(CGRect)lineFragmentRectForProposedRect:(CGRect)proposedRect atIndex:(NSUInteger)characterIndex writingDirection:(NSWritingDirection)baseWritingDirection remainingRect:(CGRect *)remainingRect {
CGRect result = [super lineFragmentRectForProposedRect:proposedRect atIndex:characterIndex writingDirection:baseWritingDirection remainingRect:remainingRect];
CGRect r = CGRectMake(0,0,self.size.width,self.size.height);
UIBezierPath* circle = [UIBezierPath bezierPathWithOvalInRect:r];
CGPoint p = result.origin;
while (![circle containsPoint:p]) {
p.x += .1;
result.origin = p;
}
CGFloat w = result.size.width;
p = result.origin;
p.x += w;
while (![circle containsPoint:p]) {
w -= .1;
result.size.width = w;
p = result.origin;
p.x += w;
}
return result;
}
Crude but effective. Looks like this:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With