Search code examples
iosobjective-cios7uilabeltextkit

How to fit text in a circle in UILabel


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?


Solution

  • 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.

    enter image description here

    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:

    enter image description here

    Of course you will have to use a UITextView instead of UILabel but I hope it helps :)