System font “Hiragino Sans” is show with clipped ascender and descenders

非 Y 不嫁゛ 提交于 2019-12-19 11:36:12

问题


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.


回答1:


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.




回答2:


@DPR 's answer helped me, but with it I had an error

Too many arguments to function call, expected 0, have 2 method_invoke

I replaced

id result = method_invoke(self, originalAscenderMethod, nil);

with

id (*function)(id, Method) = (id (*)(id, Method)) method_invoke; id result = function(self, originalAscenderMethod);

And

result = method_invoke(self, originalLineHeightMethod, nil);

with

result = function(self, originalLineHeightMethod);



来源:https://stackoverflow.com/questions/44374358/system-font-hiragino-sans-is-show-with-clipped-ascender-and-descenders

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!