过去几年中,移动应用像风暴一样席卷世界,改变了我们的网上工作、娱乐方式。很多移动应用开发技术应运而生,而移动也开始得到开发过程重视。尽管移动已经看似无所不在,但未来才刚刚开始。新一代的移动设备,好比可穿戴设备,好比物联网的许许多多移动构件,就在我们面前。我们会发现:用于数据展示和命令接收的用户界面不断地推陈出新;越来越多的公司站在真正的移动浪潮之巅。这会在未来几年中影响软件的设计、开发、测试方式。

\\

InfoQ的这篇文章是快速变革的移动技术系列的一部分。你可以在这里订阅该系列的新文章通知。

\\

今年,随着Swift编程语言及其1.0版的发布,苹果允许向AppStore提交用Swift开发的iOS应用,为iOS编写应用从未如此简单。这篇文章展示了用Swift创建iOS应用的全过程,以及如何通过SceneKit展示3D图形。

\

本文中代码的Swift版本为1.0版,仅供学习使用。\

软件需求

\\

创建本文相关代码之前,先要在OSX 10.9或更高版本中安装Xcode 6.1。文中的代码可以直接在模拟器中运行,想要部署到硬件设备上的话,需要激活iOS开发者账号。

\\

iOS新项目创建

\\

创建新项目,可以通过Xcode启动时的欢迎对话框,或是“文件→新建→项目”菜单。

\\

\\

出现在Xcode新窗口菜单中的新对话框包含了多种iOS或OS X应用选项。有很多缺省模版类型,用以创建不同行为的示例代码项目。

\\

\\

选择“iOS→应用→游戏”可以从众多不同类型中选择一个来创建模版:

\\

  • SceneKit\\t
  • SpriteKit\\t
  • OpenGL ES\\t
  • Metal\

SceneKit创建一个面向通用设备的名为MerrySwiftmas的Swift应用。(通用应用意味着该应用可以运行在iPad和iPhone/iPod上;如果应用只面向一种设备,可以在此处或接下来的info.plist中进行更改)。

\\

\\

运行项目

\\

这样就根据模版创建了一个新项目。项目的用户界面可以被划分为四个独立区域:左边是导航区(可以通过快捷键⌘1-9进行切换);右上部是各种检视器(可以通过快捷键⌥⌘1-9进行切换);右下方是各个类库(可以通过快捷键^⌥⌘1-9进行切换)。

\\

\\

窗口中间是编辑区,该区域会显示导航区所选中的文件。选中最顶层项目时,编辑区会展示一系列通用信息(这些信息存储在info.plist文件中);单击选中其他文件时,编辑区会发生变化,双击文件时,则会弹出含有该文件的新窗口。

\\

窗口顶部是运行按钮;运行意味着构建并启动缺省目标设备及应用;本例中,是一个加载应用的模拟器窗口:

\\

\\

Swift代码简介

\\

飞船的加载、显示是靠GameViewController.swift文件中的viewDidLoad函数。

\\

\import UIKit\import SceneKit\\class GameViewController: UIViewController {\  override func viewDidLoad() {\    super.viewDidLoad()\    let scene = SCNScene()\    ...\    let scnView = self.view as SCNView\    scnView.scene = scene\    scnView.autoenablesDefaultLighting = true\    scnView.allowsCameraControl = true\  }\  ...\}\

\\

正如其他C类型语言,块集成用花括号{},类成员访问用.,函数和构造器传参用括号()。参数可以是定位参数或命名参数,冒号隔开名称和值。

\\

虽说Swift是静态编译的强类型应用,却不必声明变量类型。Swift会在编译时自动推导。Var用于声明变量,let用于声明常量。

\\

关键字as用于类型转换,有了它,源于UIViewController超类、被声明为UIViewself.view就可以当SceneKit SCNView使用,进而对诸如scene或是allowsCameraControl这类SceneKit特有的字段进行访问。

\\

替换宇宙飞船

\\

视图控制器首次加载时,viewDidLoad()的函数体会被调用,函数体可以用上述代码替换。运行替换后的代码会得到不含其他信息的黑色屏幕。

\\

场景图像通过结点结构进行展示,并以rootNode为顶层结点。任何结点都能添加子结点;结点的翻转变换会统一影响其所有子结点。

\\

向图中添加柱体,可以像下面这样,创建一个含有SCNCylinderSCNNode并添加到图中:

\\

\...\scnView.allowsCameraControl = true\\let rootNode = scene.rootNode\let cylinder = SCNCylinder(radius:1,height:3)\let tree = SCNNode(geometry: cylinder)\rootNode.addChildNode(tree)\

\\

如果现在运行应用,你会看到一个相当无趣的白块:

\\

\\

