rdurand
rdurand

Reputation: 7410

Balance multi line UILabel

When I create a UILabel with textAlignement set to NSTextAlignmentCenter, lineBreakMode to NSLineBreakByWordWrapping and a long text, it splits like this :

+------------------------------------+
| Text that does not fit on a single |
|                line                |
+------------------------------------+

But what I really want is something like this :

+------------------------------------+
|       Text that does not fit       |
|          on a single line          |
+------------------------------------+

Of course, I don't want to handle the line break manually. I want the label to automatically balance the lines so their lengths are as close as possible.

Upvotes: 6

Views: 604

Answers (4)

sammydm
sammydm

Reputation: 41

I'm working in Swift 3 (iOS 10), so I converted dev_jac's answer for those who need it:

override func drawText(in rect: CGRect) {
    var newRect = rect

    if (textAlignment == .center) {
        let oneLineRect = self.textRect(forBounds: CGRect.infinite, limitedToNumberOfLines: 1)
        let numLines = ceil(oneLineRect.size.width / self.bounds.size.width)
        var betterWidth : CGFloat = oneLineRect.size.width / numLines
        if (betterWidth < rect.size.width) {
            var check = CGRect.zero
            repeat {
                betterWidth *= 1.1
                let b = CGRect(x: 0, y: 0, width: betterWidth, height: CGRect.infinite.size.height)
                check = self.textRect(forBounds: b, limitedToNumberOfLines: 0)
            } while (check.size.height > rect.size.height && betterWidth < rect.size.width)

            if betterWidth < rect.size.width {
                let difference : CGFloat = rect.size.width - betterWidth
                newRect = CGRect(x: rect.origin.x + difference/2, y: rect.origin.y, width: betterWidth, height: rect.size.height)
            }
        }
    }

    super.drawText(in: newRect)
}

Upvotes: 4

dev_jac
dev_jac

Reputation: 865

I had the same issue and inspired by this question I found a solution by just overriding - (void)drawTextInRect:(CGRect)rect

You can see it in this GitHub Project

The method itself if you just want to copy / read it is here

- (void)drawTextInRect:(CGRect)rect {
    if (self.textAlignment == NSTextAlignmentCenter) {
        CGRect oneLineRect = [self textRectForBounds:CGRectInfinite limitedToNumberOfLines:1];
        NSInteger numberOfLines = ceil(oneLineRect.size.width / self.bounds.size.width);
        CGFloat betterWidth = (oneLineRect.size.width / numberOfLines);
        if (betterWidth < rect.size.width) {
            CGRect check = CGRectZero;
            do {
                betterWidth *= 1.1;
                CGRect b = CGRectMake(0, 0, betterWidth, CGRectInfinite.size.height);
                check = [self textRectForBounds:b limitedToNumberOfLines:0];
            } while (check.size.height > rect.size.height && betterWidth < rect.size.width);

            if (betterWidth < rect.size.width) {
                CGFloat difference = rect.size.width - betterWidth;
                rect = CGRectMake(rect.origin.x + difference/2.0, rect.origin.y, betterWidth, rect.size.height);
            }
        }
    }
    [super drawTextInRect:rect];
}

PR, forks, comments are welcome!

Upvotes: 3

Keller
Keller

Reputation: 17081

If you are using auto layout, UILabel's preferredMaxLayoutWidth property may be of use to you.

Upvotes: -1

rmaddy
rmaddy

Reputation: 318934

There is no support built in for what you need. You will need to do your own calculations using NSString boundingRectWithSize:options:attributes:context:. You will need to loop through various sizes to find the best width to get two filled lines. Then use that width to size the UILabel.

Upvotes: 0

Related Questions