title: "Text Kit教程:入门(Swift版)” date: 2015-8-23 tags: [Swift] categories: [Swift] permalink: text-kit-tutorial-swift
#Text Kit教程:入门(Swift版)
raywenderlich原文链接: Text Kit Tutorial: Getting Started
- 原文2014/12/12更新,适用于Xcode6.1.1
- 译文2015/08/23翻译,适用于Xcode6.4 (原文代码稍有改动,以适配Swift 1.2)
iOS
绘制文本的方式在持续变强,因为苹果多年来一直在增加特性和功能.iOS7
的发布带来了文本绘制的一些重大变化.现在的iOS8
在此基础上让它更加易用.一个简洁的概述能让你大致了解iOS
的文本编辑功能.
在iOS6
以前,web视图通常是绘制加粗/斜体/颜色等混合样式最简单的方式.
2012年,iOS6
在一些UIKit
控件中增加了属性字符串.这使得完成那些没有渲染为HTML
的布局更加容易.
iOS6
中,UIKit
控件的文本功能建立在WebKit
和Core Graphics
的字符串绘图函数上,如下面的分层图所示:
**注意:图中有没有一些古怪的东西冲击到你? 这就对了--
UITextView
**的底层是WebKit
.iOS6
将属性字符串当做HTML
绘制到文本视图上, 这个事实对于那些没有深挖框架的开发者来说并不显见.
然而, 从iOS7开始, 有了更简单的方法. 使用当前简约的设计避免装饰,更多的聚焦于排版,比如UIButton去掉所有的边框和阴影,只留下文字. 不足为奇的是iOS7增加了一整个全新的框架用来处理文本和文本属性: Text Kit
.???
现在架构更整洁了. 所有基于文本的UIKit控件(除了UIWebView)都使用Text Kit,如下图所示:
Text Kit
建立在Core Text
上面, 继承了Core Text
框架的所有功能, 同时无处不让开发者高兴是, 将它包裹了一层改进的面向对象API.
在这个Text Kit
教程里, 随着你创建一个简约但功能丰富的iPhone版记笔记app,你将探索Text Kit
各方面的特性. 这些特性有reflowing text/动态文本缩放和on-the-fly文本风格.???
准备创建注意中的东西? 那么读完就开始学习Text Kit
吧!
**注意:**写本教程时,
iOS8
仍然是测试版,依照协议我们不能发表iOS8的截屏. 这里所有的截屏都来自iOS7
, 它们看起来和iOS8
中非常相似.
##入门
本教程包含一个起始项目,它有创建好的用户界面,以便于专心学习Text Kit
. 下载这个起始项目文件后开始学习吧. 解压文件,在Xcode
中打开项目,同时编译/运行这个App.
**译者注:**在
Xcode6.4
中,需要做如下修改方可运行:
- 1,修改
AppDelegate.swift
文件第16行为:func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
;- 2,修改
NotesListViewController.swift
文件第32行的as
为as!
;- 3,删除
NoteEditorViewController.swift
文件第22行的!
号;
这个App创建了一个Note
实例的初始数组,并在表视图控制器中绘制它们. 故事板(Storyboard)和联线(segue)检测表视图cell的选中状态, 然后过渡到另一个能编辑和选中笔记的视图控制器.
注意: 如果你没有用过故事板, 那学习iOS7故事板教程. 要是你订阅了视频教程,那学习故事板和联线视频教程.
浏览和运行源代码能感受到这个App的结构和运行方式. 做完了这个,开始下一部分吧, 论述App中动态字体的使用.
##动态字体
动态字体是iOS7中最改变游戏规则的特性之一; 它承担App符合用户选择的字体大小/宽度的责任. iOS7中,打开设置App导航到通用/辅助功能和通用/文本大小,在这能看到影响App显示文本的设置(如下图),iOS8中,打开设置App导航到通用/辅助功能/更大字体能看到动态字体文本大小.
iOS7可通过加粗字体来增强文本可读性,也有选项用来设置支持动态字体功能的App的首选字体大小.
注意: 苹果在WWDC 2013上发布Text Kit时,强烈鼓励开发者适配动态字体. WWDC 2014时,苹果又进了一步. 他们强调所有自带App都支持动态字体. 而且,他们让动态字体在iOS8中更加易用. 虽然苹果并没有威胁说不适配就打断谁的腿, 但是他们非常强调他们的看法. 在"WWDC 2014 session 226 表和集合视图的新内容"里,包含了iOS8动态字体支持表和集合视图的内容. 非常建议你观看这个视频! 用户会很期待为iOS7+开发的App能实现这些功能, 所以不要冒险忽视他们.
为了使用动态字体,你需要指定的是字体样式而不是字体名称和大小. iOS7
给UIFont
增加了一个新方法,preferredFontForTextStyle
,它为用户字体设置中给定的样式创建字体.
下图展示了6种不同的字体样式:
左边的文本是用户可选的最小字体大小,中间的文本是最大,右边的文本展示了辅助功能中加粗字体的效果.
###基本支持
实现动态字体的基本支持比较简单. 与其在App中使用明确的字体,不如请求特定样式的字体. App在运行时根据给定的样式和用户的文本设置来选择合适的字体.
iOS8中,苹果让我们实现动态字体功能比iOS7中更加简单. 尤其是表视图中的默认label自动支持动态字体! 虽然如此,你可能想兼容iOS7,并且/或者你想在表视图中使用自定义label. 所以,你首先要学习如何在iOS7中处理动态字体,然后你会发现在iOS8中苹果如何让其变得更简单.
###为什么iOS7是伟大的,但iOS8更伟大
起始项目设置的部署平台是iOS8. 开始前,构建并运行这个App并试着多次改变默认字体大小. 你将发现表视图里笔记列表的文本大小和单元格高度会相应的改变. 而且,你不需要做什么! 但是, 确实也能看到笔记内容并没有反映文本大小设置的更改.
尽管不十分完美,但在iOS7中表现的也不错. 本教程大部分情况下不需要考虑使用的是iOS7还是iOS8(只需确保使用Xcode6!),但是现在,把部署平台改为iOS7并使用相应的iOS模拟器. 大部分情况下也可用于iOS8, 因此甚至值得你不打算兼容iOS8之前的iOS版本.
注意: 在Xcode6中设置部署平台为iOS7,选择
View/Navigators/Show Project Navigator
. 在右手边的控制面板中,选择project
,点击info
,在iOS Deployment Target
弹出菜单中选择iOS7
. 确保模拟器是iOS7
设备也是很重要的. 在iOS Simulator
中选择Hardware/Device/iOS7/iPhone 5s
.
现在你搞好了运行iOS7 App的所有设置,先构建和运行一下. 设置玩字体大小后你将非常失望, App忽略了那些设置. 现在,你要为它在iOS7中正确运行做点事.
打开NoteEditorViewController.swift
,在viewDidLoad
最后面添加这些代码:
textView.font = UIFont.preferredFontForTextStyle(UIFontTextStyleBody)
这里并没有明确一种诸如Helvetica Neue的字体. 取而代之的是,用UIFontTextStyleBody
文本样式常量请求了适合body文本的字体
下一步,打开NotesListViewController.swift
,添加下面代码到tableView(_:cellForRowAtIndexPath:)
方法的return语句前:
cell.textLabel?.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
再次,这里指定文本样式,iOS将返回合适的字体.
用语义获取字体名称,如UIFontTextStyleSubHeadline
,确保你的App像期望的一样正确地响应用户定义的排版设置,避免硬编码字体名和样式遍布你的代码. 再次构建和运行App,现在注意表视图和笔记屏幕显示正确的字体大小; 下图展示了2种不同显示的截图:
看上去很好,但是眼尖的读者会发现这只是解决方法的一半. 先回到设置App的通用/文字大小再次调整文字大小. 这次,不重新运行应用切换回SwiftTextKitNotepad,你会发现App并不会调整到新的字体大小. 你的用户们不会这样友好! 看起来这就是你需要改正此应用的第一个问题。
###响应更新
打开NoteEditorViewController.swift
,添加下面代码到viewDidLoad
最后:
NSNotificationCenter.defaultCenter().addObserver(self,
selector: "preferredContentSizeChanged:",
name: UIContentSizeCategoryDidChangeNotification,
object: nil)
上面的代码给类注册接受通知,当首选内容大小更改事件发生时会接到通知,并调用传递给它的方法(preferredContentSizeChanged). 下一步,给类添加下面的方法:
func preferredContentSizeChanged(notification: NSNotification) {
textView.font = UIFont.preferredFontForTextStyle(UIFontTextStyleBody)
}
这里简单的设置文本视图的字体为以新的首选大小为基础的字体.
注意: 你可能会奇怪为什么字体设置是和之前一样的值. 当用户改变首选字体大小时, 必须再次请求首选字体,它并不会自动更新. 当字体的选择更改时,通过preferredFontForTextStyle返回的字体会不同.
打开NotesListViewController.swift,用下面的代码重写viewDidLoad方法:
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self,
selector: "preferredContentSizeChanged:",
name: UIContentSizeCategoryDidChangeNotification,
object: nil)
}
哈, 有没有发现这和添加到NoteEditorViewController.swift的代码一模一样? 是的,它们一样. 但是你处理首选字体的更改时有细微的不同. 添加下面的方法到类里:
func preferredContentSizeChanged(notification: NSNotification) {
tableView.reloadData()
}
上面的代码简单的教了刷新表视图的可视单元格, 刷新会更新每个单元格的外观. 这会触发preferredFontForTextStyle()的调用并刷新字体选择. 构建并运行App; 改变文本大小设置,然后确认App能正确地响应新的用户设置.
###改变布局
现在似乎不错了,可是在你选择相当小字体大小时,表视图最后看起来有点稀疏,就像下面右手边(原文出错,应该为左手边--译者注)的截图:
(在iOS7中)这是动态字体的棘手部分之一. 为了确保应用的字体大小范围看起来不错,应用布局需要响应用户的文本设置. 自动布局(Auto Layout)为你搞定了很多问题,但是这个问题需要你亲自搞定.
表行高需要同字体大小一起改变. 实现tableView(_:heightForRowAtIndexPath:)
委托方法能很好的解决这个问题. 把下面的代码添加到NotesListViewController.swift的Table view data source分区标签下:(下面代码稍有改动以适应Swift1.2--译者注)
let label: UILabel = {
let temporaryLabel = UILabel(frame: CGRect(x: 0, y: 0, width: Int.max, height: Int.max))
temporaryLabel.text = "test"
return temporaryLabel
}()
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
label.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
label.sizeToFit()
return label.frame.height * 1.7
}
上面的代码创建了一个UILabel的共用实例变量label,表视图用它来计算单元格的高度. 接着, 在tableView(_:heightForRowAtIndexPath:)
里设置该label的字体为表视图单元用的字体. 然后调用label的sizeToFit
方法,它能强制label的frame紧紧的贴合文本,最后frame的高度乘以一定比例返回给表视图高度.
构建并运行App; 再一次调整文本大小,现在表格的行能动态适应文本大小了,见下面的截图:
如果你喜欢,接下来的教程你可以重置部署平台为iOS8.
###凸版印刷效果
凸版印刷效果是给文字添加细微的阴影和高亮以获得一种深度的感觉--仿佛文字是直接压进了屏幕上.
注意: "凸版印刷(letterpress)"一词???
打开NotesListViewController.swift
,用下面的实现替换tableView(_:cellForRowAtIndexPath:)
:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
let note = notes[indexPath.row]
let font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
let textColor = UIColor(red: 0.175, green: 0.458, blue: 0.831, alpha: 1)
let attributes = [
NSForegroundColorAttributeName : textColor,
NSFontAttributeName : font,
NSTextEffectAttributeName : NSTextEffectLetterpressStyle
]
let attributedString = NSAttributedString(string: note.title, attributes: attributes)
cell.textLabel?.attributedText = attributedString
return cell
}
上面的代码创建了一个凸版印刷样式的属性字符串用作表单元格的标题. 构建并运行App; 现在表视图展示的文本有不错的凸版印刷效果,就像下面这样:
凸版印刷是一种细微的效果--但不意味着你可以过渡使用! 可视效果也许能让你的文字更加有趣,但是它并不能让文字更易读.
###排除路径(Exclusion Paths)
文字平滑地环绕在图片或其他对象周围是大部分文字处理工具的标准功能. Text Kit允许你围绕复杂的路径和排除路径表示的图形绘制文本. 告诉用户笔记的创建日期,这会很方便; 你打算在笔记的中间偏右上角添加一个小的弯曲视图显示日期信息. 你要先添加视图本身--然后创建排除路径以使文本环绕在其周围。
####添加视图
var timeView: TimeIndicatorView!
timeView = TimeIndicatorView(date: note.timestamp)
textView.addSubview(timeView)
override func viewDidLayoutSubviews() {
updateTimeIndicatorFrame()
}
func updateTimeIndicatorFrame() {
timeView.updateSize()
timeView.frame = CGRectOffset(timeView.frame, textView.frame.width - timeView.frame.width, 0)
}
func preferredContentSizeChanged(notification: NSNotification) {
textView.font = UIFont.preferredFontForTextStyle(UIFontTextStyleBody)
updateTimeIndicatorFrame()
}
####排除路径