说三道四技术文摘-感悟人生的经典句子
说三道四 > 文档快照

Apple TV指南:从零开始用Swift开发个tvOS应用

HTML文档下载 WORD文档下载 PDF文档下载
这篇文章主要介绍了如何在Apple TV上开发一个名为tvOS的App,以及关于tvOS的一些相关的定义,最终呈现给用户一个非常有趣的界面。

本文出自:Jameson Quave's Blog,作者:Jameson Quave,译文出自:SwiftGG,译者:ray16897188

 

教程结束时,我们会做出这样一个应用:


你更喜欢哪个艺术家?

第一部分

开始

在我们开始之前你需要安装 Xcode 7.1 beta 版,可以从这里下载:下载 Xcode 7.1 Beta。

注意:下载 Xcode 7.1 Beta需要有一个苹果开发者账号,由于目前 Xcode 是预发行版,以后正式发布的时候可能会有变化。
安装的时候要注意,如果你重命名Xcode7.1应用,会遇到一个已知Bug。一定有人会这么做,所以提前说明……别这么做,否则你的tvOS模拟器会崩溃。

同时要注意,虽然支持优胜美地 (Yosemite),但是在该操作系统上,功能会受限。推荐用 OSX 10.11 El Capitan 或更新的系统。ElCapitan beta可以在这里下载。

下面我们来介绍一些 tvOS 相关的定义。

TVMLKit

TVMLKit 是 Apple 设计的一个新框架,能在使用 Swift 或 Objective-C 实现应用逻辑的同时使用 Javascript 和 XML 开发更炫酷的用户界面。

TVML

TVML 是“TV Markup Language”(TV 标记语言)的缩写,基本上是一些 XML 语句,用于实现基于C/S(client-server,客户端-服务端)架构的 tvOS 应用布局。布局界面时,我们会用到一些 Apple 提供的 TVML 模板创建我们的 UI,然后用 TVJS 写交互脚本。

TVJS

我能告诉你的是,TVJS 就是你(可能已经)熟悉的 JavaScript。

Hello World

我们从一个基本的 hello world 程序开始。就 Apple TV 而言,我们可以只把"Hello World"输出到控制台上。这也许是个不错的开始,但更好的选择是使用 Apple TV 的一些 TVMLKit 元素在屏幕上创建一个模板。

首先,打开 Xcode 7.1 并创建一个新项目。你可以看到一个模板列表,我们在左侧选择CHANGE tvOS,然后再选Single View Application模板。

这样就会根据 tvOS 模板创建一些默认文件和一个简单的 Swift 入口点,对一会儿创建 UI 很有帮助。

建立 TVJS 主文件

在 C/S 架构的 tvOS 应用中,服务端本质上就是 TVML 和 JavaScript 文件以及和它们相关的所有数据。JavaScript 文件会装载 TVML 并把页面(page)放入视图栈中。可以从另一个角度理解:JavaScript 文件就像 TVML 文件的路由器或是控制器(controller),而 TVML 文件本质上是若干视图(views)。

拉开序幕

首先我们要修改应用的AppDelegate.swift文件。第一步是让我们的应用遵循TVApplicationControllerDelegate协议。该协议定义在 TVMLKit 框架中,所以需要导入它。更新AppDelegate.swift文件,如下所示:

