iOS开发:UILabel字号根据屏幕缩放
admin
2023-02-09 11:00:11
0

场景:

假设我们有这样一个需求,iPhone 6(屏幕宽度为375pt)上的设计图上的字号为17pt,iPhone 6 Plus上的字号根据屏幕宽度缩放,即字号为(17pt x 414pt / 375pt)= 18.768pt

解决方案:

如果一个一个设置太麻烦,容易遗漏,这时候我们采用 runtime 的替换方法来实现,如果嫌替换方法太麻烦,我们可以用第三方库 Aspects 来辅助我们解决。

步骤:

  1. 添加pod

    pod 'Aspects', '~> 1.4.1'
  2. 新建UILabel Category,命名为UILabel+AspectsScaling
    以下为文件内容
    UILabel+AspectsScaling.h 文件
#import 
NS_ASSUME_NONNULL_BEGIN
@interface UILabel (AspectsScaling)
@end
NS_ASSUME_NONNULL_END

UILabel+AspectsScaling.m 文件

#import "UILabel+AspectsScaling.h"
#import "Aspects.h"
@implementation UILabel (AspectsScaling)
+ (void)load {
  NSError * error = nil;
  [self aspect_hookSelector:@selector(initWithCoder:) withOptions:AspectPositionAfter usingBlock:^(id info, NSCoder * coder) {
    [info.instance scaleFont];
  } error:&error];
  [self aspect_hookSelector:@selector(initWithFrame:) withOptions:AspectPositionAfter usingBlock:^(id info, CGRect frame) {
    [info.instance scaleFont];
  } error:&error];
  //以下是log方法,可以不要
#if DEBUG
  [self aspect_hookSelector:@selector(scaleFont) withOptions:AspectPositionBefore usingBlock:^(id info) {
    UILabel * label = info.instance;
    NSLog(@"UILabel: Before Scaling font size: %f", label.font.pointSize);
  } error:&error];
  [self aspect_hookSelector:@selector(scaleFont) withOptions:AspectPositionAfter usingBlock:^(id info) {
    UILabel * label = info.instance;
    NSLog(@"UILabel: After Scaling font size: %f", label.font.pointSize);
  } error:&error];
#endif
}
- (void)scaleFont {
  CGFloat ratio = CGRectGetWidth(UIScreen.mainScreen.bounds) / (CGFloat)375;
  self.font = [UIFont fontWithDescriptor:self.font.fontDescriptor size:self.font.pointSize * ratio];
}
@end

解释:

  1. 显然,这是缩放字体的方法
    - (void)scaleFont;
  2. 这个方法是在原来的initWithCoder: 方法后面执行一个 block ,这是 Aspects 库的方法,利用的是 runtime,可以自行了解源码
    [self aspect_hookSelector:@selector(initWithCoder:) withOptions:AspectPositionAfter usingBlock:^(id info, NSCoder * coder)...
  3. 再看 log 方法,这个 log 方法利用 Aspects ,在替换字体前后 NSLog 字体的字号,这个区别在参数 AspectPositionBefore 和 AspectPositionAfter
    [self aspect_hookSelector:@selector(scaleFont) withOptions:AspectPositionBefore usingBlock:^(id info) ...
    [self aspect_hookSelector:@selector(scaleFont) withOptions:AspectPositionAfter usingBlock:^(id info) ...
  4. 我们看看 Aspects 的Aspects.h文件:
    里面提供两个方法,
    • 一个是类方法(修改类的所有实例的方法),
    • 一个是实例方法(修改单个实例的方法),
    • 返回值是一个id可以保存以后取消修改,
    • usingBlock:(id)block 里面的类型id一般情况下可以写成^(id info, ...) ...是要修改的方法的所有参数,如@selector(initWithFrame:) ,block 类型^(id info, CGRect frame)
...
typedef NS_OPTIONS(NSUInteger, AspectOptions) {
    AspectPositionAfter   = 0,            /// Called after the original implementation (default)
    AspectPositionInstead = 1,            /// Will replace the original implementation.
    AspectPositionBefore  = 2,            /// Called before the original implementation.

    AspectOptionAutomaticRemoval = 1 << 3 /// Will remove the hook after the first execution.
};
...
+ (id)aspect_hookSelector:(SEL)selector
                      withOptions:(AspectOptions)options
                       usingBlock:(id)block
                            error:(NSError **)error;

/// Adds a block of code before/instead/after the current `selector` for a specific instance.
- (id)aspect_hookSelector:(SEL)selector
                      withOptions:(AspectOptions)options
                       usingBlock:(id)block
                            error:(NSError **)error;
...

总结

Aspects 是 iOS Aspect-oriented programming (AOP) 的一种实现,
满足以下几点就可以使用(但不是必须满足才能使用)

  • 原来要有实例方法实现
  • 频繁调用,一个一个修改太麻烦
  • 在原来的实例方法的前面和后面可以插入代码完成需求
  • 最最常用的是log,以后可以一步注释
[UIViewController aspect_hookSelector:@selector(viewWillAppear:) withOptions:AspectPositionAfter usingBlock:^(id aspectInfo, BOOL animated) {
    NSLog(@"View Controller %@ will appear animated: %tu", aspectInfo.instance, animated);
} error:NULL];

Aspects 不是万能的,GitHub项目主页有Compatibility and Limitations ,一种常见的问题是当拦截一个方法的时候,它会把相关类当作已拦截,就会报错(A method can only be hooked once per class hierarchy ),所以当方法名相同时要考虑其他方法,这个 Aspects 库无法满足需求

相关内容

热门资讯

德国总理:美国正在被伊朗羞辱 德国之声4月27日报道,德国总理默茨在访问一所学校时表示,在当前的持续冲突中,伊朗领导层正试图羞辱美...
理响中国|“长”歌以行,风云激... 光阴如梭,东方潮阔。这里是中国的长三角,世界的长三角。无论过去、现在还是未来,这片土地都因时代而生,...
白宫:特朗普及其国安团队开会讨... 新华社华盛顿4月27日电 美国白宫新闻秘书莱维特27日在记者会上证实,总统特朗普及其国家安全团队当天...
人民日报刊文:日本放开杀伤性武... 日本放开杀伤性武器出口推高地缘冲突风险(国际论坛)常思纯《人民日报》(2026年04月28日 第 0...
医疗保障法草案二审:明确生育保... 满足多样化健康保障需求本报记者 彭 波4月27日,医疗保障法草案二审稿提请十四届全国人大常委会第二十...
天津一景区发生自转旋翼机事故1... 澎湃新闻记者 吕新文中国民用航空华北地区管理局4月22日公布《豪客通航“10•1”天津长芦汉盐旅游区...
卡塔尔埃米尔与美国总统特朗普通... 当地时间24日,卡塔尔埃米尔塔米姆与美国总统特朗普通电话,重点就中东地区局势以及伊朗与美国谈判问题交...
男子30年前被扣押2859克黄... 澎湃新闻记者 王鑫家住辽宁省大连市的潘永嘉近日向澎湃新闻反映称,三十年前,他在大连周水子机场被盖州市...
商务部:取消反制欧盟两家金融机... 中华人民共和国商务部令二〇二六年 第1号鉴于欧盟已取消对中国两家金融机构的制裁措施,现公布《关于取消...
过去24小时共有5艘船只通过霍... 总台记者当地时间24日获悉,过去24小时内,共有5艘船只通过霍尔木兹海峡,其中包括一艘伊朗油轮。(总...