玩转swift -- UIKit 之 UIFont

一、简介

UIFont类提供了用于获取和设置字体信息的接口。类提供了访问字体的特点,还提供了访问,这是在布局过程中中使用的字体的字形信息系统。他们通过接受它们作为参数的方法,您可以使用字体对象。下面依据源码顺序,分门别类详细测试讲解。

二、测试与详解

1、创建字体(Creating Fonts)

// 根据用户设定的字体大小及粗细设置字体
@available(iOS 7.0, *)
open class func preferredFont(forTextStyle style: UIFontTextStyle) -> UIFont

// 根据系统的用户偏好设置及设备的SizeClass 创建字体
@available(iOS 10.0, *)
open class func preferredFont(forTextStyle style: UIFontTextStyle, compatibleWith traitCollection: UITraitCollection?) -> UIFont

// 根据字体的名字及字体的大小创建字体
public init?(name fontName: String, size fontSize: CGFloat)

// 这个方法是在现有字体基础上获取一个不同大小的字体
open func withSize(_ fontSize: CGFloat) -> UIFont

// 根据描述符及字体的大小去创建Font
@available(iOS 7.0, *)
public init(descriptor: UIFontDescriptor, size pointSize: CGFloat)

通过上面第一二个API设置之后,文本控件中的字体就会以用户的偏好设定去显示,但是如果程序在运行的时候,用户设置修改了字体,此时再切回程序,字体并不会自动的跟着变。基于以上的原因,在iOS10以下的系统,开发者需要监听 UIContentSizeCategoryDidChangeNotification 通知,来重新设置字体。在iOS10以后,需要设置文字控件的属性 adjustsFontForContentSizeCategory为true。

① 首先测试一下 UIFontTextStyle 的各种样式,样式如下

// 标题1样式
@available(iOS 9.0, *)
public static let title1: UIFontTextStyle
// 标题2样式
@available(iOS 9.0, *)
public static let title2: UIFontTextStyle
// 标题3样式
@available(iOS 9.0, *)
public static let title3: UIFontTextStyle
// 大标题样式
@available(iOS 7.0, *)
public static let headline: UIFontTextStyle
// 小标题样式
@available(iOS 7.0, *)
public static let subheadline: UIFontTextStyle
// 主内容样式
@available(iOS 7.0, *)
public static let body: UIFontTextStyle
// 插图样式
@available(iOS 9.0, *)
public static let callout: UIFontTextStyle
// 脚注样式
@available(iOS 7.0, *)
public static let footnote: UIFontTextStyle
// 说明1样式
@available(iOS 7.0, *)
public static let caption1: UIFontTextStyle
// 说明2样式
@available(iOS 7.0, *)
public static let caption2: UIFontTextStyle

测试代码如下:

// MARK: 测试 preferredFont
func testFontTextStyle() {
        
    self.addFontLabelWithTextStyle(style: .title1);
    self.addFontLabelWithTextStyle(style: .title2);
    self.addFontLabelWithTextStyle(style: .title3);
    self.addFontLabelWithTextStyle(style: .headline);
    self.addFontLabelWithTextStyle(style: .subheadline);
    self.addFontLabelWithTextStyle(style: .body);
    self.addFontLabelWithTextStyle(style: .callout);
    self.addFontLabelWithTextStyle(style: .footnote);
    self.addFontLabelWithTextStyle(style: .caption1);
    self.addFontLabelWithTextStyle(style: .caption2);
}
// 添加label
func addFontLabelWithTextStyle(style: UIFontTextStyle) {
        
    let label = UILabel.init(frame: CGRect.init(x: 40, y: 80 + self.textStyleIndex * 40, width: 400, height: 20));
    label.text = String(style.rawValue);
    label.font = UIFont.preferredFont(forTextStyle: style);
    self.view.addSubview(label);
        
    self.textStyleIndex += 1;
}

测试结果如下图

②测试一下 UIContentSizeCategoryDidChangeNotification

测试代码如下:

// MARK: 测试 preferredFont 监听通知
func testPreferredFontNotify() {
        
    NotificationCenter.default.addObserver(self, selector: #selector(resetLabelStyle(notification:)), name: NSNotification.Name.UIContentSizeCategoryDidChange, object: nil)
}
    
// 回调通知
func resetLabelStyle(notification: Notification) {
        
    self.view.subviews.forEach { (view) in
            
        if view is UILabel {
                
            let label = view as! UILabel;
            let style = label.font.fontDescriptor.object(forKey: UIFontDescriptorTextStyleAttribute) as! String
            label.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle(rawValue: style));
        }
    };
}

接下来更改字体的大小及粗细等,路径如下:

设置\通用\辅助功能

更改设置完毕(增加字体的字号)后,切换到测试程序,截图如下:

③ 测试一下iOS10 新添加的文字控件属性,更新系统字体。

在iOS10中,苹果在UILabel中提供了一个新的属性 adjustsFontForContentSizeCategory ,将其设置为True,就可以了自动更新了,不用监听那个通知了。
如下:

label.adjustsFontForContentSizeCategory = true;

经过和与第二条的通知测试一样的流程,结果一致。

④ 粗略讲一下 UITraitCollection

为了表征 Size Classes,Apple 在 iOS 8 中引入了一个新的类,UITraitCollection。这个类封装了像水平和竖直方向的 Size Class 等信息。iOS 8 的 UIKit 中大多数 UI 的基础类 (包括 UIScreen,UIWindow,UIViewController 和 UIView) 都实现了 UITraitEnvironment 这个接口,通过其中的 traitCollection 这个属性,我们可以拿到对应的 UITraitCollection 对象,从而得知当前的 Size Class,并进一步确定界面的布局。

UIViewController 默认遵循了UITraitEnvironment协议,用来监听 traitCollection 的变化,如下:

 @available(iOS 8.0, *)
public func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)

测试一下, 在VC中添加如下代码:

// MARK: 测试 UITraitCollection
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        
    print(previousTraitCollection ?? "测试没有获取到TraitCollection");
        
    guard previousTraitCollection != nil else {
            
        return;
    }
        
    self.view.subviews.forEach { (view) in
            
        if view is UILabel {
                
            let label = view as! UILabel;
            let style = label.font.fontDescriptor.object(forKey: UIFontDescriptorTextStyleAttribute) as! String
            label.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle(rawValue: style), compatibleWith: previousTraitCollection);
        }
    };
}

运行程序,并切换横竖屏,打印如下:

测试没有获取到TraitCollection
<
UITraitCollection: 0x6000000daa90; 
_UITraitNameUserInterfaceIdiom = Phone,
_UITraitNameDisplayScale = 2.000000, 
_UITraitNameDisplayGamut = sRGB, 
_UITraitNameHorizontalSizeClass = Compact, _UITraitNameVerticalSizeClass = Regular,
_UITraitNameTouchLevel = 0, 
_UITraitNameInteractionModel = 1,
_UITraitNameUserInterfaceStyle = 1, 
_UITraitNameLayoutDirection = 0, 
_UITraitNameForceTouchCapability = 1, _UITraitNamePreferredContentSizeCategory = UICTContentSizeCategoryL
>

在第一次运行的时候,并没有获取到当前的 TraitCollection ,切换后猜得到更改后的 TraitCollection ,在切回来的时候,也会打印。

但是在界面上并没有发现字体有什么变化,暂时没有想到用到第二个方法去测试,这里猜测是用来匹配自动布局的时候,字体的缩放。

⑤ 了解一下 UIFontDescriptor 字体描述符

UIFontDescriptor,即用属性字典描述字体的机制。
可以通过字体描述符创建字体,也可以通过字体描述符中的属性字典的更改来更改字体。
字体描述符并非字体,两者是不同的概念,但是二者可进行相互转化。一个 UIFont 对象通过其 fontDescriptor 获得其对应的字体描述符,而UIFont通过初始化函数 init(descriptor:size:) 可根据字体描述符获取对应的字体。
更多关于 UIFontDescriptor的初始化、属性、方法等可参考苹果文档,已经详细表述了,之后有需要也会写篇文章测试记录下。

二、创建系统字体(Creating System Fonts)