import TVMLKitclass AppDelegate: UIResponder,UIApplicationDelegate,TVApplicationControllerDelegate {....
此协议包含四个 tvOS 实现AppDelegate后会调用的函数,用于给我们的应用发送 tvOS 生命周期通知。现在我们无需操心这些,但在后面的教程中我们会对它们进行深入研究。目前只要像上面的代码那样把协议加进去就够了。

下一步,我们要添加一些代码,让 JS 文件起作用。由于是 beta 版,我们还需要自己完成这些工作。我相信在 Xcode 的后续版本中这一步会变成一个模板。

在程序里didFinishLaunchingWithOptions这个函数中我们要完成一些步骤。它们对所有应用来说都是一样的,所以你可以直接复制这段代码:


// 在一个可选属性中保存对 appController 的引用var appController: TVApplicationController?func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {  self.window = UIWindow(frame:UIScreen.mainScreen().bounds)  let appControllerContext = TVApplicationControllerContext()  let jsFilePath = NSURL(string: "http://localhost:8000/main.js")  let javascriptURL = jsFilePath!  appControllerContext.javaScriptApplicationURL = javascriptURL  if let options = launchOptions  {    for (kind, value) in options    {      if let kindStr = kind as? String      {        appControllerContext.launchOptions[kindStr] = value      }    }  }  self.appController = TVApplicationController(context: appControllerContext, window: self.window, delegate: self)  return true}

简单说说这段代码干了什么:它拿到了一个TVApplicationControllerContext引用,这个Context只是为我们的AppDelegate类提供了一些启动数据,然后给了我们一个能调整和修改启动过程的接口。接着把URL 传给待会儿要运行的main.js文件,并将appController的路径设置成这个 URL。

现在就要添加我们的 JavaScript 文件了,点击 File > New,然后在 iOS tab 下面选择 Other >Empty file。将这个文件命名为main.js。

用同样方法创建一个hello.tvml文件。

在main.js文件中添加一些简单的 JavaScript 代码,用来装载hello.tvml文件:

function getDocument(url) {  var templateXHR = new XMLHttpRequest();  templateXHR.responseType = "document";  templateXHR.addEventListener("load", function() {pushDoc(templateXHR.responseXML);}, false);  templateXHR.open("GET", url, true);  templateXHR.send();  return templateXHR;}function pushDoc(document) {  navigationDocument.pushDocument(document);}App.onLaunch = function(options) {  var templateURL = 'http://localhost:8000/hello.tvml';  getDocument(templateURL);}App.onExit = function() {  console.log('App finished');}
现在在hello.tvml文件中添加:

<document>  <alertTemplate>      <title>Hello tvOS!</title>  </alertTemplate></document>

TVML 文件是 UI 的实际内容。文档(document)必须用模板编写,否则现在的代码运行时会崩溃。这个 TVML 文件只是包含了一个简单的模板和一个单元素的标题。

在编写这些代码时我发现一个问题:本地无法引用这些文件,文件必须放在一个 web 服务器上。所以最简单的解决方案是找到你刚创建 TVML 和JS 文件的位置,并在命令行中敲进如下指令:

启动服务端

python -m SimpleHTTPServer 8000

这条指令用 Mac OS 内建的 python 解释器开启了一个端口号为 8000 的 web 服务器,可以用它来托管本地文件。如果在命令行中,执行了上面给出的代码,那么现在按一下Xcode 的 play 按钮就能在 tvOS 模拟器中启动了。还有一个要注意的事情:这是一个不够安全的 HTTP 请求,在 iOS 9 中会被默认的应用传输安全机制拦截。为了能够按之前的方法来使用本地主机,我们需要在Info.plist文件中添加一个key。

允许直接加载(Allows Arbitrary Loads)

选择Info.plist文件然后按加号(+)来创建一条新记录。在列表中选择”App Transport Security Settings”并按 return 建。这将创建一个新的字典条目,展开它,在这行上按加号(+)添加一个子行。接着选中”Allows Arbitrary Loads”并将其设为true。都设好了之后我们就能用模拟器运行应用了。

添加按钮

在本例中你看到的实际上是一个被 Apple 称作alertTemplate的模板。你还能嵌入一些基本控件,比如在模板中添加文字和按钮。试着添加一些按钮吧:

<document>    <alertTemplate>        <title>Hello tvOS!</title>        <button>            <text>A Button</text>        </button>        <button>            <text>A Second Button</text>        </button>    </alertTemplate></document>

这里我们只加了子按钮(child button)元素,每个子按钮都有它自己的子文本(child text)元素。这段代码在 tvOS模拟器上全屏显示alert和两个按钮。如果你自学能力很强,苹果的官方文档中列出了你能使用的所有模板和控件。

第二部分

增加交互事件

在第一部分中我们创建了一个简单的 TVML document,里面有几个按钮。这个document看起来是这样的:

<document>	<alertTemplate>		<title>Hello tvOS!</title>		<button>			<text>A Button</text>		</button>		<button>			<text>A Second Button</text>		</button>	</alertTemplate></document>
这是一个带按钮的警告(alert)界面,目前这些按钮没有任何作用。这段代码直接硬编码了具体内容,更好的方式是使用代码生成XML,在 JS 中很容易实现。我们在main.js文件中添加一个新函数,把上面的代码封装成一个更简单的警告界面,它只包含一个 OK 按钮。

function alert(str) {	var alertXMLString = `<?xml version="1.0" encoding="UTF-8" ?>	<document>		<alertTemplate>			<title>Hey Listen!</title>			<description>${str}</description>			<button>				<text>OK</text>		</button>	</alertTemplate></document>`var parser = new DOMParser();var alertDOMElement = parser.parseFromString(alertXMLString, "application/xml");navigationDocument.presentModal(alertDOMElement);}
这里创建了一个alertXMLString字符串,它表示的是包含一个按钮的简单警告界面所对应的 TVML。description节点比较特殊,我们使用TVJS 的内嵌字符串语法${variable}来插入str的值。

接下来,创建一个新的DOMParser对象,把这个字符串转换成一个实际的 XML DOM 元素。

最后,我们用navigationDocument的presentModal方法展示一个模态框,内容就是上一步的 DOM 元素。navigationDocument是一个全局变量,它永远指向XML 文档的根节点。

现在,删除onLaunch函数中之前的代码,直接调用刚才创建的函数……

App.onLaunch = function(options) {	alert("Hello!");}


Hello警告

运行应用,你会看到一个炫酷的”Hello!” tvOS 警告。但是点击 OK 没有任何反应。我们该怎么处理像触摸之类的事件呢?

通常来说,在 JavaScript 和 TVML 的世界中,你需要给 DOM 元素添加一个事件监听器(event listener)。举个例子,我们可以给alert函数添加第二个参数,把 OK 按钮触发select事件时需要调用的函数作为参数传入。下面我们就加入这个名为doneCallback的参数:

alertDOMElement.addEventListener("select", function() { doneCallback }, false);
更新后的完整函数如下:

function alert(str, doneCallback) {	var alertXMLString = `<?xml version="1.0" encoding="UTF-8" ?>	<document>		<alertTemplate>			<title>Hey Listen!</title>			<description>${str}</description>			<button>				<text>OK</text>			</button>		</alertTemplate></document>`var parser = new DOMParser();var alertDOMElement = parser.parseFromString(alertXMLString, "application/xml");alertDOMElement.addEventListener("select", doneCallback, false);navigationDocument.presentModal(alertDOMElement);}

现在我们可以修改之前的onLaunch函数,添加一个回调函数来显示一个 TVML 页面。在此之前,我们需要再添加一个getDocumentContents函数,它会在页面加载完毕之后调用回调函数。这个回调函数只有一个参数,用来接收 XMLHttpRequest 对象的响应内容。这样我们就可以轻松地加载多种 TVML 文件。 

function getDocumentContents(url, loadCallback) {	var templateXHR = new XMLHttpRequest();	templateXHR.responseType = "document";	templateXHR.addEventListener("load", function() { loadCallback(templateXHR) }, false);	templateXHR.open("GET", url, true);	templateXHR.send();	return templateXHR;}
代码和之前定义的getDocument方法几乎一样,区别是这里是异步操作,而且不会在界面上显示任何内容。 

有个这个函数,我们就能执行下面的调用,当 OK 按钮被点击时替换屏幕上的警告内容。 

App.onLaunch = function(options) {    alert("Hello!", function() {      var helloDocument = getDocumentContents("http://localhost:8000/hello.tvml", function(xhr) {        navigationDocument.dismissModal();        navigationDocument.pushDocument(xhr.responseXML);      });    });}
我们使用stackTemplate模板来改写hello.tvml文件,这样界面会更有趣。stackTemplate非常适合用来展示一组包含标题和图片的列表内容。下面是本例用到的内容:

<document>    <stackTemplate>        <banner>            <title>Which Artist Do You Prefer?</title>        </banner>        <collectionList>            <shelf>                <section>                    <lockup>                        <img src="http://localhost:8000/nina.png" width="256" height="256" />                        <title>Nina Simone</title>                    </lockup>                    <lockup>                        <img src="http://localhost:8000/coltrane.png" width="256" height="256" />                        <title>John Coltrane</title>                    </lockup>                </section>            </shelf>        </collectionList>    </stackTemplate></document>

这基本上就是stackTemplate的布局方式,banner是顶部的横幅内容,collectionList包含许多shelf对象,而shelf对象则包含许多section对象,section对象又包含许多lockup对象,最后这个才真正包含我们的图片和标题。在本例中我向目录中添加了一些图片,它们是nina.png和coltrane.png。


预告:  2015中国移动开发者大会(MDCC 2015)将于10月14日-16日在北京新云南皇冠假日酒店召开。大会特设九大技术专场:平台与技术(iOS)、平台与技术(Android)、平台与技术(跨平台)、产品与设计、游戏开发、企业移动化、虚拟现实专场、硬件开发与技术、嵌入式开发。大会将聚集国内最具实力的产品技术团队,与开发者一道进行最前沿的探讨与交流。 

第一时间掌握最新移动开发相关信息和技术,请关注mobilehub公众微信号(ID: mobilehub)。

吴毅挺:OpenStack是开源IaaS平台大势所趋的集大成者 做好扁平化设计-交互篇 狙击Google Glass?三星将推基于Tizen系统的Gear Glass GamePlay:跨平台开源3D游戏引擎,黑莓的努力你不懂 “少即是多” 高通QRD大会精华汇总 天云盾:构筑在云端的Web安全防护 解析大数据时代的数据库集群技术 莫里航海图,最早的大数据实践 弥补空缺!HappyFox面向iOS开发者发布开源移动HelpDesk SDK Chitika北美网络流量报告:WP KO黑莓、iOS超Android 《近匠》秦时明月:知名IP光环下的生存之道 微软研究院正式发布编程学习游戏Code Hunt 走近讲师:Memblaze刘爱贵讲述云计算中的闪存应用 【云先锋】ClouDil云方案:PB级大数据解决方案 一周消息树:这是要“发”啊?Google Brain之父正式加盟百度 最受欢迎的游戏引擎集结号:小白篇 多盟总设计师邢开捷:四两设计拨千金 Cocos2d-x游戏《Monster Strike》终结《智龙迷城》霸榜神话 高通物联网技术解决方案:从汽车到智慧城市 Arduino携手Atmel 发布Arduino Zero开发板 以色列玫瑰:细数十位科技创新领域的女性佼佼者 8款最佳的开源在线学习CMS系统 在发布站点前,Web开发者需要关注哪些技术细节? TOCC、中航信、高德、北斗平台重磅专家登场 邀您共享ITS技术盛宴 腾讯大数据之TDW计算引擎解析——Shuffle AMD APU14 BEIJING技术创新大会精彩看点 这是云整合时代开始的节奏吗?Rackspace考虑退出 一周热点:如何使用15美元每月的机器实现百万文档检索 黑客获取数据信息的目的和进攻手段及应对之策 Surface Pro 3遭大曝光:799$起、四色可选、12寸屏幕 当软件遇上设计,浅谈敏捷UX VS.精益UX 关于光驱 如何获取delphi中dbgrid的单元格的内容 问题征解,关于一个智力题的程序解法。 sos!!求救!!!java运行时环境设置的初级问题!各位大侠帮忙了!!!! :-(( 關于delphi6的ActionMainMenuBar组件和ActionToolBar组件 的菜鳥級問題?? 李维的电子商务篇的第二章的例子没法运行,这是为何? 谁能告诉我学VC的步骤和教材啊,还会加分啊 如何将一个可以滚动的窗口的全部内容转换为BMP保存起来(如:IE)?(提供线索就有分)欢迎参与成果供享!!! 请教关于VsPrint Oracle8i Enterprise Manager Release 2.1 相当于ORACLE8.1.几啊? 使用ListView如何操作数据 ★★★简单问题送100分★★★:如何打印框架容器里的的全部内容,包括mschart图表,label,combox等? 我的DW里加入FLASH特效时为什么中文会变成乱码?我给分的............ 我感到天昏地暗、日月无光、前途渺茫、无心睡眠、精神恍惚、双目痴呆相恋8年准备结婚的女友突然和别人结婚了!我受不了了!我快疯了! 请教有关IE5带的ActiveX控件Dhtmled.ocx的98下安装问题 程序员与高级程序员到底那个好过 关于 delphi的一点东东 access一个日期时间字段是短格式的,用什么SQL语句改成长格式的? 怎样才能在主页中显示自己的qq是否在线?????? 有没有平面按钮控件 Interdev6.0 IDE 环境下无法使用MSDN access一个日期时间字段是短格式的,用什么SQL语句改成长格式的?? 大家来帮帮我想想?应该怎么样来删除呢!!!! 各位用VB.net的兄弟,请问你们使得是 Beta1 还是 Beta2 ? 迷周星弛的请来签名啊! 如何操纵RichTextEdit控件??? 我得到GIF图像的这一点的值,怎样得到它的正确的RGB值? 我想请问在CHTML(compact html)中如何对页面进行刷新。 各位老大,你们是如何找到工作的? 在VB.net中,如何实现VB6中的 set A=createobject("XXXX.XXXX") 诸位大虾救命,如何在EDIT控件中判断DBCS字符 !!求救!!对时!!! 在VC中使用Crystal report 8的问题 如何编写c程序自动对GIF图片设置大小 请教:鼠标在windows桌面上按键是否会引发某个事件? 查看DLL的输出函数 menu的问题 web的组件由什么来编写? 嵌入式数据窗口,怎样可以动态往里插数据窗口??? 在DELPHI中如何调用NETBIOS的API函数 AddNew 怎么用?- 怎么办那,啊? 问题标题前的绿色的对号是什么意思?是不是表示问题已经解决? 怎么学VC啊,菜鸟的菜菜的问题啊。 关于打印比较 提取汉字的拼音 四级挂了,给分,气死了 难题时时有,把它问诸位,不知哪位高手,今日肯帮俺------解决送个人照片(不知各位肯要否?) 刚考完英语四级,来这里发泄一下。同时送上300分! PHP和JAVA如何通讯? 请问哪里有 soft-ice for 2k 求函数f(x)=3x^2-5x+2.x属于[0,2]的值域? 5除=5分之1解方程 4.2X除1.6—14=0.175解方程 40分之7等于x加20分之70怎么计算?打错了 是40/7=X+20/30 函数y=(x^2-4x-5)/(x^2-3x-4)的值域是?.. 解方程4^x-2^(x+2)-12=0 解方程:4.8+2.1x=15.3, +求函数y=(5x-1)/(4x+2)x属于【-3,-1】的值域. 5/1X-4/1X=40 怎么解(方程) 解方程:20%X-5分之1=5分之4 4分之1X+3分之1=1又6分之1 求函数y=(x^2-4x+5)/(x^2-3x-4)的值域 x-5分之1x=40 解方程 解方程:5分之2+5分之1X=2 3x的平方减5x减2分之1减3的差加2x方的和 35x-12=51 x+2.1x=1.55 3/4x+1/2x=75 4.2x除以2=12.6应用1、强强和年年同时从学校出发,沿同一条路去2000米外的少年宫,强强骑车每分钟行210米,年年走着去每分钟60米.多少分钟后两人相距1200米2、(这个不 解方程.2分之1X+3分之1X=20分之1 X分之0.5=0.8分之3 X:3.25=5分之1:4分之5 求函数y=3x+1/x-2的值域 如果x+24=51,那么x-24=()写出思考过程 解方程 1x=(1-20%)乘(x+6)解方程 求函数Y=3x/(3x+1)的值域. 五十一至五十五 1、方程-0.1x=1的解是________.2、方程3(y+2)=y的解是________.3、方程2x+3=4x-3的解是________. 1.求函数 y=(1/3)^(-x^2+5x)的值域 2.函数y=(2/5)^(根号下7-3x+21.求函数 y=(1/3)^(-x^2+5x)的值域2.函数y=(2/5)^(根号下7-3x+2x方) x-5分之2-3分之1x=5分之12 怎么解 1).-2X=3,2).x+2=-1,3)4X-3=9,4)4-2/1X=5,给这几题解方程~.. 求函数的值域y=3x+2/x-1 用配方法解方程 (1) 2x平方-4x-1=0 (2) 4分之1x平方-6x=3=0 1又3分之1x+8×2分之1=16(解方程) 已知函数f(x)=lg(2+x)+lg(2-x) 1.函数g(x)=[10^f(x)]+3x,求函数g(x)的值域.2.若不等式f(x)>m有解.求实数m的取值范围 4分之1x除以百分之40等于8分之5 m为何值时,一元二次方程m(x²-2x+1)-2x²+x=0(1)有两个不相等的实数根?(2)有两个相等的实数根?(3)没有实数根? 若x 0.7X1又9分子4+2又4分子3x(-15)+0.7x9分子5+4分子1x(-15)等于多少 已知一元二次方程x²-2x m=0若方程的两个实数根为x1、x2,且x1+3x2=3,求m的值 函数f(x)=(3x-1)/(3x+1)的值域最说出怎么算的注:是3的x次方 f(x)=(3^x-1)/(3^x+1) 二楼的SNOWHORSE70121,u为什么不等于1? 2乘7分之2+3分之1x=2.5怎样解方程, 已知关于x的一元二次方程x²-2x-m=0有实数根 6X的平方减5X等于2,用工式方法解 解方程 6分之1x-12.5%乘7=3分之2 急,解一元二次方程:-x²/4+5/2x=0 已知X为整数,且分式2X+2X\X的平方-1的值 解方程3分之2X-4分之1X=8分之5 20(X+5)=200 8分之1.[7分之1(6分之1X-5)-4]-3=2 解方程 使分式(x+1)的3次方分之2X的平方-4X+2的值为整数的X的整数值有哪些 解方程:【x-20-12】乘4分之1=【x+12】乘16分之3 4分之1x÷2=8分之3 解方程 、、、谢谢 当x不等于零时,函数 y=3x+1/2x 的值域是 A.[-√6,√6]B.(-∞,-√6]∪(√6,+∞)C.(-∞,-√6]∪[√6,+∞) 解方程12分之x-20分之2x-1=3分之x+4-1时方程两边都乘 4分之1x÷2=8分之3 1)y=x^2+3x-4:x^2+x+2 (2)y=x^2-5x+4:x^2-2x-8,x大于等于2小于等于3.求值域, 解方程x^3-13x-12=0 12分之1x+8分之5=4 解方程 y=-x+1/2x-3(-2 15(x-12)-13x=1020 8一X的负倒数为19,X一97 (1)y=x^2+3x-4:x^2+x+2 (2)y=x^2-5x+4:x^2-2x-8,x大于等于2小于等于3.求值域 解方程:x-16分之3x=27.2 当x等于多少时,√x+√6与√x-√6互为倒数 函数f(x)=3x的平方-5x+2.x∈[0,2]的值域是可以不用配方法求吗 有别的方法求出来吗? 解方程:2分之1:3分之1=4分之1:x . 3:x=9:27 . 6:25分之9=5分之4:x .x:12=4.2:3.5.有4题,要过程. 0.75加25%X等于8分之7,
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn