![b83e7bf31e6e707f159b278af8d579f7.png](https://i-blog.csdnimg.cn/blog_migrate/bbc5ccbf33783d5a360ac90528b7dcd3.jpeg)
首先抱歉这最后一篇没有能赶上每天更新一篇的节奏,一个是因为最后完结篇的内容很多,另外是因为从上周五到现在感冒一直没好,影响了进度。
废话不多说,我们继续开始学习吧~
恭喜你,你已经来到了本系列课程的最后一部分,看完最后的这篇教程后,我们的入门系列教程就算是结束了。
不知不觉,我们已经在学习iOS开发的道路上前进了这么多。如果你仔细读过所有的内容,无论是唠叨,YY,实践,还是科普,那么你对iOS产品的开发应该有了一些基本的了解。
不过学无止境,而且在学习iOS开发的过程中,编码不是最重要的,设计和开发出满足用户需求的产品才是王道。永远记住这一点!无论是程序猿,产品经理或是设计师,都需要把这一点铭记在心。科技与艺术的结合才能打造真正优秀的产品,帮主早就为我们指明了前行的道路,至于如何做就看我们自己的了。
还是建议大家把英文水平提高下,多去看官方示例文档,多去stack overflow,多看github上的源代码。无它,唯手熟尔。当然,只追求手熟远远不够,这只能让你成为一个码农。在代码世界中,除了坚持写代码,更重要的还是要多思考,多学习。
在最后一章的内容中,我们将会涵盖以下内容:
1.支持更多设备分辨率
2.添加一些动态特效
3.设置应用的图标
4.设置应用的名称
5.在上进行测试
接下来我们将一一进行讲解。
支持更多设备分辨率
在目前的设计中,我们是为iPhone SE设备准备的,那么对于更大屏幕的设备支持如何呢?)。
首先将Simulator从目前的iPhone SE更换为iPhone8或者iPhone8 Plus,甚至是iPhone X,iPhone XR,iPhone XS MAX。
编译运行,你会发现呈现出来的效果不是我们想要的~
![5d8cdc61df8a66f016a80825bf86fabd.png](https://i-blog.csdnimg.cn/blog_migrate/37e00b7a66898fe5aedf222ab462983c.jpeg)
最大的问题就是,貌似留下了巨大的空白。如果你在真机上测试,那么也是一个德行~
是时候介绍iOS开发的一大利器-Auto Layout了!Auto Layout(自动布局)是UIKit的一项核心科技,可以让我们的应用轻松适配多种设备分辨率,包括4.7寸,5.5寸,5.8寸的iPhone,以及iPad。
在Xcode中打开Main.storyboard,打开底部的View as: 面板,然后选择iPhone 8设备(别忘了把Orientation调整回landscape)。
![a52bc846fb364b3b7f7b2749cece073d.png](https://i-blog.csdnimg.cn/blog_migrate/138f72e800dfdb0af2480a8f14e3c59a.jpeg)
首先让我们修复背景图片的显示问题。很明显,目前的背景图片无法填满更大的屏幕。
这个时候Auto Layout就可以发挥作用了。
在storyboard中,选择View Controller下的Background这个image view元素,然后点击Xcode底部的Add New Constraints。
使用该按钮可以定义名为constraints的关系,它会影响界面中所选视觉元素和其它视觉元素之间的显示关系。当我们运行应用时,UIKit将评估这些constraints,然后计算视觉元素的最终呈现方式。听起来好像有点抽象,不过实际试一下就知道了。
为了让背景图拉伸到屏幕的边缘,那么背景图的左、上、右、下四个角都必须和屏幕边缘平齐。那么使用Auto Layout,只需创建两个对其的constraints即可,一个水平的,以及一个垂直的。
在弹出的Add New Constraints 菜单中,将left,top,right和bottom的spacing都设置为0,然后确保每个红色I形条都被启用。(红色的I形条用来指定在添加新的constraints哪些应被启用。)设置完成后点击Add 4 Constraints即可。
![83d33d95f4426fceaf1aad9ebdfabfe5.png](https://i-blog.csdnimg.cn/blog_migrate/41e9713711184b7c71fe0333db1536f4.jpeg)
此时,你会看到背景图填满了整个视图。如果你没有看清楚变化,那么可以使用撤销和重复操作来看看具体的差别。
此时,在Document Outline下面可以看到新添加的Constraints:
![2d76a5943a8cdf17abc5dc7600bc617d.png](https://i-blog.csdnimg.cn/blog_migrate/85e623e383711e4aa0b1424be001b7f8.jpeg)
如果设置正确的话,此处应该显示了四个constraints,其中每个代表了图片的一个角。
选择iPhone 8 Simulator 编译运行项目,看看此时的显示是否正常。当然,其它视觉元素还没有完美显示,但是至少背景图片已经OK了~
注意:如果我们在storyboard界面里面将View as的设置切换回iPhone SE,那么背景图片的显示也是正常的。
现在让我们来设置关于界面。
在storyboard中选择About view controller,然后选择背景图,使用类似的方式Add New Constraints。具体的操作方式参考刚才的讲解,这里就不再废话了。
好了,现在关于界面的背景图也显示正常了,但是Close按钮和Web view的显示还有点问题。
在storyboard中,拖曳Close按钮,使其贴近视图和bottom guide的中心。
![1298a964f09a4dd84006b9626c51ab14.png](https://i-blog.csdnimg.cn/blog_migrate/38c70caa394a54db11a852174d3f5e50.jpeg)
Interface Builder提供了非常方便的蓝色点虚线,可以帮助我们来手动对齐视觉元素。
为了让Close始终位于界面的中间位置(而无论屏幕的宽度是多少),我们需要创建一个centering constraint。
点击Close按钮以选中它,从Align菜单(在Add New Constraints按钮的旁边)中选择Horizontally in Container,然后点击Add 1 Constraint。
![121de6b41e2c19bc022fe98b776db8c6.png](https://i-blog.csdnimg.cn/blog_migrate/694a134db8640ae28a6b242d792ed8ba.jpeg)
此时Interface Builder绘制了一个蓝色的bar来代表constraint,然后在按钮的周围出现了一个红色的方框。
不过这里就出现问题了:红色的框代表这些constraints有些问题,通常是提醒我们constraints还不够。
需要记住的是:对每个视觉元素来说,都需要有足够的constraints来定义其位置和大小。目前Close按钮已经知道自己的大小了-我们在Size inspector中已经指定了。但是对它的位置来说,目前只有X轴上的一个constraint,这还不够,我们需要在Y轴上也添加一个constraint。
如你所见,constraints有几种不同的类型,比如用于对齐的constraints,和spacing constraints,等等。
选中Close按钮,点击Add New Constraints按钮,在Spacing to nearest neighbor部分,将bottom spacing 设置为20,并确保选中对应的I形条。
最后点击Add 1 Constraint以完成设置。
![9279a22fff7f66a4551f433d091af0f6.png](https://i-blog.csdnimg.cn/blog_migrate/32bdc544584d792231c19a3f2e647f2e.jpeg)
现在红色的边框变成了蓝色的,代表一切OK了。
我们可以选择不同的Simulator,来查看最终的效果。
注意:有些时候可能我们看到的不是蓝色的bar,而是橙色的,那就意味着Auto Layout中的constraints设置仍然存在问题:
![ec5a3077830c26a72f8e01c6a9531edf.png](https://i-blog.csdnimg.cn/blog_migrate/3afc1e634185c4a97c51077d533c116f.jpeg)
那么这种情况意味着什么呢?首先所设置的constraints是有效的(否则bar或者边框会变红),但是这个控件元素并非处在界面中的正确问题。橙色的框表示Auto Layout根据开发者所提出的constarints所计算出的控件元素应处的位置。
当出现这种情况的时候,我们应该再次选中Close按钮,然后点击界面底部的Update Frames按钮。
![519e84db000ab05f500d927b3b36092e.png](https://i-blog.csdnimg.cn/blog_migrate/ae1c3d4e065d1579732a92ff76b7d950.jpeg)
当然,你也可以选择使用菜单栏中的Editor->Resolve Auto Layout Issues->Update Frames命令。
好了,问题解决了。
小提示:
如果我们没有在视图中添加任何constraints会怎样?事实上,Xcode将会在编译应用时自动添加constraints。这也是为什么最开始我们什么也没做,依然能够顺利运行应用的原因。
不过遗憾的是,这些默认的constraints往往不能满足我们的要求。比如,默认的constraints统筹无法自动适配视图到不同分辨率的屏幕上。毕竟,Auto Layout还没有引入AI技术~
还有一点要注意的是,一旦我们在视图中添加了一个自定义的constraints, Xcode就不会再为视图自动添加任何constraints了。一旦迈出了第一步,我们就要为视图中所有视觉元素的布局和显示负责了~
接下来我们还要继续设置关于界面。
在storyboard中选择关于界面中的Web view,然后点击Add New Constraints。首先确保取消勾选Constraint to margins。然后点击所有四个I形条,并将spacing都设置为20,除了底部的spacing设置为8:
![1a5f426711e1f287f9cd312c556ba092.png](https://i-blog.csdnimg.cn/blog_migrate/1063519cd8c831eda33480ac633f8dac.jpeg)
完成设置后,点击Add 4 Constraints即可。
此时web view就有了四个constraints:
![3d1f791c105e5fd1fba6695a4c92ccb4.png](https://i-blog.csdnimg.cn/blog_migrate/d8c8e333e22534a5f58e3142651aa84d.jpeg)
其中的三个将web view和主视图的相对位置确定,而下面的那个则是确定和Close按钮之间的相对位置。
有了以上四个constraints,已经足够确定任何场景下web view的大小和位置了~
修复主场景中的其它视觉元素
接下来让我们在storyboard中回到主游戏场景。我们需要将所有的label,button和slider放入一个新的”container”(容器) 视图中。这样我们就可以使用Auto Layout将其始终放置在屏幕的中间。
在Document Outline中选中所有的label,button和slider,如图所示:
![d31c88ff3ede2aee68e5926c785bb486.png](https://i-blog.csdnimg.cn/blog_migrate/2499731a85d884012004880d68408099.jpeg)
从Xcode的菜单栏中选择Editor- Embed In- View,这样就可以将所选的视觉元素放入一个新的container 视图中:
![f879b4b3cf315fcfbfdd2aae2868056c.png](https://i-blog.csdnimg.cn/blog_migrate/30cfc87e690412d0f6cb3e9264bcfd56.jpeg)
这个新的视图是全白的,当然最终我们肯定不希望看到它是这个样子的,不过目前先保持这样,以便我们添加constraints.
选中这个新添加的container view,然后点击Add New Constraints,勾选Width和Height,并使用Interface Builder中所指定的数值。然后点击Add 2 Constraints以完成设置。
![da15e95a61baa5ab6befc638f50d39bf.png](https://i-blog.csdnimg.cn/blog_migrate/c6b2241aba63c1b39af8eeab046a5599.jpeg)
此时Interface Builder会在这个container view的周围绘制几个条,以代表我们刚刚添加的Width和Height constraint,但是目前它们显示的颜色是红色。
Don’t panic!这里的红色只是代表没有足够的constraints。我们接下来将添加所缺失的constraints。
继续选中这个container view,然后点击Add New Constraints旁边的Align图标,勾选Horizontally in Container和Vertically in Container选项,然后点击Add 2 Constraints。
好了,现在所有的Auto Layout 条都会变成蓝色,而视图也会完美的显示在中央位置。
最后,让我们更改container view的Background 色彩为Clear Color(0% opacity,也就是100%透明)。
此时我们可以随意选择任何一种iPhone Simulator进行测试~
小练习:
如果你足够细心的话,会发现当我们在不同分辨率的设备上进行测试时,虽然视觉元素始终处于界面的中间,但是在大一点的屏幕中,视觉元素并没有填满整个区域,这是因为我们将container view设置为某个特定的大小。如果我们希望界面元素可以根据屏幕的实际可用大小更改位置和大小,那么就必须删掉container view,然后为每个视觉元素分别添加所需的autolayout constraints了。
试试看~
消除警报
自动我们在storyboard里面创建了游戏视图以来,其实Xcode就一直给我们一堆⚠️警告信息。之前我们一直没有处理这些警告,但是对处女座来说简直是不能忍的。所以,还是来消除警报吧。
![a7d7d199139e8f7965e77a3207d90c82.png](https://i-blog.csdnimg.cn/blog_migrate/04f44c6ff3e66dcaa277a416109483af.jpeg)
看到这些警告信息,可能大家直接就懵逼了,到底是什么Views还没有constraints呢?给个明确的信息也好啊。
如果你注意观察storyboard里面视图元素列表的View Controller Scene,会看到它的右边有个黄色的指示箭头,点开后就可以看到具体的警告信息了。
![7d9f9169d653b81c5a7c87b5cef62815.png](https://i-blog.csdnimg.cn/blog_migrate/9fc41f745640097a9eb4e784ad28e6eb.jpeg)
![b7c4baef5fe1c53ddfdc5b95522c6179.png](https://i-blog.csdnimg.cn/blog_migrate/d28a4ba7e2ed224003d887ca77014e13.jpeg)
看到这一堆警告信息,是不是有点要崩溃的赶脚。
但是解决方法其实也不难,只不过麻烦点。你需要为每一个视图元素分别设置constraint。这里就不再一一赘述了,大家可以自行尝试,然后参考最终的示例项目。
在iPhone X及以上版本设备上测试
在非刘海屏的模拟器上跑起来已经完全OK了,但是在iPhone X和iPhone XR,iPhone XS MAX显示就又有问题了。
![500a4294fbe42081bad455c046edce83.png](https://i-blog.csdnimg.cn/blog_migrate/e43b5ea8115453958c9232640dd7f015.jpeg)
这又是什么情况呢?
从Xcode9开始,为了应对iPhone X和以上机型的刘海屏,苹果给Auto Layout添加了一个新的机制。考虑到在大多数请下,我们不希望应用在刘海下显示内容,因此Xcode提供了所谓的safe layout guides。如果我们注意看背景图片的Auto Layout constraints,那么可以看到它对齐了safe area。
当然,其实在大多数情况下,这种处理方式是正确的,我们只希望内容在安全区显示。但是对绝地打靶这款小游戏来说,我们希望背景可以盖住整个屏幕。
好吧,那么应该如何解决呢?很简单,只需要让背景图的constraints对齐界面的主视图。
在storyboard的视觉元素树状图中选中背景图,然后切换到Size inspector。可以看到那里显示了4个constraints。
![2e011c73cf933395b31347b891011dcb.png](https://i-blog.csdnimg.cn/blog_migrate/87327db2503956924b0cce688d742be2.jpeg)
双击第一个约束,就可以看到这个constraint的详细信息:
![96b07deefe35103d896307fff523c1ba.png](https://i-blog.csdnimg.cn/blog_migrate/f08fe13f727b96e1d9271dbb7bbef4da.jpeg)
点击First Item的下拉列表,将默认的Safe Area切换到Superview。此外,如果Constant字段的数值不是0,那么需要将其更改为0。
![985dfeb635a1509bebd20e035db36055.png](https://i-blog.csdnimg.cn/blog_migrate/48b5e7839f0994e0c328c423769449db.jpeg)
对其它三个constraints进行类似的设置调整,注意其中两个的Safe Area是Second Item。
全部设置完成后,再在iPhone X/iPhone XR/iPhone XS/iPhone XS Max的模拟器上跑一跑,确保一切正常~
添加Crossfade(淡入淡出动态特效)
回顾我们的清单,接下来我们还需要添加Crossfade动态特效。
为此,我不得不提一下Core Animation。Core Animation是iOS的系统框架之一,使用它可以给应用添加各种酷炫的动画,而且只需要短短几行代码就可以实现。
还是那句话,仅仅让代码跑起来,让应用可以正常运行还不够,我们得从一些微妙的细节出发,让用户体会到使用我们产品或游戏的爽快感觉。
这里将要添加一个简单的淡入淡出效果,也就是当“Start Over”的按钮被触碰后,会使用一个过渡效果让这个过程显得不是那么突兀。
在Xcode中 切换到ViewController.swift,更改startNewGame方法的代码如下:
@IBAction func startNewGame() { score = 0 round = 0 startNewRound() let transition = CATransition() transition.type = CATransitionType.fade transition.duration = 1 transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) view.layer.add(transition, forKey: nil) }
简单的来看,我们创建了一个动画,它的类型是淡入淡出,时间长度是1秒。最终的效果是从当前屏幕上所显示的内容切换到更改后的内容。
再次点击Run运行,可以看看效果。
如果你足够仔细,会发现一些微妙的视觉体验差异~
关于Core Animation的更多知识,感兴趣的童鞋可以深入探索:https://developer.apple.com/documentation/quartzcore
快结束了-添加图标
其实这个东西一点都不新,只要你通过手机上或itunes上的app store下载过东西,就会明白对于一款iOS产品来说,图标是多么的重要!
在Xcode中点击左侧导航栏的Assets.xcassets,然后选择AppIcon
现在里面有若干个空槽,分别代表应用所需的不同类型图标。
![71a3faaae629d8c33e90f7557fd82673.png](https://i-blog.csdnimg.cn/blog_migrate/460832312f82271ac63398e62c64b617.jpeg)
在Finder中打开本教程的Icon文件夹,把Icon-40.png文件拖到iPhone Notification iOS 7-12 20pt的2x槽中。
你可能会问,为什么要把Icon-40.png拖入为20pt准备好的槽中呢?记住这个槽里面的描述是2x,代表着它是位高清设备准备的,而每个Point代表2个像素。
把Icon-60.png文件拖入旁边的3x槽,这是为3x分辨率准备的。3*20是60(这个乘法计算应该没什么难度吧?)
对于其它的几个槽,我们依次类推,这次就不再赘述了。
文件夹中的其它文件是为iPad准备的。虽然我们这个应用是专为iPhone设计的,不过所有的iPad都可以运行为iPhone准备的应用。
注意为iPad准备的图标是1x和2x(而非3x)。
全部完成之后的设置如下图所示:
![9bb01b6807b93ee178641730d7eec90c.png](https://i-blog.csdnimg.cn/blog_migrate/59f58f095115ed93881879d2a51ea707.jpeg)
编译运行应用,然后将其关闭。在Simulator中可以看到我们想看到的图标。
显示游戏名称
图标有了,启动画面有了,我们还需要有一个好的产品名称。
之前我们用了简单的英文名称BullsEye,但我希望在Appstore里面显示的是绝地打靶,或者其它你喜欢的名称,总之和人类的表达习惯更接近。不过需要注意的是,在图标下面的空间非常有限,你的产品名称也不能太长了,否则就无法显示完全。
在xcode中切换到Project Settings界面,然后更改Display Name为绝地打靶即可。
![7394b428e836d0c1f39f14d2e9f33c34.png](https://i-blog.csdnimg.cn/blog_migrate/5d2bd9ab92cffc41bd939bd976f4059e.jpeg)
还记得我们之前介绍Info.plist文件时提过吗,在那里也有一个Bundle display name,现在切换到Info.plist,会发现那里的信息也已经自动修改了。
![b1aa579205e9beff78b3bac22cd742a4.png](https://i-blog.csdnimg.cn/blog_migrate/77178acd8a32017d749c314d3d227b8a.jpeg)
注意:如果没有提供Bundle display name,那么Xcode会自动使用Bundle name字段的信息。这里的默认值是$(PRODUCT_NAME),意味着Xcode会自动将项目名称BullsEye作为产品的名称。但是通过提供Bundle display name,就可以覆盖系统自动设置的产品名称。
好了,现在点击Run来运行,就可以在模拟器上看到图标和更改后的产品名称了。
在结束教程之前,我们还要给这个小游戏加点新东西。
任何一款优秀的游戏都是一款艺术作品,它包含了精美的画面,炫酷的动画,动听的音效。。。且慢,我们这款游戏似乎是默片?你可以接受苍老师的视频教程里面竟然没有声音吗?显然不能,我们这个小游戏也一样,要加点好听的。
首先来个背景音乐怎么样?
在ViewController.swift的文件顶部添加一行代码:
import AVFoundation
这就引入了一个新的框架,所谓的AVFoundation框架,此AV非苍老师的AV,但这个AVFoundation框架的主要作用就是播放音频视频,所以苍老师也用得着。
当然,只是添加了这一行代码还不够,我们还需要将这个framework和项目更紧密的联系在一起。
在Xcode的主界面找到TARGETS下的BullsEye,然后在General视图下找到Linked Frameworks and Libraries,然后添加AVFoundation.framework。
![21aeb9389516ddf31647eb62f308aebb.png](https://i-blog.csdnimg.cn/blog_migrate/e05ecccdff69b7a557bf3bc872c5b0be.jpeg)
接下来在定义其它实例变量的代码下面添加一行代码:
var audioPlayer: AVAudioPlayer!
这就添加了一个AVAudioPlayer类型的播放器变量。
接下来在ViewController.swift的文件中添加一个新的方法:
// play background music func playBgMusic(){ let musicPath = Bundle.main.path(forResource: "bgmusic