前言
之前做過很多項目都沒考慮過字體適配問題。相信絕大多數(shù)人在做項目時录淡,都沒仔細去考慮這件事捌木。一般都是根據(jù)UI出的圖做個估算,有耐心的估計會自己拿工具測量下嫉戚。如今刨裆,考慮到iPhone機型的多樣性,UI設計師不可能針對每一款iPhone的屏幕出一套UI圖彬檀。一般而言帆啃,都是基于5s的標準出UI。當我們在設置字體時窍帝,往往都是基于UI并且針對不同的屏幕字體也都是絕對的努潘。那么問題來了,細心的同學可能會注意到坤学,相同大小的字體在5s或6上也許差別不大疯坤,但在6p上字體有縮小的現(xiàn)象,其原因由分辨率導致深浮。
在6出來不久压怠,曾看過有關適配的文章,其中關于iPhone尺寸規(guī)格如下:
設備 | 寬 | 高 | 對角線 | 邏輯分辨率 | scale Factor | 設備分辨率 | PPI |
---|---|---|---|---|---|---|---|
3GS | 2.4inch | 4.5inch | 3.5inch | 320x480 | @1x | 320x480 | 163 |
4(s) | 2.31inch | 4.5inch | 3.5inch | 320x480 | @2x | 640x960 | 326 |
5c | 2.33inch | 4.90inch | 4inch | 320x568 | @2x | 640x1136 | 326 |
5(s) | 2.31inch | 4.87inch | 4inch | 320x568 | @2x | 640x1136 | 326 |
6 | 2.64inch | 5.44inch | 4.7inch | 375x667 | @2x | 750x1334 | 326 |
6p | 3.06inch | 6.22inch | 5.5inch | 414x736 | @3x | 1242x2208 | 401 |
從iPhone3GS/iPhone4(s)過渡到iPhone5(s)時飞苇,在邏輯上寬度不變高度稍高刑峡,之前舊的素材和布局通過AutoresizingFlexible簡單適配即可運行得很好,但由于高寬比增大玄柠,上下兩端出現(xiàn)黑粗邊(典型如LaunchImage)突梦。從分辨率的角度來看,除了需要提供LaunchImage這種滿屏圖羽利,其他基本沿用二倍圖(@2x)宫患;從屏幕尺寸角度來看,需要對縱向排版略加調整这弧。
從iPhone5(s)發(fā)展到iPhone6(+)娃闲,由于高寬比保持不變虚汛,iOS對圖標、圖片皇帮、字體進行等比放大自適應卷哩,清晰度會有所降低。同時属拾,絕對坐標布局會導致在大屏下出現(xiàn)偏左偏上的問題将谊。從分辨率的角度來看,iPhone6沿用二倍圖(@2x)渐白,但需為iPhone6+提供更高的三倍圖(@3x)尊浓;從屏幕尺寸角度來看,需要重新對UI元素尺寸和布局進行適配纯衍,以期視覺協(xié)調栋齿。
字體適配
以上屬于科普類的東西,下面來點實際的襟诸。
關于字體適配有2種方案瓦堵。
- 方案一:
設置一個大小區(qū)域范圍,比如10~30pointSize
的范圍(pointSize為UIFont
的一個CGFloat類型的屬性)歌亲,然后for
循環(huán)降序遍歷此范圍設置一個臨時的UIFont
變量谷丸,根據(jù)此變量計算當前文本的大小,與當前UILabel
的height
作比較找出合適的字體应结。
#define ADAPTIVE__FONT_SIZE_MINIMUM_VALUE 20
#define ADAPTIVE_FONT_SIZE_MAXIMUM_VALUE 30
-(UIFont *) adjustFontSizeToFillItsContents
{
NSString* text = self.text;
for (int i = ADAPTIVE_FONT_SIZE_MAXIMUM_VALUE; i>ADAPTIVE__FONT_SIZE_MINIMUM_VALUE; i--) {
UIFont *font = [UIFont fontWithName:self.font.fontName size:(CGFloat)i];
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:@{NSFontAttributeName: font}];
CGRect rectSize = [attributedText boundingRectWithSize:CGSizeMake(self.frame.size.width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin context:nil];
if (rectSize.size.height <= self.frame.size.height) {
return [UIFont fontWithName:self.font.fontName size:(CGFloat)i];
break;
}
}
return self.font;
}
- 方案二:
計算出一個scale
重新設置UIFont
各淀,偽代碼如下:
CGFloat scale = ([UIScreen mainScreen].bounds.size.width / 320);
NSLog(@"before : %.1f", [font pointSize]);
font = [UIFont fontWithName:[font fontName] size:fontSize * scale];
NSLog(@"after : %.1f", [font pointSize]);
既然需要重新設置UIFont
叉存,那么我們不可避免的要hook
下UIFont
的類方法``fontWithName:size:做個函數(shù)交換的處理。 函數(shù)的交換我們需要用到
runtime`機制。
void bd_exchageClassMethod(Class aClass, SEL oldSEL, SEL newSEL)
{
Method oldClsMethod = class_getClassMethod(aClass, oldSEL);
assert(oldClsMethod);
Method newClsMethod = class_getClassMethod(aClass, newSEL);
assert(newClsMethod);
method_exchangeImplementations(oldClsMethod, newClsMethod);
}
然后妓局,我們給UIFont
創(chuàng)建一個Categroy
文件循集,文件名為AdaptiveFont
蔗怠。在實現(xiàn)文件代碼如下:
@implementation UIFont (AdaptiveFont)
+ (void)hook
{
bd_exchageClassMethod([UIFont class], @selector(fontWithName:size:), @selector(hook_fontWithName:size:));
}
+ (UIFont *)hook_fontWithName:(NSString *)fontName size:(CGFloat)fontSize
{
NSLog(@"before : %.1f", fontSize);
CGFloat scale = ([UIScreen mainScreen].bounds.size.width / 320);
NSLog(@"scale : %f", scale);
UIFont *font = [self hook_fontWithName:fontName size:fontSize * scale];
NSLog(@"after : %.1f", [font pointSize]);
printf("<--------------------->\n");
return font;
}
@end
接口文件暴漏相關方法如下:
@interface UIFont (AdaptiveFont)
+ (void)hook;
+ (UIFont *)hook_fontWithName:(NSString *)fontName size:(CGFloat)fontSize;
@end
相對比較而言厢拭,我還是傾向于方法二。方法一的前提條件是height
要適配好玷坠,不能是絕對值蜗搔,否效果。當然八堡,方法二也一樣樟凄,只不過height
若是絕對值,會出現(xiàn)文字顯示不全的問題兄渺。
在用法上缝龄,方法一只需調用adjustFontSizeToFillItsContents
,而方法二需在application:didFinishLaunchingWithOptions:
函數(shù)調用下hook
。
當然叔壤,這并不是最終也不是最好的適配方案瞎饲。個人覺得根據(jù)PPI
適配字體,限于經(jīng)歷有限只能研究到這炼绘。
歡迎糾錯嗅战,有什么好的字體適配方案也可以在下方評論進行探討。