// 获取指定尺寸的系统标准字体
open class func systemFont(ofSize fontSize: CGFloat) -> UIFont
// 获取指定尺寸的系统粗体
open class func boldSystemFont(ofSize fontSize: CGFloat) -> UIFont
// 获取指定尺寸的系统斜体
open class func italicSystemFont(ofSize fontSize: CGFloat) -> UIFont
// 获取指定尺寸及粗细程度的系统字体
@available(iOS 8.2, *)
open class func systemFont(ofSize fontSize: CGFloat, weight: CGFloat) -> UIFont
// 获取指定尺寸及存息程度的等宽数字系统字体
@available(iOS 9.0, *)
open class func monospacedDigitSystemFont(ofSize fontSize: CGFloat, weight: CGFloat) -> UIFont

不再赘述, 测试代码如下:

func testGetSystemFont() {
        
self.addFontLabelWithFont(font: UIFont.systemFont(ofSize: 18));
self.addFontLabelWithFont(font: UIFont.boldSystemFont(ofSize: 18));
self.addFontLabelWithFont(font: UIFont.italicSystemFont(ofSize: 18));
self.addFontLabelWithFont(font: UIFont.systemFont(ofSize: 18, weight: 3));
self.addFontLabelWithFont(font: UIFont.monospacedDigitSystemFont(ofSize: 18, weight: 3));

}
  // MARK: 测试获取系统字体
func addFontLabelWithFont(font: UIFont) {
        
    let label = UILabel.init(frame: CGRect.init(x: 40, y: 80 + self.textStyleIndex * 40, width: 400, height: 20));
    label.text = font.fontName;
    label.font = font;
    self.view.addSubview(label);
        
    self.textStyleIndex += 1;
}

结果如下:

由图可知,当前的默认系统字体与monospacedDigitSystemFont获取到的字体是一样的。

三、获取可用的字体名称


// 系统上可用的字体系列的名称数组
open class var familyNames: [String] { get }
// 特定字体系列中可用的字体名称数组。
open class func fontNames(forFamilyName familyName: String) -> [String]

见名知意,测试代码如下(打印系统直邮的所有字体):

func getAllSystemFonts() {
        
    UIFont.familyNames.map {
            
        UIFont.fontNames(forFamilyName: $0);
            
        }.forEach { (fonts:[String]) in
                
            fonts.forEach({
                    
                print($0);
            })
    };
        
}

测试结果如下:

Copperplate-Light
Copperplate
Copperplate-Bold
KohinoorTelugu-Regular
KohinoorTelugu-Medium
KohinoorTelugu-Light
Thonburi
Thonburi-Bold
Thonburi-Light
CourierNewPS-BoldMT
CourierNewPS-ItalicMT
CourierNewPSMT
......(后续太多,不在列举,可以自己测试一下)

四、获取字体名称属性(Getting Font Name Attributes)

// 获取字体所在的字体系列的名称
open var familyName: String { get }
// 获取字体的名称
open var fontName: String { get }

这两个属性与第三部分一一对应,不再赘述。

五、获取字体指标(Getting Font Metrics)

// 字体的实际大小
open var pointSize: CGFloat { get }
// 基准线以上的高度
open var ascender: CGFloat { get }
// 基准线以下的高度
open var descender: CGFloat { get }
// 最大文字的高度
open var capHeight: CGFloat { get }
// 小写字母(x)的高度(相当于最小)
open var xHeight: CGFloat { get }
// 行的高度
@available(iOS 4.0, *)
open var lineHeight: CGFloat { get }
// 字体加上下留白的高度
open var leading: CGFloat { get }

对于这部分没有概念的话,可以看一下下图(来自维基百科):

字体指标示意图

六、获取字体描述符

@available(iOS 7.0, *)
open var fontDescriptor: UIFontDescriptor { get }

之前介绍了以下 UIFontDescriptor, 这里就不再赘述了。

七、完毕

UIFont 这个类测试讲解到这里就结束了,在之后的时间里,我会将swift下的UIkit的相关类依次整理一下,温故而知新!感兴趣的童鞋,可以关注我的个人博客网站http://www.wanglongshuai.com

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容