柱体看上去并不具备3D效果,这只是因为柱体尚未上色、场景尚未投灯光。这些都很容易搞定:可以像下面这样,将柱体的漫反射材质上色,场景投自动灯光:

\\

\...\rootNode.addChildNode(tree)\\cylinder.firstMaterial?.diffuse.contents = UIColor.brownColor()\scnView.autoenablesDefaultLighting = true\

\\

现在再运行应用,柱体就会显示出着色后的的3D效果。因为场景允许摄像控制(采用上帝视角观察),所以可以通过手指或鼠标手势拖动场景:

\\

\\

下一步是在柱体之上添加锥体,方法与添加柱体类似。

\\

\...\scnView.autoenablesDefaultLighting = true\\let cone = SCNNode(geometry: SCNCone(topRadius:0, bottomRadius:3, height:3))\cone.position.y = 3\cone.geometry?.firstMaterial?.diffuse.contents = UIColor.greenColor()\tree.addChildNode(cone)\

\\

为了把锥体放在柱体之上,初始位置上移了3个单元,但仍保持和柱体中心对称。?.是可选访问器:如果值为空,那么一切都不变;如果值非空,那么计算表达式的剩余部分。

\\

现在应用的运行结果如下:

\\

\\

完成树

\\

由于要在顶部添加更多锥体,锥体的创建代码会被重复多次。也可以用含有这部分代码的for循环替代。

\\

\...\scnView.autoenablesDefaultLighting = true\\for i in 1...3 {\  let cone = SCNNode(geometry: SCNCone(topRadius:0, bottomRadius:3, height:3))\  cone.position.y = 2 * Float(i) + 1\  cone.geometry?.firstMaterial?.diffuse.contents = UIColor.greenColor()\  tree.addChildNode(cone)\}\

\\

for循环中,用1...3代表闭区间[1,2,3]。半闭合区间[1,2]可以用1..\u0026lt;3表示。通过堆叠锥体来生成树。

\\

整型i可以通过Float强制转换成浮点型以便根据树根计算相对位置。

\\

应用运行时,会显示一棵树:

\\

\\

添加礼物

\\

SCNBox的话,可以把礼物放在树下。柱体(即树的底部)的位置在0,0,0及其之上,盒子的位置要略低一点:y=-1Xz方向随意。可以显式指定位置,但for循环所允许的位置只有(0,1),(1,0)以及(1,1)。

\\

\for i in 1...3 {\  let present = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0))\  present.geometry?.firstMaterial?.diffuse.contents = UIColor.blueColor()\  present.position.x = Float(i % 2) * 2\  present.position.z = Float(i / 2) * 2\  present.position.y = -1\  tree.addChildNode(present)\}\

\\

\\

重构为类

\\

把这些一股脑儿堆放在视图控制器加载的时候,并不是好的实践方案。这种情况下,代码复用和测试都会变得相对困难。实际上,创建树的相关代码可以单独成类。

\\

用“文件→新建→文件”创建一个名为ChristmasTree的iOS Swift文件。文件创建时为空,需要导入SceneKit并声明一个名为ChristmasTree的类。

\\

\import SceneKit\\class ChristmasTree {\}\

\\

想创建SCNNode子类的话,要把父类名称放在子类名称之后,并用冒号隔开。实现协议(接口)用的也是同样的语法。这样做时,还要添加一系列调用init的构造器:

\\

