<译>iOS 8 Today Extension Tutorial
原文链接:iOS 8 Today Extension Tutorial
Ray提醒: 这篇文章可以说是一个精简版,它是从iOS8 by Tutirials这本书中摘录的一个章节。iOS8 by Tutirials是我们iOS8 Feast套餐中的一部分。通过这篇文章我们可以看到书中的内容是怎么样的。希望你喜欢!
iOS8介绍了一种新的概念 App Extensions
:一种在操作系统上与其他应用共享应用程序功能的方式!
这些Extensions
中有一种叫Today Extensions
,又称Widgets
。它可以让你在Notification Center
中呈现信息。这是一种很好的方式向用户直接的提供他感兴趣的最新的信息。
在这个教程中,你会编写一个Today Extension
用来渲染bitcoin.org中比特币基于美元的当前市场价格。
介绍比特币
如果你对比特币还不熟悉,那用一句很短的话来介绍的话就是比特币是一种还处于初期的数字加密货币。除了可以用来作P2P的交换和购买外,比特币交易还允许用户将比特币交易成另外一种数字加密货币,比如狗狗币和莱特币,以及像美金和欧元这样的常用货币。
介绍Crypticker项目
一旦你开始写一个Extension
,你必须先需要一个载体应用
(host app)来做扩展;看看Crypticker
项目吧。
Crypticker
是用来展示当前的比特币价格、当前价格与昨日价格的区别和历史价格图表的一个简单的App。图表包括30天的历史价格;滑动你的手指可以看到过去特定的某天的准确价格。
Extension
会包含所有这些功能,除了点击图表可以看到特定某天的价格。Today Extension
也有局限性,尤其是当它在滑动(swipe
)的时候,Swipe Gesture
总是在Notification Center
中的Today
和Notifications
这两个sections
里被触发,所以他没有真的做到提供了最好的或者最可靠的用户体验。
入门指南
我们从下载Crypticker起始项目入手。这个项目包含整个如上面描述的Crypticker
app。我们的教程不会关注这个项目本身的开发,所以你愉快又惊讶的发现这个教程是如此的简洁。毕竟,你要写一个Extension
,而不是整个app。
编译运行。请注意你需要一个可以工作的网络来链接web service
以及拉取实时的价格。
(译者注:这个App还是Swift1.0的代码,可以根据Xcode的提示将代码修改成符合Swift1.1,主要是Swift1.1加入了可失败初始化功能)
App看起来非常像上面的屏幕截图;当然显示的数据是基于当前的比特币市场怎么样。触碰在底部附近的图表会绘制一条线以及显示遮天相关的价格。
BTC Widget
为了不熟悉的人更好的理解,BTC就是比特币的缩写。很像USD就代表美元。Today Extension
会渲染出一个Crypticker
主视图的缩小版本。
理论上,Crypticker
有能力展示多种加密货币的价格,但是你的Extension
是对BTC的特制版。因此,它的名字应该叫BTC Widget
。
1
|
|
在这篇教程的结尾,你的Today Extension
会看起来像这样:
添加一个Today Extension target
所有的Extention
被包装成一个和他们载体工程隔离的binary
。所以你需要为你的Crypticker
工程添加一个Today Extention
的target
。
在Xcode的Project Navigator
中,选中Crypticker project
,然后选择Editor\Add Target…
来添加一个新的target
。当模板选择器出现后,选择iOS\ Application Extension
下的Today Extension
。点击Next
。
设置Product Name
为BTC Widget
,然后验证Language
为Swift
,Project
为Crypticker
,Embed in Application
也是Crypticker
。点击Finish
。
当提示激活BTC Widget``scheme
的时候.就像文本指示器一样,会为你创建另一个Xcode``scheme
。
恭喜!现在BTC Widget
会展示在你的targets
的列表中。
确保你选中了BTC Widget
target,选择General
标签,然后按下在Linked Frameworks and Libraries
下的+
按钮。
选中CryptoCurrencyKit.framework
然后点击Add
。
CryptoCurrencyKit是一个在Crypticker app
中使用的用来从网络上获取货币价格的自定义的framework
。你很幸运,有个难以置信般善良和体贴的程序猿模块化了Crypticker
的代码到framework
,所以它可以在多个target
下共享 :]
为了在载体工程和它的extensions质检共享代码,你必须使用自定义framework
。如果你不这么做,你会发现你重复撰写了很多代码以及违反了软件工程中一个很重要的原则:DRY- or,Dont Repeat Yourself
,我再说一次:Dont Repeat Yourself
(不要重复发明轮子)
现在,你已经准备好开始实现extension
了。
注意,现在有一个以你新的target
命名的文件组在你项目的Navigator
中,BTC Widget
。这是Today Extension
代码默认分组存放的地方。
展开这个文件组,你会看到这里有一个view controller
,一个storyboard
文件和一个Info.plist
文件。它的target
配置信息也告诉他去MainInterface.storyboard
加载他的interface
,MainInterface.storyboard
包含一个指定class为 TodayViewController.swift
的 ViewController
。
你会发现有一些你期望看到的文件在Today Extension
模板中却消失不见了。比如说app delegate
。记住所有的Extension都运行在另外一个载体工程的内部,所以他们和传统的app的生命周期不一样。
本质上,Extension的生命周期是被映射在TodayViewController
的生命周期上的。
打开MainInterface.storyboard
。你会看到一个带着明亮的Hello World``label
的深色的View
。为了与通知中心的暗色调协调,当Today Extension
有一个清晰透明的背景和一个明亮或者艳丽的文本时是最清晰易读的。
确保BTC Widget
scheme在Xcode
的toolbar
中被选中,然后编译运行。这时会出现一个窗口来问你哪一个app需要运行。Xcode正在问你哪个载体应用需要运行。选择Today
。这会告诉你的iOS打开通知中心的Today
视图。它会依次展开你的Widget
。
这也会附加Xcode
的调试器到widget
的进程中。
看你的插件,碉堡了,对不对?虽然这是一个超级刺激的东西,但widget
也需要一点工作。是时候让它做一些有趣的事了。
1 2 |
|
建立接口
打开MainInterface.storyboard
然后删除那个label。在Size Inspector
中设置view的高为150pts,宽为320pts。从Object Library
拖出一个Button
,两个Label
和一个View
到你的ViewController
的view
上。
- 将其中一个label放在左上角,在
Attributes Inspector
中设置他的Text
为$592.12
,Color
为Red: 66, Green: 145,Blue: 211
。这个label会用来显示当前的市场价格。 - 将另外一个label放在你刚刚设置的label的右边,但在右边留出一个button的空间。在
Attributes Inspector
中设置他的Text
为+1.23
,Color
为Red: 133, Green: 191, Blue: 371
。这个会哦你过来显示昨天和当前的价格的不同。 - 将button移动到视图的右上方。在
Attributes Inspector
中设置他的Image
为caret-notification-center
,删除他的Title
。 - 最后,将空的view放在两个label和button的下面,拉伸它直到它的底部和边缘和
containing view
吻合,设置他的Height
为98
.在Attributes Inspector
中设置他的Background
为Clear Color
,然后在Identity Inspector
中设置他的Class
为JBLineChartView
。
1
|
|
现在视图和Outline
看起来像这样:
1
|
|
不要担心页面的布局,你马上就会通过适当的定义布局来添加AutoLayout约束。
现在展开在Project Navigator
的Crypticker
组,选中Images.xcassets
。在File Inspector
中勾选BTC Widget
将其添加到extension的target上。
这样Xcode就会将Images.xcassets
从你的Crypticker
target上添加到BTC Widget
的target上;这是你的button使用的caret-notification-center
图片存放的地方。如果你在载体app和widget上有重复的image assets,这是一个很好的共享方式。这可以通过不添加已经在使用的图片来减少App膨胀。
返回到MainInterface.storyboard
,打开Assistant Editor
。确保TodayViewController.swift
是它的active file。将下面的代码添加到TodayViewController.swift
的顶部:
1
|
|
这是用来导入CryptoCurrencyKit framework。 接下来,你需要像这样更新他的类声明:
1
|
|
这会让TodayViewController
成为CurrencyDataViewController
的一个子类,确保他符合NCWidgetProviding
协议。
CurrencyDataViewController
是包含在CryptoCurrencyKit
中也被Crypticker
载体应用的第一个视图使用的Controller。由于Widget和App会通过UIViewController展示相似的信息,他会把可重用的组件放到一个superclass中,然后按不同的需求来做子类化。
NCWidgetProviding
是Widget
特有的接口。有两个接口的方法需要你来实现。
按住Ctrl
,从IB的Button拖动鼠标到类声明下。在弹出的对话框中确保Connection
设置为Outlet
,Type
设置为UIButton
,在Name
中输入toggleLineChartButton
后点击Connect
。
然后还是按住Ctrl,这次从IB的Button拖动鼠标到类的底部。在弹出的对话框中改变Connection
值为Action
,设置Type
为UIButton
,在Name
中输入toggleLineChart
后点击Connect
。
TodayViewController
是CurrencyDataViewController
的子类,CurrencyDataViewController
有三个outlet
:price label
, price change label
和 line chart view
。你现在需要把他们联通起来。在Document Outline
中按住Ctrl
,从Today View Controller
拖拽到price label
(设置他的text为$592.12).在弹出框中选中priceLabel
来建立连接。对另外一个label也做重复的操作,选中弹出框中的priceChangeLabel
.最后对 Line Chart View
做同样的操作,在弹出框中选中lineChartView
。
Auto Layout
为了你的Widget可以自适应,你就会需要对它设置Auto Layout
的约束。iOS8有一个新的自适应布局的概念。大意是视图可以被设计成各自有一个单独的布局,并且这个布局可以在各种各样的屏幕大小上工作。视图被设计成可以在未来以及不知道材料的设备上都可以自适应。
其中一个你要添加的约束是用来显示和隐藏我们的图表以及用来帮助定义Widget整体的高度。通知中心会依赖于你定义的适当的高度来显示你的Widget。
选中$592.12这个label,然后选择Editor\Size to Fit Content
。如果Size to Fit Content
选项不在你的菜单中,取消选中这个label,然后再次选中重试一遍。Xcode有的时候会抽风。接下来,使用在storyboard画布下的Pin
按钮。分别钉住Top
和Leading
的空间为8和16。确保Constrain to margins
处于关闭状态。
现在选中+1.23这个label,再次选中Editor\Size to Fit Content
。然后使用Pin
按钮,钉住Top
和Trailing
空间都为8.
选中那个Button,使用Pin
按钮,钉住他的Top
和Trailing
两个的空间为0,以及他的Button空间为8。再钉住他的Width
和Height
都为44.确保Constrain to margins
处于关闭状态。
你需要减小Button的底部空间约束的优先级。选中button,然后打开Size Inspector
, 定位到Bottom Space to
:约束列表的约束,点击Edit
然后改变他的Priority
为250.
通过降低优先级你被Auto Layout
系统允许打破他认为必要的约束。250是一个被设置给所有的约束优先级的默认以及必须的且碰巧小于1000的任意值。在折叠状态下这个约束需要被打破。通过设置不同优先级的约束你暗示系统当发生冲突的时候哪一个约束最先或者最后打破。
最后,选中Line Chart View
。使用Pin
按钮,钉住他的Leading
, Trailing
和Bottom
三个空间为0以及他的高度为98。
从Document Outline
中选中视图控制器的View,然后选择Editor\Resolve Auto Layout Issues\All Views\Update Frames
.这会通过更新视图的frame来匹配他们的约束,从而修复在画布上的任何Auto Layout
的警告。如果Update Frames
不可用,那么你的一切都狠完美,这是不必要的运行。
完成了你的所有的约束后,最后一步就是为Line Chart View
的高度创建一个outlet的约束。在Document Outline
中找到Line Chart View
然后点击显示的三角形。
然后点击三角形,找到我们需要的高度约束。选中他,然后按住Ctrl,拖拽到 Assistant Editor
,放在其他outlet的下面。在弹出框中确保Connection
被设置为Outlet
,在Name
中输入lineChartHeightConstraint
。点击Connect
。
实现TodayViewController.swift
现在接口已经确立了,而且所有的东西都已经连接上,在Standard Editor
中打开 TodayViewController.swift
。
你会发现你在与一个普通的UIViewController的子类打交道。很欣慰,对不对?一会儿后你会遇到NCWidgetProviding
协议中的一个叫做widgetPerformUpdateWithCompletionHandler
的方法。在这个教程的结尾你会学到更多与它相关的东西。
这个ViewController
主要负责显示当前的价格,价格的区别,响应button的点击和在line chart中显示价格的历史。
在TodayViewController
的顶部定义一个属性用来跟踪线形图(line chart)是否可见:
1
|
|
现在用下面的实现替换掉viewDidLoad()
的模板代码:
1 2 3 4 5 6 7 8 9 10 |
|
这个方法会做以下几件事情:
1.设置线形图的height约束的约束值为0,所以线形图默认是hidden的。
2.设置线形图的dataSource和delegate为self。
3.设置一些占位文案在两个label上。
还是在TodayViewController
中,添加如下方法:
1 2 3 4 5 6 7 8 9 10 11 |
|
其中的fetchPrices
方法是在CurrencyDataViewController
中定义的,他是一个可以异步调用block。这个方法会向我们在这个教程开始的时候提到的web-service
发送一个请求来获取比特币的价格信息。
在方法的实现块中更新所有的label和线形图。更新方法也在父类中为你定义好了。他们通过fetchPrices
对需要的值做一个简单的检索以及合理的格式化后用来显示。
由于Widget的设计,你还需要实现widgetMarginInsetsForProposedMarginInsets
方法用来提供自定义的 margin insets
,添加如下代码到TodayViewController
中:
1 2 3 4 |
|
Widget默认有一个很大的left margin
,这在Apple很多的默认Widget上是恨明显的。如果你想要填充整个通知中心的宽度,你必须实现这个方法并且返回UIEdgeInsetsZero
,他会把所有的边的margin insets
都转化为0。
现在是时候看看你到目前为止都有些啥了。选中BTC Widget
这个Scheme
。编译运行。当app运行后提示的时候选择Today
。
*如果通知中心没有显示,那么在屏幕的顶部用手指向下滑动来激活它。
*如果Widget没有在通知中心显示,你需要通过编辑菜单来添加它。在今日视图的底部你会看到编辑按钮。点击按钮展开所有安装在系统中的Widget。在这里你可以随心所欲的enable, disable以及re-order他们。如果BTC Widget没有处于enable状态,则enable他。
屌爆了,有木有!你的Widget现在已经在通知中心实时的显示比特币的价格。但是你可能已经意识到了一个问题。按钮不能工作以及看不到线形图。
接下来,你要为你添加的button实现toggleLineChart
方法,用来展开widget的视图并且显示线形图。就像方法名说的一样,这个button就像一个转换键一样;他也会折叠视图从而隐藏图表。
用下面的代码替换空的toggleLineChart
:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
这个方法用来操纵线形图的高度约束,从来约束他的显示。它也对button有一个旋转变化,所有它可以准确的反应出图表的可见度。
约束更新后,你必须重新加载图表的数据,以便它在新的约束上重新绘制。
你会在viewDidLayoutSubviews
上做这些。添加如下代码到TodayViewController
:
1 2 3 4 |
|
确保BTC Widget这个scheme被选中。编译运行。当App运行的时候出现提示就选择Today
。
在左图,你会看到当图表隐藏的时候widget是如何展示的。在右图,你会看到当图表打开时widget是如何展示的。不是很寒酸!
添加如下代码到TodayViewController
,你会拥有一个快速更新的线颜色以及目光尖锐的widget:
1 2 3 4 5 |
|
确保当前的scheme依旧选中,编译运行。当app运行的时候出现提示就选择Today
。
你最后要做的就是通过允许系统来创建一个快照,当屏幕关闭的时候添加对Widget更新视图的支持。系统会定期的帮助你的widget保持最新。
用下面的代码替换掉widgetPerformUpdateWithCompletionHandler
现存的实现:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
这个方法做以下几件事情:
*他通过调用fetchPrices来从网络服务中获取当前的价格数据。
*如果没有错误,接口就会更新数据。
*最后 - 就像NCWidgetProviding协议要求的一样 - 方法调用系统提供的带着.NewData枚举的block。
*发生错误的时候,block会带着.Failed枚举调用。这会告诉系统没有新的可用的数据,用现存的快照吧,亲!
你可以在这里下载最后的工程.
现在去干哈
iOS8通知中心是你自己个人的playground
!Widget在一些其他的手机操作系统上早就已经有了,Apple最终提供给你一个创造他们的能力。
作为一个有进取心的开发者,你可能想要再看看你现在的app,思考怎么用widget去提升他们。利用widget的可能性更进一步想象新的app的idea。
如果你喜欢学习更多关于创建其他类型的iOS8的App扩展。看看我们的书 iOS 8 by Tutorials,这里你可以学习关于Photo Extensions
, Share Extensions
, Action Extensions
以及更多的知识。
我们迫不及待的想看你能想出什么东西来了,希望你的Widget尽快的在我们的通知中心的顶部!