Craig Watkinson
Craig Watkinson

Reputation: 913

iOS/UIFont - reducing font width

I have a UILabel that is a fixed size. Unfortunately on rare occasions, the text I need to fit into it doesn't fit! I have tried reducing the font size, but it needs to reduce so much that it looks terrible.

Is it possible to change the font width somehow? UIFont does not seem to have any properties to allow me to do this? Do I need to use a UIWebView and use CSS? I don't know much CSS, so any help is much appreciated if this is the best way to solve this.

Alternatively, any other ways to solve this?

Thanks Craig

Upvotes: 2

Views: 1760

Answers (3)

user23743
user23743

Reputation:

The simplest way to shrink just the width of the text is to apply a transform to the label's layer:

label.layer.transform = CATransform3DMakeScale(desiredWidth/textWidth, 1.0, 1.0);

Upvotes: 2

Cowirrie
Cowirrie

Reputation: 7226

Do you mean you want to squeeze it horizontally while keeping the height? This is achievable, up to about 60% of the regular width. Beyond that it looks terrible.

Here is the drawRect for a UILabel subclass which squeezes independently on either axis if necessary.

// This drawRect for a UILabel subclass reproduces most common UILabel formatting, but does not do truncation, line breaks, or scaling to fit.
// Instead, it identifies cases where the label text is too large on either axis, and shrinks along that axis.
// For small adjustments, this can keep text readable.  In extreme cases, it will create an ugly opaque block.
- (void) drawRect:(CGRect)rect;
{
    CGRect bounds = [self bounds];
    NSString *text = [self text];
    UIFont *font = [self font];

    // Find the space needed for all the text.
    CGSize textSize = [text sizeWithFont:font];

    // topLeft is the point from which the text will be drawn.  It may have to move due to compensate for scaling, or due to the chosen alignment.
    CGPoint topLeft = bounds.origin;

    // Default to no scaling.
    CGFloat scaleX = 1.0;
    CGFloat scaleY = 1.0;

    // If the text is too wide for its space, reduce it.
    // Remove the second half of this AND statement to have text scale WIDER than normal to fill the space.  Useless in most cases, but can be amusing.
    if ((textSize.width>0) && (bounds.size.width/textSize.width<1))
    {
        scaleX = bounds.size.width/textSize.width;
        topLeft.x /= scaleX;
    }
    else
    {
        // Alignment only matters if the label text doesn't already fill the space available.
        switch ([self textAlignment])
        {
            case UITextAlignmentLeft :
            {
                topLeft.x = bounds.origin.x;
            }
                break;

            case UITextAlignmentCenter :
            {
                topLeft.x = bounds.origin.x+(bounds.size.width-textSize.width)/2;
            }
                break;

            case UITextAlignmentRight :
            {
                topLeft.x = bounds.origin.x+bounds.size.width-textSize.width;
            }
                break;
        }
    }

    // Also adjust the height if necessary.
    if ((textSize.height>0) && (bounds.size.height/textSize.height<1))
    {
        scaleY = bounds.size.height/textSize.height;
        topLeft.y /= scaleY;
    }
    else
    {
        // If the label does not fill the height, center it vertically.
        // A common feature request is for labels that do top or bottom alignment.  If this is needed, add a property for vertical alignment, and obey it here.
        topLeft.y = bounds.origin.y+(bounds.size.height-textSize.height)/2;
    }

    // Having calculated the transformations needed, apply them here.
    // All drawing that follows will be scaled.
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextScaleCTM(context, scaleX, scaleY);

    // Begin drawing.
    // UILabels may have a shadow.
    if ([self shadowColor])
    {
        [[self shadowColor] set];
        CGPoint shadowTopLeft = CGPointMake(topLeft.x+[self shadowOffset].width/scaleX, topLeft.y+[self shadowOffset].height/scaleY);
        [text drawAtPoint:shadowTopLeft withFont:font];
    }

    // The text color may change with highlighting.
    UIColor *currentTextColor;
    if ((![self isHighlighted]) || (![self highlightedTextColor]))
        currentTextColor = [self textColor];
    else
        currentTextColor = [self highlightedTextColor];

    // Finally, draw the regular text.
    if (currentTextColor)
    {
        [currentTextColor set];
        [text drawAtPoint:topLeft withFont:font];
    }
}

Upvotes: 1

He Shiming
He Shiming

Reputation: 5819

You can set the minimum font size of a UILabel to a smaller value, and check Autoshrink to let it automatically shrink. This parameter is available in Interface Builder.

The internal implementation will reduce kerning, which is the width of space between characters. It cannot actually reduce width though.

This is your better bet. If you are still unsatisfied with results. You may have to change your design.

Upvotes: 0

Related Questions