\class ChristmasTree: SCNNode {\  override init() {\    super.init()\  }\  required init(coder: NSCoder) {\    fatalError(\"init(coder:) has not been implemented\")\  }\}\

\\

Swift约定:对象要有一个主要(或缺省)初始化函数;在很多UIKit对象中,这意味着要遵循NSCoder协议,也就是说,要有一个编码器相关的init函数。通过把字段属性转换成序列化结构并还原,NSCoder能将Interface Builder中的对象持久化。如果你忘了写,Xcode会报错并帮你添上。这样做的实际效果就是,用Swift重写子类时,“需要”一个构造器,“重写”其他构造器。

\\

现在类框架写好了,也该补充内容了。把创建树的代码从viewDidLoad方法移动到ChristmasTreeinit方法中来:

\\

\override init() {\  super.init()\\  let cylinder = SCNCylinder(radius:1,height:3)\  let trunk = SCNNode(geometry: cylinder)\  cylinder.firstMaterial?.diffuse.contents = UIColor.brownColor()\addChildNode(trunk)\\  for i in 1...3 {\    let cone = SCNNode(geometry: SCNCone(topRadius:0, bottomRadius:3, height:3))\    cone.position.y = 2 * Float(i) + 1\    cone.geometry?.firstMaterial?.diffuse.contents = UIColor.greenColor()\    addChildNode(cone)\  }\\  for i in 1...3 {\    let present = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0))\    present.geometry?.firstMaterial?.diffuse.contents = UIColor.blueColor()\    present.position.x = Float(i % 2) * 2\    present.position.z = Float(i / 2) * 2\    present.position.y = -1\    addChildNode(present)\  }\}\

\\

这样就把树的添加工作留到视图控制器更新时再做:

\\

\override func viewDidLoad() {\  super.viewDidLoad()\  let scene = SCNScene()\  let scnView = self.view as SCNView\  scnView.scene = scene\  scnView.allowsCameraControl = true\  let rootNode = scene.rootNode\  let tree = ChristmasTree()\  rootNode.addChildNode(tree)\  scnView.autoenablesDefaultLighting = true\}\

\\

种植森林

\\

可以拥有上千棵树,为何满足于只有一棵?现在树被抽象为一个类。种一百棵树很简单。用嵌套for循环的position属性安排每棵树的位置。在步长不为1的情况下,可以通过内置函数stride生成数字迭代器。

\\

\for x in stride(from:0, to:100, by:10) {\  for z in stride(from:0, to:100, by:10) {\    let tree = ChristmasTree()\    tree.position.x = Float(x)\    tree.position.z = Float(z)\    rootNode.addChildNode(tree)\  }\}\

\\

这样,一百棵树就种好了,间隔10个单位,分布在2D网格中。

\\

\\

所有树看上去都很相似,部分原因在于所有礼物的整齐排列。加入树的旋转后,会不再那么相似。旋转用弧度衡量:圆的弧度为2π,因此,想得到随机位置,可以选一个180以内的随机数,除以180,再乘以π:

\\

\tree.position.x = Float(x)\tree.position.z = Float(z)\tree.rotation.y = 1\tree.rotation.w = Float(M_PI) * Float(arc4random_uniform(180)) / Float(180)\

\\

现在森林看上去有那么一点不一样了:

\\

\\

添加颜色

\\

目前所有礼物都是蓝色。如果你恰巧喜欢蓝色,就很不错,只是看上去会有一点单调。还有一种选择,你可以创建颜色序列,并随机选取。

\\

尽管Swift支持structenum类型的static成员,但直到Swift1.1,尚不支持static类成员。(实际上,要是你试着向Swift类中添加static成员,编译器会抱怨你不该用static,而应该用class,可当你用了class,编译器又会表示尚不支持)

\\

值得庆幸的是,类之外也能声明变量,这样声明的全局变量正好能存储构建好的颜色序列。通过随机系数就可以选取礼物的颜色。

\\

\let colors = [\  UIColor.blueColor(),\  UIColor.cyanColor(),\  UIColor.magentaColor(),\  UIColor.orangeColor(),\  UIColor.purpleColor(),\  UIColor.redColor(),\  UIColor.whiteColor(),\  UIColor.yellowColor(),\]\\class ChristmasTree: SCNNode {\...\      let present = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0))\      present.geometry?.firstMaterial?.diffuse.contents = colors[random() % colors.count]\

\\

\\

结论

\\

Swift是一种很容易上手的语言,因为它有自己的REPL(swift,简单交互式编程环境,源于Lisp,译者注);只通过几行代码就可以用SceneKit创建交互图形应用。最后,祝你们圣诞快乐!

\\

AlexGitHub空间可以找到该项目的源代码。

\\

关于作者

\\

二十年前,Dr Alex Blewitt第一次在NeXTstation上接触到Objective-C面向对象编程并一直使用至今。Swift的发布预示着OS X平台的未来,Alex为此写过一本书,Swift Essentials,该书定于下月出版发行。有空时,如果天气很好,Alex会从本地的Crafield机场试飞。

\\

过去几年中,移动应用像风暴一样席卷世界,改变了我们的网上工作、娱乐方式。很多移动应用开发技术应运而生,而移动也开始得到开发过程重视。尽管移动已经看似无所不在,但未来才刚刚开始。新一代的移动设备,好比可穿戴设备,好比物联网的许许多多移动构件,就在我们面前。我们会发现:用于数据展示和命令接收的用户界面不断推陈出新;越来越多的公司站在真正的移动浪潮之巅。这会在未来几年中影响软件的设计、开发、测试方式。

\\

InfoQ的这篇文章是快速变革的移动技术系列的一部分。你可以在这里订阅该系列的新文章通知。

\\\\

查看英文原文:Merry Swiftmas from InfoQ

\\


感谢夏雪对本文的审校。

\\

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ,@丁晓昀),微信(微信号:InfoQChina)关注我们,并与我们的编辑和其他读者朋友交流。

