UILabel clipping italic (oblique) text at left and right edges of content ( iOS 6+)

The top label shows the default behavior of a UILabel when the text is left aligned that the label maintains its intrinsic content size. The bottom label is a simple (almost trivial) subclass of UILabel. The bottom label does not clip the 'j' or the 'l'; instead, it gives the text some room to breathe at the left and right edges without center aligning the text (yuck).

enter image description here

Although the labels themselves don't appear aligned on screen, their text does appear aligned; and what's more, in IB, the labels actually have their left edges aligned because I override alignmentRectInsets in a UILabel subclass.

enter image description here

Here's the code that configures the two labels:

#import "ViewController.h"
#import "NonClippingLabel.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *topLabel;
@property (weak, nonatomic) IBOutlet NonClippingLabel *bottomLabel;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSString *string = @"jupiter ariel";

    UIFont *font = [UIFont fontWithName:@"Helvetica-BoldOblique" size:28];

    NSDictionary *attributes = @{NSFontAttributeName: font};

    NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:string attributes:attributes];

    self.topLabel.attributedText = attrString;
    self.bottomLabel.attributedText = attrString;     
}

Here's the implementation of the NonClippingLabel subclass:

#import <UIKit/UIKit.h>

@interface NonClippingLabel : UILabel

@end

@implementation NonClippingLabel

#define GUTTER 4.0f // make this large enough to accommodate the largest font in your app

- (void)drawRect:(CGRect)rect
{
    // fixes word wrapping issue
    CGRect newRect = rect;
    newRect.origin.x = rect.origin.x + GUTTER;
    newRect.size.width = rect.size.width - 2 * GUTTER;
    [self.attributedText drawInRect:newRect];
}

- (UIEdgeInsets)alignmentRectInsets
{
    return UIEdgeInsetsMake(0, GUTTER, 0, GUTTER);
}

- (CGSize)intrinsicContentSize
{
    CGSize size = [super intrinsicContentSize];
    size.width += 2 * GUTTER;
    return size;
}

@end

No editing a font file, no using Core Text; just a relatively simple UILabel subclass for those using iOS 6+ and Auto Layout.

Update:

Augie caught the fact that my original solution prevented word wrapping for multi-lined text. I fixed that issue by using drawInRect: instead of drawAtPoint: to draw the text in the label's drawRect: method.

Here's a screenshot:

enter image description here

The top label is a plain-vanilla UILabel. The bottom label is a NonClippingLabel with an extreme gutter setting to accommodate Zapfino at size 22.0. Both labels are left and right aligned using Auto Layout.

enter image description here


Swift version of NonClippingLabel with fixed sizeThatFits method from bilobatum answer.

class NonClippingLabel: UILabel {

    let gutter: CGFloat = 4

    override func draw(_ rect: CGRect) {
        super.drawText(in: rect.insetBy(dx: gutter, dy: 0))
    }

    override var alignmentRectInsets: UIEdgeInsets {
        return .init(top: 0, left: gutter, bottom: 0, right: gutter)
    }

    override var intrinsicContentSize: CGSize {
        var size = super.intrinsicContentSize
        size.width += gutter * 2

        return size
    }

    override func sizeThatFits(_ size: CGSize) -> CGSize {
        let fixedSize = CGSize(width: size.width - 2 * gutter, height: size.height)
        let sizeWithoutGutter = super.sizeThatFits(fixedSize)

        return CGSize(width: sizeWithoutGutter.width + 2 * gutter,
                      height: sizeWithoutGutter.height)
    }

}