Q:

System font "Hiragino Sans" is show with clipped ascender and descenders

I need to use the iOS's system font "Hiragino Sans W3" in my app, but no matter which size/style or the UILabel's dimensions I choose, the font's ascenders and descenders always appear clipped:

It seems that this can this fixed by creating a subclass of UILabel and overwriting method textRectForBounds:limitedToNumberOfLines: to return the "correct" value. So the following code...
- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines
{
    CGRect result = [super textRectForBounds:bounds limitedToNumberOfLines:numberOfLines];
    result = CGRectInset(result, 0, -5);
    return result;
}

...results in both the ascender and descenders not being clipped anymore:

I know it's also possible to adjust font ascender and descender positions using an external editor. But this is a system font, shouldn't it work correctly without any modifications? Is there something I'm missing here?
Thanks in advance for any answers.

A:

I think this issue really boils down to the font itself ("Hiragino Sans"). Using a font editor it is possible to see that the glyphs go beyond the ascender and descender values, which is what iOS seems to assume as the vertical "bounding box" for displayed text.
For the lack of a better solution, I've been using a (pretty ugly) hack which modifies the values for UIFont read-only properties ascender and lineHeight.
File UIFont+Alignment.h:
#import <UIKit/UIKit.h>

@interface UIFont (Alignment)

- (void)ensureCorrectFontAlignment;

@end

File UIFont+Alignment.m:
#import "UIFont+Alignment.h"
#import <objc/runtime.h>
#import <objc/message.h>

static NSHashTable *adjustedFontsList;

@implementation UIFont (Alignment)

- (void)ensureCorrectFontAlignment
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        adjustedFontsList = [NSHashTable hashTableWithOptions:NSHashTableWeakMemory];
    });

    @synchronized (adjustedFontsList) {

        if ([adjustedFontsList containsObject:self]) {
            return;
        }
        else if ([self.fontName containsString:@"Hiragino"]) {

            SEL originalAscenderSelector = @selector(ascender);
            Method originalAscenderMethod = class_getInstanceMethod([UIFont class], originalAscenderSelector);
            SEL originalLineHeightSelector = @selector(lineHeight);
            Method originalLineHeightMethod = class_getInstanceMethod([UIFont class], originalLineHeightSelector);

            id result = method_invoke(self, originalAscenderMethod, nil);
            CGFloat originalValue = [[result valueForKey:@"ascender"] floatValue];
            [result setValue:@(originalValue * 1.15) forKey:@"ascender"];

            result = method_invoke(self, originalLineHeightMethod, nil);
            originalValue = [[result valueForKey:@"lineHeight"] floatValue];
            [result setValue:@(originalValue * 1.25) forKey:@"lineHeight"];

            [adjustedFontsList addObject:self];
        }
    }
}

@end

To apply, just import the header and invoke [myUIFont ensureCorrectFontAlignment] after creating a new font instance.