使用Swift和SceneKit开发一片圣诞树林相关推荐

  1. xcode13 swift语言 ios开发 快捷代码优化方式(代码重构)例子

    xcode13 swift语言 ios开发 快捷代码优化方式(代码重构)例子 这是自己在CSDN上发的第二篇文章,开始用markdown编辑,更加美观一些. 问题描述 在用xcode swift语言开 ...

  2. swift python混合开发_引用swift项目

    iOS - OC 与 Swift 互相操作 前言 在 Swift 语言中,我们可以使用 Objective-C.C 语言编写代码,我们可以导入任意用 Objective-C 写的 Cocoa 平台框架 ...

  3. 学习Swift:经验丰富的开发人员指南

    因此,您的老板希望您学习iOS应用开发. 或者,也许您有一个要处理的应用程序侧项目. 你从哪里开始? 学习新平台和编程语言可能会令人生畏. 即使您尝试学习基础知识,也会不断发布新技术,从而很难保持最新 ...

  4. linux下swift编程教程视频教程,Ubuntu 14.04下搭建 Swift 3.0 开发环境教学视频+PPT

    分享Ubuntu 14.04下搭建 Swift 3.0 开发环境教学视频+PPT. Linux版本:Ubuntu: 14.04 LTS Swift: 3.0 到https://swift.org/do ...

  5. 《Swift iOS应用开发实战》——2.2 了解故事板

    本节书摘来自华章计算机<Swift iOS应用开发实战>一书中的第2章,第2.2节,作者:刘铭 著, 更多章节内容可以访问云栖社区"华章计算机"公众号查看. 2.2 了 ...

  6. Swift Playgrounds 能够开发手机App么

    Swift Playgrounds 能够开发手机App么 "我只有一​​台iPad.我可以用它来开发iOS应用吗?" 对于初学者来说,这是最常见的问题之一.我的回答总是:" ...

  7. 从苹果Swift语言乱弹开发语言

    忽然想起来说这个,要从看了Swift语言开始,苹果新出了自己的开发语言,大有与Google的Go语言,IBM的X10语言挣势的架势,又有不少人站在后面摇旗呐喊了,喧闹的背后造成不少人的迷茫,是不是之前 ...

  8. Swift封装 计算器开发

    前言: 师弟要毕业设计,就敲了swift版的计算器给他参考下.现在把代码放上来,通过这个计算器,可以学习简单的封装:将逻辑与界面分离并提供接口的编程方式,这也是我们学习面向对象的必要点. 基于 xco ...

  9. ios 3D引擎 SceneKit 开发(2) --贴图篇

    hello ,大家好,我是Roc.Tian,最近一直在研究苹果自家的3D 引擎 SceneKit ,适当写写博客,总结一下,与大家分享一下,也希望跟大家交流,共同进步. 今天简单说一下 SceneKi ...

最新文章

  1. 前端的单页面模式和多页面模式
  2. php打印四行三列表格,php打印数组_php数组实例之表格状打印
  3. 机器学习线性回归_机器学习-线性回归
  4. jQuery经典案例【倒计时】
  5. Web2.0时代,RSS你会用了吗?(技术实现总结)(转载)
  6. 51C语言编译后执行到一半,“C语言” 读书札记之[再续编译执行]
  7. Python零基础学习笔记(二十)—— tuple元组
  8. python成功之路,Day1-发展历史
  9. H5JS二维动画制作!two.js的基本操作class2
  10. docker java镜像_Docker容器引擎与架构
  11. 2021 CCF网络推荐会议时间列表
  12. 结构相似度索引(SSIM)全攻略:理论+代码(PyTorch)
  13. 【STC单片机】STC15串口收发示例程序模板
  14. 哈尔滨啤酒集团有限公司盈利能力分析毕业设计
  15. 使用VLC-QT开源库开发流媒体播放器
  16. 游建慧:大山铺镇侧卧恐龙身畔的烟火人间骇伦
  17. 【7gyy】笔者支招:巧设安全模式防攻击
  18. Hbase性能测试及优化过程记
  19. 开发3D游戏建模都需要哪些软件?软件繁多,如何从中挑选学习?
  20. vue中使用svg图片

热门文章

  1. python运用在大数据中精准生活_在大数据中“精准”生活阅读答案
  2. 目前很火的自媒体平台,到底还值不值得站长们入驻
  3. DataWhale集成学习(中)——Task09提升(Boosting)方法和Adaboost
  4. python开发专属表情包_Python开发个人专属的表情包网站
  5. 互联网思维“独孤九剑”
  6. springboot 实现elasticsearch索引数据迁移
  7. Revit二次开发5、外部事件(ExternalEvent)
  8. 电影院选座(最优位置)问题
  9. 联想企业网盘助力中信证券上云,打造云中最美团队
  10. 3D Viewing: the Pinhole Camera Model(翻译)