如何在c 游戏开发发中添加可手动移动长条代码

Android游戏开发实践之人物移动地图的平滑滚动处理
转载 & & 作者:
玩过rpg游戏的朋友应该都知道RPG的游戏地图一般都比较大 今天我和大家分享一下在RPG游戏中如何来处理超出手机屏幕大小的游戏地图。
如图所示为程序效果动画图
地图滚动的原理
在本人之前博客的文章中介绍过人物在屏幕中的移动方式,因为之前拼的游戏地图是完全填充整个手机屏幕的,所以无需处理地图的平滑滚动。这篇文章我着重的向 大家介绍一下控制人物移动后地图滚动的处理方式。举个例子 如上图所示 比如人物向右移动,如果地图贴在屏幕左边边界 将先移动人物在地图的坐标,当人物在屏幕中超过三分之二后 则将地图向人物行走的反方向移动给玩家一种人物还在向右移动的假象,其实这时候人物只是播放向右行走的动画 在屏幕中的坐标不变 ,当地图向人物行走反方向移动到不能移动的时候 则表示 地图右侧边界已经贴住手机屏幕右侧边界 这样地图则不能在移动 这时候将移动人物在屏幕上的坐标直到人物贴住屏幕右侧边界,其它三个方向原理完全一样。
代码的实现方式
还是以人物向右移动为例,我们须要三个坐标 一个是m_HeroPos 来保存人物在地图中的X坐标 一个是 mScreenPos 来保存人物在屏幕中的显示坐标 mMapPos 来保存地图在手机屏幕中的显示坐标,按键盘右键后人物在地图中的坐标加上8像素(表示行走的步长),当人物的坐标超过屏幕的三分之二的时候需要检测地图是 否已经在边界, 使用地图绘制在屏幕中的X坐标 加上地图的宽度 判断是否等于屏幕的宽度如果等式不成立 则表示地图可以向左放移动 这时候后人物mScreenPos人物显示坐标将不变 mMapPos地图显示坐标将向左移动人物一个步长的位置,如果之前的等式成立 则表示地图无法在向左移动 这时候mScreenPos人物显示坐标将向右移动人物一个步长的位置 直到屏幕右侧边界 mMapPos地图显示坐标不会改变。其三个方向的移动算法完全一样。
如图所示地图在平滑的滚动
拓展学习之卡马特地图缓冲算法
这个游戏demo 还不能称之为完美 在与物理层碰撞中还是存在一点问题 不过 最重要的是 没有使用卡马克地图缓冲算法, 下面我详细介绍一个卡马克地图缓冲算法原理。我们需要一张地图换冲图, 为了实现平滑滚动的效果缓冲图的大小应该是手机屏幕宽高 ,这样内存中就会常驻 一张 手机屏幕大小的缓冲图。 如果人物向下方行走则表示地图向上移动,这时候屏幕 将被切割出来了2部分, 上半部分为缓冲图向上移动后的缓冲内容,下半部分则就是我们需要更新地图的部分,将更新后的下半部分与地图缓冲的上半部分结合起来绘制在屏幕中则完成了一 次地图更新,这样可以大大提升游戏绘制地图的效率。如果不使用这种方式来更新地图 则地图每移动一次 都须要双for循环重新一个tile一个tile的绘制一遍效率会很低下。
如图所示 地图向上移动后更新绿色的地图部分 与 将上次缓冲的红色部分结合起来就是新的游戏地图,其它方向原理完全一样。
如图所示:如果游戏中可以按右下方式来移动主角 那么以屏幕右下角为圆心 地图就会被切割成4个部分下图中红色区域须要更新其中 3 个部分 地图左边超出的部分 地图下面超出的部分 地图左下方超出的部分,更新的原理和上面一样。
这么看来卡马克不愧是个游戏领域的天才,马克地图缓冲算法不愧是最最优秀的地图算法之一,大家如有有兴趣可以去查一查关于卡马特的资料,他确实是一个顶尖的游戏开发工程师。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具告别手写代码,HTML5可视化游戏开发工具震撼发布 - 轻微生活『玄学之夜』强档节目,每周六晚9:00定期开聊游戏圈轶事,『雅文』和业内大神探讨分析纯粹的游戏。参与群语音请加群:8198073,录播合集猛戳
本周推荐()
本文是关于iOS 8应用预览视频的话题,从设计、技术规范,到录屏、编辑工具,介绍的都比较详尽;建议收藏,在接下来用的到的时候作以参考。下面进入译文。
最近这段时间,苹果的世界里出现了很多新东西,比如屏幕更大的iPhone 6,可穿戴设备Apple Watch,iOS8,以及旨在帮助用户更好的发现应用的App Store改版等等。
说到App Store的改版,最值得设计师、开发者和市场人员关注的大概就是视频预览功能了。官方将其称为“应用预览(App Previews)”,如今已经正式出现在iOS8的App Store当中。自然,已经有一大波设计师和开发者为他们的产品制作了预览视频并通过iTunes Connect上传。坦率的说,如果你也有自己的产品,那么也该开始考虑做这件事了。来自官方的设计制作规范与截屏图片一样,苹果针对应用预览视频提供了一些。视频在上传之后同样需要接受苹果的审核,这就意味着如果你在预览视频方面破坏了规则,一样会被拒掉,所以建议你首先仔细阅读一下官方的规范。技术规范当前,我们可以为每个应用上传两段预览视频,其中一段用于iPhone,一段用于iPad。
从技术角度讲,你需要遵守以下这些规范:
长度在15至30秒之间
通过H.264 MPEG或ProRess 422(HQ)压缩
每秒30帧(30p)
最终文件不超过100MB
扩展名可以是.mov、.mp4或.m4v
分辨率规格如下(单位为像素):
此外,上传视频需要基于以下平台及软件:
安装了OS X Yosemite的iMac或MacBook
Safari浏览器
设计规范规范当中的一些内容不大容易解释,因为它们更像是“建议”,而非“规则”。不过要记住,苹果官方会将这些“建议”作为评估的基础;遵守这些规范将能帮你避免遇到那些不必要的麻烦。
设计规范内容较多,我们不妨先来看下概要:
聚焦在最重要的三个卖点上
通过录屏演示产品体验
演示操作时不要让手挡住界面
不要通过编辑视频来造假
内容至少要适合4岁以上的人群
不要使用你不拥有版权的素材
不要依赖于文字介绍
瞄准目标用户群体及其语种
视频中不要涉及价格及运营商信息
不要使用有可能“过期”的内容或素材
从应用中录制实际音效
使用高品质的背景音乐及画外音
选择一张合适的封面
聚焦在最重要的三个卖点上当然,你不必严格按照字面上的意思强行挑出三个卖点进行演示;重点在于不要试图在30秒的视频中面面俱到,因为用户根本没法理解和记住那么多东西。
另一方面,虽然苹果允许你直接使用30秒的实际操作视频,但也不要将这30秒全部用来演示某一个功能流程,因为这会很无聊,而且可能使用户错过其他一些重要的东西。
用户通常会因为对产品当中一到三个卖点的关注而下载你的应用。卖点在设计上?或是某种新技术?还是对传统界面模式的优化改良?挑出那些最吸引人的亮点,在一段简单的蒙太奇当中展示出来;要做好这件事,你真的不需要拥有多么专业的视频制作能力。
此外,你并不一定需要去展示产品logo,应用图标,或是“Download on the App Store”标识。如果真的有必要,也尽量试着做出一些风格,例如通过某种形式的动画来展示logo和图标,给用户留下更积极的印象,强化品牌识别效应。甚至有公司是专门提供这方面服务的。通过录屏演示产品体验苹果真心不希望演示视频看上去像赤裸裸的广告。他们希望让你通过应用自身来更加“诚实”的进行展示。使用实际应用录屏,而不是展示人们拿着手机操作的样子,或是你从手机主屏一路点击进入应用的过程。
对于游戏来说,有取巧的可能,例如只放一些过场动画。苹果同样知道这一点,他们建议你还是尽量放些游戏进行过程中的录屏。演示操作时不要让手挡住界面听上去很容易?但对于开发者来说,和苹果打交道就没有什么容易的事。演示视频中的应用界面不要被操作者的手指挡住,尽量通过前面说的录屏来演示;如果确实要展示交互方式,可以在录屏中加入视觉提示元素,例如通过逐渐淡出的圆环代表触摸点。不要通过编辑视频来造假这是很严肃的事。不要通过欺骗的手段让人们觉得你的应用比实际的好。除了道德因素以外,这种手段更会为你的产品带来相当负面的评价,一星就是用户用来发泄愤慨的手段。
还要记得按照应用的实际分辨率来显示内容,不要放大。此外,不要把转场效果剪切的让用户误以为实际应用当中有这样的效果。内容至少要适合4岁以上的人群任何人都可以访问App Store。我曾经见过3岁的小孩在iPad上通过各种复杂的手势操作应用,熟练的仿佛他在他妈的肚子里就已经开始使用这设备了一样。一旦了解有这种事情存在,你便知道为什么苹果会担心内容的适用人群了。
引用官方的话讲,你应该避免“令人反感的内容,暴力或成人主题,以及脏话。”不要使用你不拥有版权的素材我猜这很容易理解。无论是在App Store还是其他地方,尊重版权和商标都是必须的。不要在视频中使用你不拥有版权的音乐、视频、商标、形象、人物形象等等。因为这方面的问题被App Store拒掉可是很蠢的。
苹果官方举了个例子:“如果你的应用可以访问iTunes Library,那么你只能在预览视频中使用自己创作的或是拥有特定授权的音乐。”不要依赖于文字介绍至少目前,应用预览视频还不支持本地化,同一个视频会被全球的用户访问到。如果必须阅读视频中的文字才能了解产品功能,那么你就把全世界非英语用户挡在了门外。
如果一定要使用文字介绍,也要注意几点。世界上说英语的人不少。除非你的产品锁定在某个特定的国家或语种上,否则还是用英语的好。保持介绍文字的简短、易理解;因为文字是出现视频当中的,所以还要注意可读性,并确保其出现的时间足够长。瞄准目标用户群体及其语种也许你的产品是一款游戏,在全球都有玩家,但最大的玩家群体在中国,而且其中多数是女性。如果这是你要瞄准的目标用户群,那么要对他们所习惯的广告形式及喜爱的功能进行研究,然后制作一段最能为中国女性玩家所接受的预览视频。视频中不要涉及价格及运营商信息苹果希望你通过功能,而不是价格,来吸引用户下载应用。坦诚的说,在如今的App Store当中,单纯的免费或低价策略未必能带来多少关注,因为90%的下载都是集中在免费应用上的。况且,用户在你应用图标的下方就能直接看到价格信息,没必要在视频中累述。
你唯一有可能需要在视频中展示价格信息的地方就是关于应用内购买的描述;可以试着在视频中的免责声明部分或是结尾展示这些信息。此外,苹果建议对于包含订阅或账户登录功能的情况也要进行类似的处理。
如果不明确的展示这类信息又会怎样?无非是产品有可能被苹果拒掉。即使通过了审核,也很可能导致用户的差评。这些结果显然都不是你希望看到的。
不过,如果你家应用里有圣诞大促的话又当如何展示呢?其实多数用户不会因为你家宝贝有五折优惠就下载应用。说到这一点...不要使用有可能“过期”的内容或素材制作优秀的预览视频是要花不少时间(和钱)的。那么,为什么要给它加上有效期呢?每个人都喜爱圣诞节,但仅限于12月份。如果某些用户在转年2月发现了你的应用,看到了一段圣诞主题的视频,那会有点荒唐吧?不仅如此,这会使用户认为你已经好几个月没有更新产品了。
除非你确定自己会在有特定意义的日期之后很快更新应用及视频,否则不要使用任何有可能过期的内容或素材。当然,如果你的应用本身就是关于圣诞或其他节日主题的,那例外。从应用中录制实际音效正如前面所说,要避免在视频中通过手指操作来演示产品的交互特性,而要使用录屏。不过仅凭视觉上的呈现,也许会略显单薄。因此,操作过程中的实际音效对于演示交互反馈特性就变得更加重要了。使用高品质的背景音乐及画外音虽说不是必需,但要找到一个合适的人来帮你做些简短的画外音解说,这也不是很困难的事。不过和前面提到的文字介绍性质相同,不要过分依赖于解说。当然有一点需要承认,如果做的够好,那么画外音可以给用户带来非常不错的第一印象。
你也可以自己做这件事,不过要在能够尽量隔绝噪音的地方使用高品质设备进行录制,而不是那种5美元的小麦克风。除非你在录音方面的确内行,否则还是建议你自己写好脚本然后交由外包。记得不要把画外音的风格搞得太像电视广告。
无论是否使用画外音进行解说,背景音乐总是你需要考虑的。如果你有原创的或是经过授权的歌曲,不妨一用。使用音乐配合视频,可以为产品奠定一种基调,让用户在下载前就对大致的体验风格有所感知。对于解说和音乐,要进行充分的效果测试,确保音质如水晶般清澈。
如果你不拥有原创音乐,那么在选择授权音乐的时候一定要考虑到风格是否适宜自己的产品。毕竟,哪怕拥有版权,System of a Down乐队的“B.Y.O.B.”也无法适用于小清新风格的购物应用。选择一张合适的封面前文都是聚焦在视频本身上面,我们还忘记了一个小细节:视频不会自动播放,用户需要点击封面才可以观看。所以我们要确保他们有愿望去点击封面才是。
我们可以把这张图片称作封面,也可以叫做海报。它应当来自于视频当中,看上去就像带有一个播放按钮的截屏图片。
上传视频时,iTunes Connect会从视频里自动挑选一帧作为默认的封面,但你也可以自主选择。别忘了做这件事,它对于转化率的提升将起到重要的作用。如果你真的忘记了,而应用已经通过了审核,那么要做好准备重新上传一个新包,哪怕只是为了更换一张封面。制作预览视频看过上面所有这些技术与设计规范,是时候动手制作自己的预览视频了。
关于制作方式,苹果官方有做推荐,同时你也有很多其他选择。接下来,我会向各位展示我所探索到的所有的可行方法。无论是个人开发者,还是公司团体,都该试着为自己的产品制作预览视频。继续往下读吧,看看哪种制作方式最符合你们在时间及预算方面的具体情况。预算较低独立开发者和小工作室不该错过这场派对。你可能需要花些时间来学习相关的制作软件,但最终仍然可以凭借自己的力量制作出优秀的应用预览视频。使用苹果自家的软件录制屏幕这大概是制作录屏的最简单的方法了。OS X Yosemite预置了一项全新的设备录制功能,目的就是帮助广大设计师和开发者制作应用预览视频。此外,你需要带有Lighting接口和Retina屏的iOS设备,系统是iOS 8;iPhone 5及之后的iPhone都可以满足条件。
下面是来自苹果官方的操作指南:
使用Lighting连接线将你的iOS设备与Mac连接起来。
在Mac上打开QuickTime播放器。
选择“文件&新建屏幕录制“。
在接下来出现的窗口中,选择你的iOS设备作为摄像头及麦克风输入源。
然后就可以开始录制了。使用第三方软件录制屏幕另外一个比较简单易行的选择就是。这款新软件也是专门针对应用预览视频的制作需求的,同样要求iOS 8硬件设备以及运行着Yosemite的Mac,当然,还有Lighting连接线。此外,这款软件自带一个简单的视频编辑工具,所以除了像使用Quicktime一样录制屏幕以外,你不需要再使用其它软件来编辑视频了。目前TechSmith AppShow还处于Beta测试阶段,你可以免费参加测试。
当然,你也可以使用市面上其它一些比较成熟的录屏软件。从前,它们的主要用途是从其它设备上录制视频,并在电视或网页上进行播放。
为什么要用这类工具?的团队告诉我们,通过AirPlay投射的屏幕视频质量更高,因为“通过USB连接的方式录制的视频帧数较低”。TechSmish和Apptamin也对此进行了验证,通过AirPlay投到Yosemite上的视频可以录制出更好的效果,包括iPhone 6和Plus都是这样。
那么这些第三方软件的工作原理是什么呢?他们本身相当于AirPlay接收器,可以将iOS设备的屏幕直接投到PC、Mac或Android上进行录制。确保这些设备在同一网络中,然后在你的iPhone或iPad中打开控制面板,激活AirPlay即可。
下面是一些比较有代表性的第三方软件:
在Mac上直接录制iOS模拟器的屏幕如果以上方法对你都不适用,还有一招:直接录制Mac的屏幕。不过同故宫这种方式录制的视频质量不是最佳的,帧数不够稳定,你可能需要通过后期编辑来改善。
具体方法就是在Xcode上加载并运行你的应用,然后通过下列软件录制iOS模拟器中的视频:
编辑录屏视频完成屏幕录制之后,你还需要进行编辑,剪掉没用的东西,把精华浓缩到30秒当中。在导出方面,要记得参考前文给到的技术规范。
你需要一些像样的工具来进行编辑工作,下面这些可供参考:
如果你使用Final Cut Pro,可以看看苹果官方的教学视频。更多视频编辑工具可以参见。
其中一些软件,例如Camtasia,会内置一些编辑工具套装。如果使用这些软件,你需要留意一下这些工具在导出方面是否符合前文给到的技术规范当中的要求。预算较高如果钱不是问题,而时间和质量是你们最关注的,那么最好的方式就是外包。下面这些公司可以提供一站式服务,包括屏幕录制、视频编辑、解说等等,让你高枕无忧:
小结预览视频显然比截屏要难制作的多,但成本越高,所能带来的机遇也就越多。制作精良的预览视频可以成为一款优秀应用的又一个标准之一,用户也会越来越多的留意那些提供了预览视频的产品。
对于那些纠结于转化率的提升,或是渴望得到更多机会展示功能的产品来说,预览视频将成为好帮手。游戏类的产品很容易通过截屏来吸引用户的目光,因为它们本身就很炫酷;而对于生产力和效率类的应用来说,要做到吸引人就不那么容易了。花些心思打造一段预览视频将能帮你更好的展示产品。
接下来会有越来越多的设计师和开发者投入到预览视频的制作工作当中,千万别落后。当然,其他方面例如应用图标和静态截屏的优化也是要考虑到的,这些都是提升下载量的重要手段
官方 raywenderlich.com Swift 编程风格指南
本文版权归 raywenderlich.com 、项目以及所有贡献者所有。译者翻译仅供知识传播使用。
本风格指南的目标是让Swift代码更简洁、可读更强。语言推荐使用跟苹果API文档风格统一的英语。
推荐:var color = "red"
不推荐:var colour = "red"
使用2个空白符作为缩进,不要使用Tab制表符,请务必在Xcode中进行设置;
方法定义时的大括号或其它大括号(if/else/switch/while等)中的左括号写在当前语句的结尾,右括号需要另起一行。
推荐:if user.isHappy {
//Do something
//Do something else
if user.isHappy
//Do something
//Do something else
方法定义之间需要插入一个空行,让代码在视觉上更清晰,也便于管理。方法内部也需要使用空行来分隔不同功能的代码块,但如果一个方法中有太多代码块,你最好将它们拆分为多个方法(重构)。
注释只有在必要的时候才写注释来解释某段代码为什么那么做。注释必须跟代码同步更新,该删掉时就删掉。
尽量避免使用多行注释,而是让代码本身去解释自己的功能。命名使用可描述性的驼峰式命名法为类、函数、方法、变量等命名。模块中的类名和常量名首字母需要大写,而方法名和变量名的首字母需要小写。
推荐:let MaximumWidgetCount = 100
class WidgetContainer {
var widgetButton: UIButton
let widgetHeightPercentage = 0.85
不推荐:let MAX_WIDGET_COUNT = 100
class app_widgetContainer {
var wBut: UIButton
let wHeightPct = 0.85
对于函数和构造器(init),除非能一目了然,否则建议为每个参数命名。如果能让函数可读性更强,请提供每个参数的外部参数名。func dateFromString(dateString: NSString) -& NSDate
func convertPointAt(#column: Int, #row: Int) -& CGPoint
func timedAction(#delay: NSTimeInterval, perform action: SKAction) -& SKAction!
// would be called like this:
dateFromString("")
convertPointAt(column: 42, row: 13)
timedAction(delay: 1.0, perform: someOtherAction)
对于类中的方法,请遵循苹果惯例,将方法名作为第一个参数的外部名:class Guideline {
func combineWithString(incoming: String, options: Dictionary?) { ... }
func upvoteBy(amount: Int) { ... }
如果在非代码文本(包括教程、书籍以及注释中)中引用了某个函数,请提供函数所有参数的外部参数名:The dateFromString() function is great.
Call convertPointAt(column:, row:) from your init() method.
The return value of timedAction(delay:, perform:) may be nil.
Guideline objects only have two methods: combineWithString(options:) and upvoteBy()
You shouldn't call the data source method tableView(cellForRowAtIndexPath:) directly.
类前缀Swift中的模块(module)包含了命名空间,所有在模块中定义的类型名都不会与其它模块冲突。所以我们不再需要使用前缀命名来减少命名冲突。如果两个来自不同模块的相同名字需要同时引用,你可以使用模块名+点+类型名的方式来处理:import MyModule
var myClass = MyModule.MyClass()
再次重申,请不要在Swift类型命名时加前缀。
如果你需要将Swift类型暴露给Objective-C,你可以为其指定一个在Objective-C中使用的合适前缀(请参考:@objc (RWTChicken) class Chicken {
分号Swift不需要在每条语句后加分号。但如果将多条语句写在一行代码中,这时需要加上分号。 然而我们并不推荐这种将多行语句写在一行的做法。 唯一例外是for-conditional-increment结构,它必须使用分号。但你应该尽量使用for-in结构来替代这种行为: 推荐:var swift = "not a scripting language"
不推荐:var swift = "not a scripting language";
注意: Swift跟Javascript有很大区别,Javascript中如果忽略分号会被认为。类和结构体请将类和结构体中的代码按以下顺序进行组织:
变量和常量属性
class Circle: Shape {
var x: Int, y: Int
var radius: Double
var diameter: Double {
return radius * 2
radius = newValue / 2
init(x: Int, y: Int, radius: Double) {
self.x = x
self.y = y
self.radius = radius
convenience init(x: Int, y: Int, diameter: Double) {
self.init(x: x, y: y, radius: diameter / 2)
func describe() -& String {
return "I am a circle at (\(x),\(y)) with an area of \(computeArea())"
func computeArea() -& Double {
return M_PI * radius * radius
上面的例子还展示了以下风格:
定义属性、变量、常量、参数以及其他语句时,在其后面加上空格,而不是在前面加。比如说,x: Int 和 Circle: Shape;
属性的getter、setter以及属性观察器willSet和didSet的实现都需要缩进;
如果多个变量和结构体有相同的使用目的或使用环境,可以将它们定义在同一行代码中。
Self 的使用请避免在Swift中使用self,因为我们不需要使用self来访问一个对象的属性或调用它的方法。
唯一需要使用的场景是在类或结构体的构造器中。你可以使用self来区分传入的参数和类/结构体的属性:class BoardLocation {
let row: Int, column: Int
init(row: Int,column: Int) {
self.row = row
self.column = column
函数定义尽量将较短的函数名定义在一行,并以一个左大括号结尾:func reticulateSplines(spline: Double[]) -& Bool {
// reticulate code goes here
如果函数名较长,请在适当的时候换行,并对下一行函数名进行缩进:func reticulateSplines(spline: Double[], adjustmentFactor: Double,
translateConstant: Int, comment: String) -& Bool {
// reticulate code goes here
闭包请尽量使用掉尾(就是将最后一个闭包参数直接附在方法调用后,看起来像是控制语句的body一样)闭包语法。无论何时,请给闭包中每个参数一个描述性的名字:return SKAction.customActionWithDuration(effect.duration) { node, elapsedTime in
// more code goes here
笔者: 提出将闭包参数另起一行的写法,我认为更合理,解决了多个闭包参数过长的问题。对于一行表达式闭包,当使用场景明确时,可以使用隐式返回:attendeeList.sort { a, b in
类型请尽量使用Swift提供的原生类型。Swift也提供了原生类型对象桥接到Objective-C对象的办法,所以必要时你能随意使用这些桥接对象提供的方法。
推荐:let width = 120.0
let widthString = width.bridgeToObjectiveC().stringValue
不推荐:let width: NSNumber = 120.0
//NSNumber
let widthString: NSString = width.stringValue
//NSString
在Sprite Kit代码中,请多使用CGFloat,这样代码会更简洁,也能避免非常多的类型转换。常量常量使用let关键字定义,而变量使用var关键字定义。
如果一个值是常量,那就必须使用let关键字来准确定义。最终你会发现,你使用let的频率远大于var。小技巧:一开始你可以将所有值都定义为常量,然后如果编译器报错了再作适当的调整。可选如果可以接受nil值,请将变量或函数返回值的类型定义为可选类型(加?)。
当你明确知道实例变量在使用前会完成初始化,比如说视图控制器中的子视图subviews在使用前会在viewDidLoad中初始化,那么你可以将这些变量定义为隐式解析类型(使用!)。
当访问一个可选值时,如果只访问一次,或者方式时有多种可能性,请使用可选链:myOptional?.anotherOne?.optionalView?.setNeedsDisplay()
使用可选绑定optional binding对只拆包(unwrap)一次,但执行多次操作的情况非常合适:if let view = self.optionalView {
// do many things with view
类型推断Swift编译器可以推断出变量和常量的类型。你可以为每个常量或变量提供一个显示的类型(加个冒号,并在后面写上类型名),但大部分情况不必这么做。
我们建议多使用类型推断让编译器自动推断出常量或变量的类型,这样代码会更紧凑。
推荐:let message = "Click the button"
var currentBounds = computeViewBounds()
不推荐:let message: String = "Click the button"
var currentBounds: CGRect = computeViewBounds()
注意:遵循此规则意味着在命名时,需要更慎重的选择具有描述性的名字。流程控制建议多使用for-in风格的for循环,而不是传统的for-condition-increment风格。
推荐:for _ in 0..5 {
println("Hello five times")
for person in attendeeList {
// do something
不推荐:for var i = 0; i & 5; i++ {
println("Hello five times")
for var i = 0; i & attendeeList. i++ {
let person = attendeeList[i]
// do something
笑脸笑脸在raywenderlich.com网站中是一个非常突出的特性。正确的使用笑脸来表达在编程时的一种极大的快乐和兴奋,至关重要。我们使用右方括号],因为它代表了ASCII艺术字符中最大的微笑;而使用右小括号)的笑脸显得有点不那么诚心,所以我们不推荐使用。
不推荐::)原文转自:https://swiftist.org/topics/165#20
本文来至,原文作者:董可人
我对“冰山算法”刚好有一些了解,可以给大家讲讲。很多人对“量化交易”的理解实在太过片面,基本上把它等同于生钱工具,我不赞同这种观点。交易首先是交易本身,有它自身的经济学意义,忽略这一点而单纯把它看成使钱增值的工具,很容易就会迷失本心。
我也不认为算法本身有什么稀奇,再好的算法也是死的,真正的核心价值一定是掌握和使用算法的人。实际上我讲的东西也都是公开的信息,但是即便了解了技术细节,能真正做好的人也寥寥无几。
希望这个回答可以让你对量化和高频交易有一个更清醒的认识。
~~~~~~~~~~~~~~~~~~
首先我相信不少人概念里的高频交易还是这个样子的:
但对高频交易来说,这种信息是非常粗糙的。所以这里先要对不熟悉背景的同学介绍一下什么叫做Order Book。现在主流的交易所一般都使用Order Book进行交易,交易所在内部的Order Book上记录所有买家和卖家的报价,比如像这样:
Bid表示买家,Offer表示卖家,这张报价单表示买卖双方发出的所有报价单(Limit Order)。这张表才是高频交易最关心的信息。任意时刻,买家的出价总是低于卖家(比如这里的98对101)。所以报价虽然一直在变化,但是只有报价是不会有任何成交的。
什么时候会产生交易呢?有两种情况,第一是任一方发出市价单(Market Order),比如一个买家发出一张单量为10的市价单,就可以买到卖方在101价格上挂的10份,这个交易成功之后,Order Book会变成这样:
第二是发出一个价格等于对方最优报价的限价单,也会产生和上述情况相同的结果。
需要强调的是,虽然真正的Order Book只存在于交易所内部,所有交易都在交易所内完成,但是交易所会把每笔报价和市价单都转发给所有人,所以所有的买家和卖家都可以自己维护一个同样的数据结构,相当于交易所Order Book的镜像。通过跟踪分析自己手里这份的镜像变化,来制定交易策略,是高频交易算法的核心思想。
~~~~~~~~~~~~~~~~~~
基础知识介绍完毕,下面为了方便大家理解,我采用一种更形象的方式来表示Order Book:
这张图对应文章开始时的那个Order Book,应该可以明白地看出,横轴表示价格,纵轴表示订单量,绿色表示买家,红四表示卖家。这样做的目的是为了引出本篇讨论的主题:冰山订单。
通过上述基本分析大家可以看出,交易所内的交易数据是完全公开的,市场上任意时刻,有谁想要买/卖多少,所有人一目了然,没有任何秘密。这样做本身是有经济学意义的,因为只有展示出买卖的需求,才会吸引潜在的商家来交易,所以在市场上一定程度的公开自己的需求是必要的。但这样同时带来一个严重的后果,一旦有某个人想要大量买/卖,他所发出的巨额限价单会直接展示给所有人。比如一个买家挂出巨额买单后,Order Book会像这样:
这对他非常不利,因为所有人都会利用这个信息来跟他做对。大家会判断,现在市场上存在大量的买压,于是会出现一大批为了赚钱而冲进来的人抢购,价格会快速上升,这样原来这个人可以在98这个价位买到的东西,很快就变得要在更高的价位上才能买到。这种情况,那些后来的人做的就是,而原来的那个人则面对。
为了解决这个问题,交易所提供了一种针对性的工具,就是所谓的冰山订单(Iceberg Order)。这种订单可以很大,但只有一小部分是公开出来的,大部分则隐藏起来,除了交易所和发单者本人谁也看不到,真的像一座“冰山”一样。比如像这样:
灰色的部分就是冰山订单隐藏的部分。这样,只有当有对应隐藏量的交易发生时,交易所才会通知其他人,就避免了别人利用显示订单的信息来做。
凡事有一利必有一弊。冰山订单虽然保护了发单者的利益,但是对于其他市场参与者来说却又变成了一种不公平的规则。那些有真实的交易需求的参与者,会因为对局势的误判而损失惨重。所以接下来的问题就变成,如何发现市场上的冰山订单?
首先有一种最简单的方法。有的时候,冰山订单是挂在最优买价和卖价之间(spread),像这样:
对于这种情况,有一个非常简单的探测方法,即发一个最小额度的限价单在spread里,紧跟着取消这个订单。比如这个例子中,发出一个卖价为99的限价单然后取消。因为这个价格本身对不上显式的买价(98),如果没有冰山单的存在,一定不会成交。但有冰山单的情况下,一旦交易所收到这个卖单,会立刻成交冰山单中对应的量,而之后的取消指令就无效了。这样,以一种微小的成本,就可以发现市场中隐藏着的订单。事实上,的确有人会做这种事情,频繁的发单然后取消,在最优价差之间形成一种高频扰动,用来探测隐藏单。
为了应对这种扰动探测,大家一般都不会直接挂单在spread里。而是会像之前那样和普通的限价单挂在一起,这样发生交易之后,你就很难推测消耗掉的究竟是正常的限价单,还是冰山订单。那么应该怎么做呢?
首先有一个直接的思路。冰山订单的存在,一定程度上反映了挂单人对市场情况的解读,认为有必要使用冰山订单而做出的判断。需要强调的是,使用冰山订单并不是没有代价的,因为你隐藏了真实的需求,在屏蔽掉潜在的攻击者的同时,也屏蔽掉了真正的交易者!而且会使得成交时间显著增加--因为没人知道你想买/卖这么多,你只能慢慢等待对手盘的出现。所以当有人下决定发出冰山订单的时候,也会有对市场情况的考虑,只有合适的时机才会做这种选择。
什么是合适的时机?有一些数据应该是相关的,比如买卖价差spread,买单量对卖单量的比值等。对这些数据,你可以在历史数据上做回归分析,建立起他们和冰山订单之间的线性/非线性模型。通过历史数据训练出来的这个模型,就可以作为你在实时交易时使用的冰山订单探测器。这是
这篇论文使用的方法。
基本模型可以定义为:F(spread,bidSize/offerSize,……) = Probability(Iceberg)
如果你想玩高深的,还可以在此基础上做HMM,SVM,神经网络之类的高级模型,但基本思路是一致的:通过盘口分析计算存在冰山订单的概率。
~~~~~~~~~~~~~~~~~~
上面说的这个方法,看起来很高级,实际效果如何呢?我想大家也看出来了,这种建模不是很精确。作为事后分析手段用来说明什么情况下可能会出现冰山订单还不错,但是作为实时交易的探测器就不是很放心。因为使用的信息太模糊了,而且说到底建模的对象只是一种相关性,没有什么保证冰山订单的发送者一定是按照这个逻辑出牌的。
所以接下来介绍的,才是真正具有高频玩家神采的方法,来自
这篇论文。
~~~~~~~~~~~~~~~~~~
高频世界里,有一条永恒的建模准则值得铭记:先看数据再建模。如果你看了上面的介绍就开始天马行空的思考数学模型,那基本上是死路一条。我见过很多年轻人,特别有热情,一上来就开始做数学定义,然后推导偏微分方程,数学公式写满一摞纸,最后一接触数据才发现模型根本行不通,这是非常遗憾的。
而看了数据的人会怎么样呢?他很可能会发现,对于冰山订单的处理,交易所的规则是非常值得寻味的。有的交易所是这样做的:一个冰山订单包含两个参数,V表示订单总量,p表示公开显示的量。比如V=100,p=10的冰山单,实际上隐藏的量是90。如果有针对这个订单的交易发生,比如交易量20,交易所会顺序发出三条信息:
Order Book的Top bid size -10
这三条信息一定会连续出现,并且第三条和第一条的时差dt很小。
因此,一旦在数据中观察到这个规律,我们就可以非常有把握的判定市场中存在冰山订单,并且连p的值都可以确定!接下来的关键问题是,如何确定V的值,即判断这个冰山订单的剩余存量有多少?
这个问题从本质上说没法精确求解,因为V和p都是由下单人自己决定的,可以是任意值。但可以从两点考虑:第一,两个值都是整数;第二,人类不是完美的随机数生成器,下决定会遵循一定规律。
从这两点出发,可以对V和p建立概率模型,即计算一个给定的(V,p)值组合出现的概率是多少?这里不去深入探讨数学分析,感兴趣的朋友可以自己去看原文。简单说,可以在历史数据上通过kernel estimation技术来估算他们的概率密度函数的形状。顺带一提,如果你亲手编写过这种估算程序,就会理解我为什么一名优秀的 Quant 需要什么样的编程水平?这个答案中如此强调编程的重要性。一个可能的概率密度函数的样子是这样的:
这样,当你在实时数据中观测到一个p的值时,就可以得出对应的V值的条件概率密度函数,即上图的一个切面,比如(p = 8):
接下来显然就很容易计算V最可能是什么值了。
综上,算法的核心在于,通过在实时数据中监测短时间内连续出现的三条相关记录判断冰山订单的存在,而对冰山订单的量化则通过由历史数据训练出的概率模型来完成。
相信你也会看出,这种算法并不是什么作弊神器。它只是利用市场上的公开数据所做的一种推测。而且这个推测也仅仅是基于概率的,更多的应该是作为一种参考。它对做市商这种流动性提供者很有意义,可以使他们避免因为对局势的误判而遭受损失。但如果你想用它来作为一种攻击手段,觉得自己能发现隐藏大单而去Front run,那实在是很不明智的选择。
最后,这种算法也只是针对特定的交易所。其他的交易所也许不会采用同样的冰山订单处理方式。所以真正有价值的是这种从实际数据出发的建模思路,具体的算法并不值钱。
~~~~~~~~~~~~~~~~~~
这个小算法给你展示了高频交易领域的“冰山一角”。它看起来也许不算很复杂,但是我却很喜欢。因为它清晰地展示了什么叫做先有思路,再有量化。因为有“冰山订单”这样一个从经济学基本的供需关系出发的真实需求,通过分析实际数据找到一丝线索,最后通过数学模型刻画出定量的策略,这才是漂亮的策略研发。
如果违背这个原则,一上来就去搬各种高级的模型去套数据,指望模型自动给你生成交易信号,这在我看来无异于痴人说梦。遗憾的是,这个梦的确太过诱人,而这个世界也从来不缺少莽夫。
且行且珍惜。
原文出自:,由@DevTalking翻译!
本文紧接的内容,继续介绍如何从头开始创建一个自适应布局的天气应用程序的用户界面!给TextContainer中添加内容打开 Main.storyboard ,从组件库(Object Library)中拖拽两个 Label 组件到TextContainer中,位置可以随意摆放:
先选择靠上的Label,然后点击底部的 Align 按钮,添加一个 Horizontal Center in Container约束,再点击 Pin 按钮,添加一个 Top Spacing to nearest neighbor 约束,设置其值为10:
然后选择右侧工具栏中的 Attribute Inspector 页签,将该Label的 Text 属性设置为 Cupertino,Color 属性设置为 White ,Font 属性设置为 Helvetica Neue, Thin , Size 属性设置为 150。
这时同学们可能会发现基本看不到刚才设置的文字的全貌,这是因为Label大小的原因。别着急,我们很快就会解决这个问题。
现在选择另一个Label,按照上述的方法给它也添加一个 Horizontal Center in Container 约束以及一个 Bottom Spacing to nearest neighbor 约束,将其值设置为10。打开右侧工具栏中的 Size Inspector 看看:
然后选择 Attribute Inspector 将该Label的 Text 属性设置为 28C , Color 属性设置为 White, 将 Font 属性设置为 Helvetica Neue, Thin, 将 Size 属性设置为 250。
现在是时候解决Label大小的问题了。选中view controller的view,点击底部的 Resolve Auto Layout Issues 按钮,在弹出菜单中选择 All Views\Update Frames ,现在看看storyboard中发生了什么:
我们看到了刚才设置的城市和温度,但是他们有一点点重叠,这可不是我们想要的结果。在我们修改这个问题之前,先看看预览编辑区的显示情况。我们发现在iPad下显示貌似还挺完美:
但是在iPhone下不出所料的无法直视,字体太大了:
接下来让我们解决这个重叠和字体大小的问题。Size Classes通用的storyboard文件固然很好,但是你想真正把它玩转还是得花功夫去研究它,这是一件很有挑战性的工作,当然我们也要懂得运用一些现有的工具来帮助我们。Xcode6就为我们提供了一些工具和技巧,帮助我们更好的实现自适应布局。
自适应布局有一个很重要的概念就是 Size Classes。它并不代表真正的尺寸,而是我们从感官上感觉尺寸的种类,通过这种种类的组合,表示出不同屏幕尺寸设备的横屏及竖屏。
Xcode6为我们提供了两个种类:普通(Regular)和紧凑(Compact)。虽然它们涉及到视图的物理尺寸,但一般它们只代表视图的语义尺寸,即不是真正的尺寸,而是我们从感官上分出的尺寸种类。
下面这个表格向同学们展示了size classes和各个尺寸设备竖屏、横屏之间的关系:
上表中的这些size classes组合都是我们在开发应用中经常碰到的。然而你也可以在视图的任何一个层级中覆盖这些size classes。当以后我们开发Apple Watch应用时显得尤其有用。Size Classes与开发者何为设计应用的UI?虽然现在你们的应用已经知道要使用Size Classes,并且你们在storyboard文件中设计应用界面时已经抛开了具体尺寸大小的束缚。但是你们难道没有发现在所有尺寸的设备中,不管是竖屏还是横屏,应用的界面布局都是一致的吗?只是自适应了尺寸大小而已。这还远远不是设计。
当你们决心要设计自适应的界面并已经开始设计的时候,有一点很关键。那就是要知道界面在不同的Size Classes要有继承的关系。你们应该首先设计一个基础的界面,然后根据不同尺寸的横竖屏在基础的界面上进行自定义。千万不要把不同的Size Classes当做独立的屏幕尺寸去设计UI。应该在你们的脑海中建立起界面的一个继承关系的思想,也就是大多数的设备使用基础界面,然后特别的尺寸及横竖屏再根据情况基于基础界面修改。
在本文中,一直没有像大家介绍过如何设置特殊设备的布局,那是应为自适应布局的核心概念Size Classes本身就是由各种特殊设备的特点抽象而来的。也就是说一个Size Classes就意味着一种特殊设备的布局特点,其实普通情况也是特殊情况中的一种。所以说我们可以组合不同的Size Classes来满足各种特殊的布局情况,比如一个支持自适应的视图,它可以在应用的父视图控制器中自适应,也可以在某一个功能的视图控制器容器中自适应。但是两者自适应后的布局却不相同。
这种改进对Apple本身也是有益的,因为他们不断的改变移动设备的尺寸,但从来没有强迫开发者和设计者重新开发和设计他们的应用以适应新尺寸的设备。这就不会让开发者和设计者对Apple不断改变设备尺寸这件事有抗拒心理。
接下来,我们将自定义Size Classes以适应iPhone横屏的时候,因为现在的布局在横屏时用户体验很糟糕。使用Size Classes回到 Main.storyboard,点击底部的 w Any h Any,你就可以看到Size Classes的选择器了:
在这个由9个方格组成的网格中,你就可以选择你想在storyboard中显示的Size Class。一共有9种组合方式:3种垂直的也就是竖屏的(任意尺寸(Any),普通(regular),紧凑(compact))选择和3种水平的也就是横屏(任意尺寸(Any),普通(regular),紧凑(compact))的选择。注意:这里有一点需要大家注意。在Size Classes中,有两个重要的概念叫做水平(Horizontal)和垂直(Vertical)。但是在IB中叫做宽(Width)和高(Height)。但他们是等价的,所以大家记住这个概念有两种叫法就可以了。目前我们的布局在紧凑高度(Compact Height)时显示的很糟糕,也就是iPhone横屏时。我们来解决这个问题,在Size Classes选择器中选择 Any Width | Compact Height 的组合:
这时你会发现在storyboard中会立即出现2个变化:
storyboard中的view controller变成了我们刚才设置的size class。
storyboard底部会出现蓝色的长条区域,并显示出当前我们正在使用的size class。
为了在该size class下改变布局,我们要临时改变一些之前设置好的约束。在自动布局中这种操作有个术语叫做 装配(installing) 和 卸载(uninstalling) 约束。当一个约束在当前的size class中是适用的,我们就将该约束装配在当前的size class中,如果不适用,我们就卸载它。
选择image view,在右侧工具栏中选择 Size Inspector。你可以看到在image view上添加的所有约束:
单击鼠标左键选择 Align Center X to: Superview 约束,然后按下键盘上的 Delete 键来卸载该约束。这时我们可以看到在storyboard中这个约束就立即消失了,并且在storyboard的结构目中和 Size Inspector中该约束都变成了灰色:
注意:你可以在Size Inspector中点击 All 来查看当前size class卸载掉的约束。鼠标双击刚才卸载的那条约束,我们可以看到在约束编辑界面的底部出现了额外的2个选项:
这两个选项的意思就是这条约束在基础布局中是可用的,但在当前的 Any Width | Compact Height 布局中是不可用的。
按照上面的步骤卸载掉image view上的另外3个约束:
现在你就可以添加适合当前size class的约束了。我们添加一个 Align/Vertical Center in Container 约束,再添加一个 Pin/Left Spacing to nearest neighbor 约束,其值设置为10:
选择image view,按住 Ctrl 键从image view上拖拽至view controller的view上,在弹出的菜单中选择 Equal Widths 约束。
打开右侧工具栏中的 Size Inspector 页签,双击 Equal Width to: Superview 打开该约束的属性编辑界面。如果 First Item 属性的值不是 cloud.Width ,那么点击输入框,在下拉菜单中选择 Reverse First and Second Item。然后将 Multiplier 属性的值设置为 0.45。
现在image view在所有的size class中显示应该都没有什么问题了。但是text container view还有点问题。你需要给它添加一个约束,让它显示在该size class屏幕的右侧。
TextContainer view现在有两种约束在身。一种是内部约束,它约束了两个Label的位置,这些约束在各size class中表现的还不错。另一种是外部的约束,它们限制了text container view的左、右、底部与它容器的左、右、底部的间距。这些约束在当前的size class中表现的就不尽如人意了。如果想使text container view在当前size class中位于容器的右下角位置,你得卸载掉左侧的约束。
选中 Left Spacing to nearest neighbor 约束:
按 Cmd-Delete 卸载该约束,和之前一样,被卸载的约束显示为灰色。
现在你需要再添加两个约束将TextContainer限制在正确的位置上。一个是让text container view的宽度为它容器(view controller的view)宽度的一半。另一个是将text container view固定在顶部。
按理来说,你现在需要选中text container view然后按住Ctrl键和鼠标左键拖动鼠标到view controller view上,然后选择约束。但是目前的情况由于image view和text container view占满了整个view controller,所以你很难选中view controller的view。同学们可以通过storyboard的结构树上进行该操作,会容易很多。
在结构树中选中TextContainer,按住 Ctrl 键和鼠标左键,拖动鼠标到结构树的View上:
弹出菜单中显示了可用的约束,按住 Shift 键点击 Top Space to Top Layout Guide 和 Equal Widths 约束:
然后选中TextContainer,在 Size Inspector 中设置刚刚添加的两个约束:
将 Top Space to: Top Layout Guide约束的值设置为0。
将 Equal Width 约束的 Multiplier 的值设置为0.5。这里要注意 First Item 和 Second Item 这两个属性的值。前者应为TextContainer view,后者为view controller view。如果不一致,那么点击任意一个输入框,选择 Reverse First and Second Item。
现在点击storyboard界面底部的 Resolve Auto Layout Issues 按钮,然后选择 All Views\Update frames 。看看发生了什么变化呢:
到目前为止,我们的布局已经越来越接近完美了,唯一一点不足的就是字体大小的自适应,我们会在下一节解决它!文字属性的自适应目前TextContainer中的文字尺寸在iPad设备上,也就是使用普通(Regular)size class显示还比较正常。但是当使用紧凑(Compact)size class时文字尺寸就显得太大了,以至于都超出了视图。不过同学们不要怕,我们照样可以在不同的Size Classes中设置不同的文字尺寸来做到自适应。注意:与重写布局不同,在不同的size class中改变文字的属性始终会影响基础布局中的文字。它不能像布局一样,在不同的size class中设置不同的属性值。我们通过下面的方法来解决这一问题。回到storyboard文件中,将目前的size class改为最基础的 Any Width | Any Height 。
选择显示Cupertino的Label,打开 Attribute Inspector 。点击 Font 属性前面的 + 号:
弹出的菜单内容是让我们选择一种size class的组合来重写该组合下的文字属性。我们选择Compact Width & Any Height:
这时就会出现另外一个文字属性下拉框,针对于我们刚才选择的 Compact Width | Any Height size class,我们将字体大小改为90:
再选择显示温度的Label,重复刚才的操作,选择size class组合时选择 Compact Width & Any Height。设置字体大小为150。
在预览区域会自动更新我们刚才的设置:
现在看起来稍微好一些了,但是显示 Cupertino 的Label被截掉了两头。同学们可能会继续调整字体大小使Cupertino显示完全,虽然目前看起来完美了,但是当换一个城市名称后或许又会出现刚才的问题。比如Washington, D.C这么长的名称,又比如Kleinfeltersville, PA这个更长的名称。那么我们应该如何设计呢?
我们的救世主 自动布局(Auto Layout) 再次出马。你只需要给显示城市名称和温度的这两个Label设置一个相对于TextContainer view的宽度约束即可。选中显示Cupertino的Label,按住 Ctrl 键和鼠标左键,拖动鼠标到TextContainer view,在弹出菜单中选择 Equal Widths 约束。对显示温度的Label做相同的操作。之后在预览界面看看发生了什么:
呃……貌似还是有问题,城市名显示不完全。Label中的文字长度超出了允许显示的空间。不过我们可以通过一个选项,让Label自动判断当前的空间可以显示多大的字体。
选择显示Cupertino的Label,然后打开 Attribute Inspector。将 AutoShrink 属性设置为Minimum font scale,将其值设置为0.5。将 Alignment 属性设置为 Centered:
对显示温度的Label做相同的操作。
再来看看预览区域,是不是在不同尺寸的iPhone横屏、竖屏下显示都比较完美了:
是时候在不同的设备上编译运行我们的程序了。用设备来检验才是最保险的。iPhone下的横屏、竖屏是多么的完美:
同学们,至此你们已经学习到了自适应布局最基本的技能。恭喜你们!
原文地址:
原文出自:,由@DevTalking翻译!通用的Storyboard通用的stroyboard文件是通向自适应布局光明大道的第一步。在一个storyboard文件中适配iPad和iPhone的布局在iOS8中已不再是梦想。我们不必再为不同尺寸的Apple移动设备创建不同的storyboard文件,不用再苦逼的同步若干个storyboard文件中的内容。这真是一件美好的事情。
我们打开Xcode,新建一个项目:
选择iOS\Application\Single View Application创建一个单视图应用:
设置项目名称AdaptiveWeather,语言选择Swift,设备选择Universal:
创建好项目后,我们在项目目录结构中可以看到只存在一个storyboard文件:
Main.storyboard文件就是一个通用的storyboard文件,它可以适配目前所有屏幕尺寸的Apple移动设备。打开该文件,同学们会看到一个View Controller,以及一个我们不太熟悉的界面尺寸:
同学们不要吃惊,没错,你们看到的就是一个简单的、有点大的正方形!大伙都知道,在上一个版本的Xcode中,storyboard里的屏幕尺寸都对应着我们所选的目标设备的尺寸,但是这样无法让我们达到“用一个storyboard搞定所有设备”的宏伟目标。所以在iOS8中,Apple将storyboard中屏幕的尺寸进行了抽象处理,也就是说我们看到的这个正方形是一个抽象的屏幕尺寸。
我们接着往下走,选中Main.storyboard文件,然后在右侧工具栏中选择File Inspector页签,然后勾选Use Size Classes选项:
在新的iOS8项目中,该选项默认是勾选的。但当你使用老版本的项目创建新的storyboard文件时就需要你手动进行勾选了。设置你的Storyboard文件首先,我们打开Main.storyboard文件,从组件库(Object Library)中选择Image View拖拽到View Controller中。选中刚刚拖入的Image View,在右侧工具栏选择Size Inspector页签,设置X坐标为150,Y坐标为20,宽为300,高为265。
然后再拖入一个View组件,设置X坐标为150,Y坐标为315,宽为300,高为265。
选择你刚才拖入的View,在右侧工具栏中选择Identity Inspector页签,在Document面板中的Label属性输入框中输入TextContainer。这个属性的作用就是给View起一个名字,方便我们辨认。这里要注意一下,Document面板有可能是隐藏的,我们需要点击它后面的 Show按钮来显示它。我们拖入的这个View最后是显示城市和温度Label的容器。
完成上面的设置后,同学们可能会发现刚才拖入的View貌似看不到,这是因为它的背景色和View Controller的背景色是相同的,都是白色,所以我们不太容易辨别。我们来解决这个问题,选中View Controller的View,然后在右侧工具栏中选择Attribute Inspector页签,设置背景色为 红:74,绿:171,蓝:247。然后再选择TextContainer,就是我们拖入的View,设置背景色为 红:55,绿:128,蓝:186。此时Main.storyboard文件中应该是这番景象:
到目前为止,我们在View Controller中添加了两个组件Image View和View,这也是仅有的两个组件,接下来我们就要给它们添加一些布局约束了。添加布局约束选择image view,点击底部自动布局工具栏中的Align按钮,勾选Horizontal Center in Container选项,将后面的值设置为0,点击 Add 1 Constraint按钮添加第一个约束。
这个约束的意思是让image view在它的容器(View Controller的View)中保持居中。
然后再点击底部自动布局工具栏中的Pin按钮,添加一个image view顶部与容器顶部间距的约束,我们设置为0:
上面这两个约束使image view处于容器居中的位置,并且它的顶部与容器顶部有一个固定的间距。现在我们需要添加image view和text container view之间的约束。同学们先选中image view,然后按住Ctrl键和鼠标左键,从image view往text container view移动鼠标:
松开鼠标左键后会弹出一个约束菜单,我们选择Vertical Spacing:
这个约束决定了image view底部和text container view顶部之间的距离。
现在选中image view然后点击右侧工具栏中的Size Inspector页签,同学们会发现这里在Xcode6中和之前的Xcode版本有所不同:
你会看到之前添加的三个布局约束,你可以在Size Inspector中很方便的修改这些布局约束。比如点击Bottom Space To: TextContainer约束后的 Edit按钮,会弹出约束属性编辑框,我们让Constant的值等于20:
然后点击该弹出框之外的任意地方关闭该弹出框。
你先已经将TextContainer view顶部与image view底部的间距调整到了20,我们还需要添加TextContainer view另外三个边的间距约束。
继续选择TextContainer view,点击底部的Pin按钮弹出 Add New Constraints窗口,在Spacing to nearest neighbor面板中设置左、右、底部的约束,将值设置为0,然后点击Add 3 Constraints按钮添加约束。这里要注意的是,在设置约束时要将 Constrain to margins选项的勾去掉,这样可以避免TextContainer view产生内边距:
这三个约束会让TextContainer view的左、右、底部三个边与容器的左、右、底部的间距始终为0。
现在Main.storyboard中应该是这番景象:
此时同学们应该会注意到在view上有几个橘黄色的约束线,这意味着还有一些约束上的问题需要我们注意。不过在运行时storyboard会自动更新view的大小来满足它与容器的约束条件。我们也可以点击底部 Resolve Auto Layout Issues 按钮,在弹出框中选择 All Views in View Controller/Update Frames 来修复提示的约束问题,但是如果我们这样做,那么image view的尺寸就会压缩成零,也就是会看不到image view。
这是因为我们的image view还有没有任何内容,但是它有一个缺省的高和宽,并且值为0。进行自动布局的时候,如果被约束的view没有实际的高和宽,那么会依照缺省的高和宽来满足约束条件。
我们接着学习,在项目结构中打开 Images.xcassets ,然后点击左下角的 +号,在弹出菜单中选择 New Image Set:
双击左上角的 Image 标题将其改为 cloud :
我们刚才新建的这个image set其实就是若干图片文件的一个集合,其中的每一个图片都会对应一个特定的应用场景,也就是针对与不同分辨率的Apple移动设备。比如说,一个图片集合可能会包含针对非视网膜、视网膜、视网膜高清三种分辨率的图片。自从Xcode中的资源库与UIKit完美结合后,在代码中引入图片时我们只需要写图片的名称,程序在运行时会根据当前运行的设备自动选择对应分辨率的图片。注意:如果你以前使用过通过资源库管理图片,那么你可能会发现在Xcode6中会有所不同。那就是3x图片是怎么回事?
这个新的分片率是专为iPhone 6 Plus提供的。这意味着每一个点是由3个像素点组成,也就是说3x的图片比1x图片的像素多9倍。目前你的图片集合中还是空的,同学们可以在这里下载需要的图片,然后将图片拖入刚才创建的名为cloud的图片集合中,将 cloud_small.png图片拖到 1x图片区域:
由于我们的图片背景颜色是透明的,所以在图片集合中看到的都是白色的图片。你可以选中某一个图片,然后按下空格键来预览图片。比如选中 1x 图片,按下空格:
现在将 cloud_small@2x.png 图片拖至 2x 图片区域,将 cloud_small@3x.png 图片拖至 3x 图片区域。和之前情况一样,我们看到的只是白色的图片,但我们可以通过空格键来预览图片集合中的图片。
现在你就可以在image view中设置图片了。我们回到 Main.storyboard 中,选中image view,在右侧工具栏中选择 Attribute Inspector 页签,将 Image View 面板中的 Image 属性设置为cloud,然后将 View 面板中的 Mode 属性设置为 Aspect Fit :
现在你的Main.storyboard中应该是这番景象:
我们看到storyboard中一直有橘黄色的约束提示,是时候让我们来修复它们了。首先选中view controller的view:
然后点击底部的 Resolve Auto Layout Issues 按钮,在弹出菜单的 All Views in View Controller 面板中选择 Update Frames :
这时,storyboard会自动根据约束条件重新计算view的大小以满足约束:
预览助手编辑器(Preview Assistant Editor)一般情况下,在这个时候我们应该会在iPad、iPhone4s、iPhone5s、iPhone6、iPhone6 Plus这几个不同尺寸的设备上编译运行程序,以便测试通用的storyboard是否能在不同尺寸的设备上正确的自适应。但这确实是个体力活,一遍一遍的更改设备、编译、运行,多么苦逼。但上天总是会眷顾我们这些苦逼的程序员,Xcode6提供了Preview Assistant Editor,能在一个界面上显示出不同尺寸设备的程序运行情况,是否有问题一目了然。
我们打开 Main.storyboard ,然后选择 View\Assistant Editor\Show Assistant Editor ,这时编辑区会分隔为两部分。再点击顶部导航栏中的 Automatic ,在弹出菜单中选择 Preview ,最后选择 Main.storyboard (Preview) :
现在在 Assistant Editor 区域会显示一个4寸的界面:
我们还可以点击预览界面底部,名字(比如图中的iPhone 4-inch)旁边的地方让屏幕翻转为横屏:
这无疑是针对检查不同尺寸设备的自适应情况的一项重大改进,但还远远不止于此!点击预览界面左下角的 + 按钮,会弹出当前storyboard文件支持的各种尺寸的设备,可供我们预览:
分别选择iPhone 5.5-inch和iPad,此时我们在预览界面就可以同时显示三种尺寸的屏幕:
此时同学们是否注意到4寸的横屏显示有点别扭呢?没错,它的那朵元太大了,我们可以通过对image view添加其他的约束条件来改善这个问题。
回到 Main.storyboard ,选择image view,然后按住 Ctrl建和鼠标左键,拖动鼠标到View Controller的View上,松开鼠标后会弹出一个菜单,我们选择 Equal Heights :
这时会出现一些红色的约束提示,这是因为我们刚才加的这个约束条件与之前加过的约束条件有冲突。因为之前我们添加过image view和TextContainer view之间的垂直间距(Vertical Margins)约束,所以image view的高度不可能等于它容器(View Controller的View)的高度。
让我们来修复该问题,首先在storyboard的结构目录中选择我们刚才添加的 Equal Heights约束,然后选择右侧工具栏中的 Attribute Inspect 页签,如果 First Item 属性不是cloud.Height ,那么在下拉菜单中选择 Reverse First and Second Item 这一项让 First Item 的值成为 cloud.Height :
接下来将 Relation 属性的值设置为 Less Than or Equal ,将 Multiplier 的值设置为 0.4 :
这一系列设置的作用是让cloud这张图片的高度要么等于它自身的高度,要么等于屏幕高度的40%,最后呈现的效果选择这两者中较小的一个高度。
现在你应该注意到了在预览面板中,4寸的横屏显示即时的对你刚才的约束改动做出了响应:
你看看其他尺寸的预览自动更新了么?答案那是必须的,所以说 Preview Assistant Editor 确实是一项重大改进,是程序员和设计人员的福音!
由于本文的示例是一个天气应用,所以光有天气图标不行,我们还得加上城市和温度才行,所以敬请期待下一篇教程吧!
本文译自。
iOS 7在视觉方面有许多改变,其中非常吸引人的功能之一就是在整个系统中巧妙的使用了模糊效果。许多第三方应用程序已经采用了这样的设计细节,并以各种奇妙的和具有创造性的方式使用它。
本文将通过几种不同的技术来实现iOS 7中的模糊效果,当然,这一切都利用了一个名为的框架。
GPUImage是由创建的,它利用GPU,使在图片和视频上应用不同的效果和滤镜变得非常的容易,同时它还拥有出色的性能,并且它的性能要比苹果内置的相关APIs出色。
注意:本文需要一台物理设备来编译并运行示例程序(在模拟器上无法使用)。同样还需要一个iOS开发者账号。如果你还没有开发者账号的话,可以来[这里](https://developer.apple.com/)注册一个。注册为开发者之后,会有许多福利哟,例如可以使用物理设备来开发程序,提前获得苹果的相关测试版程序,以及大量的开发资源。
下面我们先来看看本文的目录结构:
为什么要是用模糊效果
添加静态的模糊效果
创建截图Category
利用断点测试截屏图片
显示截屏图片
设置contentsRect
重置模糊滤镜
对其背景图片
线程中简洁的分支
一些潜在的实时模糊方案
一个折中的方法——对视频实时模糊
利用GPUImage对视频进行模糊处理
何去何从?
开始首先先来下载本文的starter工程,并将其解压出来。
用Xcode打开Video Blurring.xcodeproj,并将工程运行到设备中。此时看到程序的效果如下所示:
点击屏幕左上角的菜单(三条横纹),可以看到界面中出现两个选项:录制视频和播放已有视频。
请注意,现在所有的用户界面都有一个灰色的背景,是不是感觉有点沉闷呢,本文我们就利用iOS 7中的模糊效果来替换掉这些沉闷的灰色背景。为什么要是用模糊效果除了外观看起来很棒以外,模糊效果还可以让程序给用户带来3个重要的概念:深度引导、上下文和关注度。深度引导在用户界面上,模糊效果可以给用户提供一个深度引导效果,并且有利于用户对程序导航的理解。在之前的iOS版本中的深度引导效果是通过:三维斜面(three-dimensional bevels)和有关泽的按钮(反映出一个模拟的光源),而在iOS 7中是通过模糊和视差()来实现的。
这里说的视差效果,可以很明显的观察出来:在装有iOS 7的设备中,将设备从一侧倾斜至另一侧,会发现设备中的图标在移动(会独立于背景)。这样可以给用户做出一个提示:界面是由不同的层构成的,并且重要的界面元素是在最前面的——这也涉及到下面将要介绍的一个概念:上下文。上下文上下文可以让用户在程序内获得一种轴承的感觉。动画的过度效果就提供了一种非常优秀的上下文,当用户点击一个按钮时,在两个view之间利用动画效果来切换画面(而不是直接显示一个新的view),可以让用户知道新的view是从哪里出现的,并且可以让用户很容易知道如何回到上一个view。
模糊效果可以将上一个view当做背景显示出来,尽管上一个view已经失去焦点了,不过可以给用户提供更多的上下文:刚刚是在哪里。通知中心就是一个非常棒的例子:当拉下通知中心时,我们可以在背景中看到原来的view(即使现在正在处于通知中心界面)。关注度让界面更加关注于某些选择项上,而移除不需要的内容,让用户可以更加快捷的进行导航。用户可以本能的忽略那些被模糊的界面元素,而将注意力集中到某些界面元素中。
通过本文,你将学到两种模糊类型的实现方法:静态模糊和动态模糊。静态模糊代表着快照的时间点,它并不能反映被模糊界面元素的变化。大多数情况下,使用静态模糊效果就足够了。相反,动态模糊则是对需要模糊的背景做出实时更新。
相信看到具体的效果才是最好的,下面我们就来看看模糊效果的具体实现吧!添加静态的模糊效果创建一个静态模糊效果首先是将当前屏幕中的view转换为一幅图片。获得图片之后,只需要对图片做模糊处理就可以了。将view转换为一幅图片(截屏)苹果已经提供了一些非常棒的APIs了,并且在iOS 7中又有了新的方法可以让截屏更加快速。
这些新的方法属于中的一部分,截屏APIs不仅可以对某个view截屏,还能把整个view层次截屏,如果你希望对某个view截屏,那么可以把view中的按钮、标签、开关等各种view也进行截屏。
此处我们将截屏的逻辑实现到UIView的一个category中。这样一来,我们就可以很方便快捷的将任意的view(以及view中的内容)转换为一个图片——也算是代码的重用吧。创建截图Category打开File/New/File...,然后选择iOS/Cocoa Touch/Objective-C category,如下图所示:
将这个category命名为Screenshot,并将它的category选为UIView,如下图所示:
将下面这个方法声明到UIView+Screenshot.h中:-(UIImage *)convertViewToI
接着将如下方法添加到UIView+Screenshot.m中:-(UIImage *)convertViewToImage
UIGraphicsBeginImageContext(self.bounds.size);
[self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
上面的方法中,首先调用了UIGraphicsBeginImageContext(),最后调用的是UIGraphicsEndImageContext(),这两行代码可以理解为图形上下文的一个事物处理过程。一个上下文可以理解为不同的概念,例如屏幕,或者此处可以理解为一幅图片。这里的两行代码起到一个离屏画布的作用——可以将view绘制上去。
drawViewHierarchyInRect:afterScreenUpdates:方法利用view层次结构并将其绘制到当前的上下文中。
最后,UIGraphicsGetImageFromCurrentImageContext()从图形上下文中获取刚刚生成的UIImage。
现在,我们已经完成了category的实现,接着我们只需要在使用到的地方将其import一下即可。
如下代码所示,将代码添加到DropDownMenuController.m顶部:#import "UIView+Screenshot.h"
同时,将如下方法添加到相同的文件中:-(void)updateBlur
UIImage *image = [self.view.superview convertViewToImage];
上面的代码确保是对superview进行截屏,而不仅仅是当前的view。不这样做的话,截屏获得的图片只是menu本身。利用断点测试截屏图片为了测试截屏的效果,我们在convertViewToImage调用的下面一行添加一个断点。这样当命中断点时,程序会在断点中暂停执行,这样我们就可以看到截屏的图片,以此确保截屏代码的正确性:
在测试之前还有一件事情需要做:调用上面这个方法。
找到show方法,并在addToParentViewController下面直接调用一下updateBlur:-(void)show {
[self addToParentViewController];
[self updateBlur]; // Add this line
CGRect deviceSize = [UIScreen mainScreen].
[UIView animateWithDuration:0.25f animations:^(void){
_blurView.frame = CGRectMake(0, 0, deviceSize.size.height, MENUSIZE);
_backgroundView.frame = CGRectMake(0, 0, _backgroundView.frame.size.width, MENUSIZE);
编译并运行程序,点击菜单按钮,可以看到Xcode在断点出停止了,如下所示:
在debugger左下角hand pane中选择image,然后单击快速查找图标按钮,就可以预览刚刚的截屏啦:
如上图所示,正是我们所预期的。显示截屏图片将截取到的图片显示到菜单的背景中就是小菜一碟啦。
一般来说我们都会利用UIImageView来显示一幅图片,而由于我们要利用GPUImage来模糊图片,所以需要使用GPUImageView。
在这里的工程中,已经添加好了GPUImage框架,我们只需要将头文件import一下即可。
将下面的代码添加到DropDownMenuController.m顶部:#import &GPUImage/GPUImage.h&
注意:GPUImage被包含在一个框架中,所以在import语句中,需要利用尖括弧,而不是双引号。
此时,有一个_blurView,类型为UIView——是菜单的灰色背景。将UIView修改为GPUImageView,如下所示:@implementation DropDownMenuController {
GPUImageView *_blurV
UIView *_backgroundV
修改之后,Xcode会报一个warning:大意是你利用UIView进行实例化,而不是预期的GPUImageView。
可以通过下面的方法消除这个警告,在viewDidLad中修改做如下修改:_blurView = [[GPUImageView alloc] initWithFrame:CGRectMake(0, 0, deviceSize.size.height, 0)];
紧随其后,将如下两行代码添加进去,并移除设置背景色的代码:_blurView.clipsToBounds = YES;
_blurView.layer.contentsGravity = kCAGravityT
clipToBounds属性设置为YES,把超出_blurView范围的子view隐藏起来,而contentsGravity确保图片出现在image view的顶部。
由于_blurView已经用于背景了,所以此处不需要额外设置了。
接着,我们还需要声明一个用于模糊效果的过滤器。
将如下代码添加到DropDownMenuController.m:文件的@implementation中:GPUImageiOSBlurFilter *_blurF
找到之前添加的断点,右键单击,并选中Delete Breakpoint:
下面是非常重要的一步了——初始化模糊滤镜。将如下代码添加到DropDownMenuController.m中:-(void)updateBlur
if(_blurFilter == nil){
_blurFilter = [[GPUImageiOSBlurFilter alloc] init];
_blurFilter.blurRadiusInPixels = 1.0f;
UIImage *image = [self.view.superview convertViewToImage];
注意:上面将模糊半径设置为一个像素,这里暂时将这个值设置低一点,这样可以确保图片的正确定位,当一切ok之后,再增加模糊半径即可。
下面是时候将图片显示到GPUImageView中了。不过并不是简单的实例化一个UIImage,并将其添加到GPUImageView中。首先需创建一个GPUImagePicture。
将如下代码添加到updateBlur方法的底部:GPUImagePicture *picture = [[GPUImagePicture alloc] initWithImage:image];
至此,我们获得了一个图片,模糊滤镜和iamge view。
接着再将如下代码添加到updateBlur底部:[picture addTarget:_blurFilter];
[_blurFilter addTarget:_blurView];
[picture processImage];
上面这几行代码,就像胶水一样,将所有的事情关联起来。将滤镜当做target添加到图片中,然后将image view当做滤镜的target。
上面代码对图片的处理全程发生在GPU上,也就是说当进行模糊计算和显示时,并不会影响到用户界面。当处理结束时,会把图片显示到image view上面。
编译并运行程序,点击菜单按钮,可以看到如下类似画面:
上面的图片看起来是不是有点奇怪?看到的图片被缩放到适配到菜单视图中了。要对此做出修正,我们需要指定图片的哪一部分需要显示在GPUImageView中——也就是处理截屏视图的上半部分。设置contentsRect按照如下代码所示修改DropDownMenuController.m文件中的show方法:-(void)show
[self addToParentViewController];
[self updateBlur];
CGRect deviceSize = [UIScreen mainScreen].
[UIView animateWithDuration:0.25f animations:^(void){
_blurView.frame = CGRectMake(0.0f, 0.0f, deviceSize.size.height, MENUSIZE);
_backgroundView.frame = CGRectMake(0.0f, 0.0f, _backgroundView.frame.size.width, MENUSIZE);
_blurView.layer.contentsRect = CGRectMake(0.0f, 0.0f, 1.0f, MENUSIZE / 320.0f); // Add this line!
通过指定_blurView.layer.contentsRect来定义一个矩形,在单元坐标空间(unit coordinate space)中,表示只使用layer content的一部分。
编译并运行程序,点击菜单按钮,会看到如下图所示效果:
虽然已经使用了图片的一部分,看起来还是不正确,这是因为它的缩放比例还不适合!此处还缺少对正确内容的缩放。
将下面这行代码添加到show方法中动画block的尾部:_blurView.layer.contentsScale = (MENUSIZE / 320.0f) * 2;
contentsScale属性声明了layer在逻辑坐标空间(以点为单位)和物理坐标空间(以像素为单位)之间的映射关系。更高比例因子表示在渲染layer时,一个点代表着多个像素点。
编译并运行程序,点击菜单按钮,可以看到缩放比例已经正常了:
没错——看起来好多了!现在关闭程序,然后重新打开,ou~~发生了什么?如下图所示:
看起来这还是有点问题。如果在对view进行animation之前将contentScale设置回2.0,会解决half bar的问题。
将如下代码添加到DropDownMenuController.m中show方法里面的animation block上面:_blurView.layer.contentsScale = 2.0f;
编译并运行程序,然后点击菜单,接着关闭菜单,再打开菜单,此时菜单开起来如下所示:
现在半个尺寸的黑色box已经没有问题了——但是现在是全尺寸的黑色box!重置模糊滤镜上面问题产生的原因是由于进行了二次模糊计算。解决的方法是移除模糊滤镜中的所有target。如果不这样做的话,之后对滤镜的调用不会输出任何的内容——进而引起黑色box的问题。
按照如下代码更新一下updateBlur方法:-(void)updateBlur
if(_blurFilter == nil){
_blurFilter = [[GPUImageiOSBlurFilter alloc] init];
_blurFilter.blurRadiusInPixels = 1.0f;
UIImage *image = [self.view.superview convertViewToImage];
GPUImagePicture *picture = [[GPUImagePicture alloc] initWithImage:image];
[picture addTarget:_blurFilter];
[_blurFilter addTarget:_blurView];
[picture processImageWithCompletionHandler:^{
[_blurFilter removeAllTargets];
上面的代码用processImageWithCompletionHandler:替换了processImage方法。这个新的方法有一个completion block,当image 处理结束时,会运行这个block。一旦image处理结束,我们就可以安全的将滤镜中的target全部移除。
编译并运行程序,点击菜单,检查一下黑色box问题是不是已经解决掉了:
多次打开和关闭菜单,确保之前的那个bug已经解决掉啦!
现在仔细观察一下打开菜单的模糊效果——有些东西看起来不正确。为了更加明显的观察到问题,我们减慢动画的时间,让其慢慢的移动。
在show方法中,将animation bloc的持续时间修改为10.0f。
编译并运行程序,点击菜单,然后观察一下菜单出场的慢动作:
恩,现在可能你已经发现问题了。被模糊的图片从顶部往下滑动——而我们的本意是希望模糊效果从上往下滑(并不是图片本身)。对其背景图片此处我们需要对静态模糊效果使用一些技巧。当出现菜单时,我们需要利用背景来将模糊效果对其。所以在这里我们不是对image view做移动处理,而是需要对image view做扩展处理,从0开始扩展至image view的全尺寸。这样就可以确保菜单打开时,图片依然保留在原位。
在show方法中,我们已经将菜单打开至全尺寸了,所以现在只需要将contentRect的高度设置为0即可(当image view首次创建并隐藏的时候)。
将下面的代码添加至DropDownMenuController.m文件的viewDidLoad方法中——在_blurView初始化的下方:_blurView.layer.contentsRect = CGRectMake(0.0f, 0.0f, 1.0f, 0.0f);
同时,在相同的一个文件中,将下面的代码添加到animation block的尾部:_blurView.layer.contentsRect = CGRectMake(0.0f, 0.0f, 1.0f, 0.0f);
contentRect属性是可以动画方式设置的。因此在动画期间会rect会自动的插补上。
编译并运行程序。可以看到,问题已经解决:
这样看起来自然多了。现在我们已经有一个具有模糊背景的滑动菜单了。
现在是时候把动画所需时间调整一下了(为了更好的效果,其实之前设置的值是为了测试所用):设置为0.25秒,接着在updateBlur方法中将_blurFilter.blurRadiusInPixels设置为4.0f。
编译并运行程序,多次打开菜单,看看效果如何:
实时模糊实时模糊涉及到的技术具有一定的难度,有些难点需要解决才行。为了有效的进行实时模糊,我们需要不停(每秒60帧)的截屏、模糊计算和显示。使用GPUImage每秒中处理60张图片(模糊并显示图片)一点问题都没有。
真正棘手的问题是什么呢?如何实时的截取屏幕图片,信不信由你!
由于截取的屏幕是主用户界面,所有必须使用CPU的主线程来截屏,并将其转换为一幅图片。
提醒:如果事物每秒钟的变化速度在46帧以上,那么人眼就无法识别出来了。这对于开发者来说也是一种解脱——现代处理器在各帧之间可以完成更多的大量工作。线程中简洁的分支当运行程序时,会执行大量的指令列表。每一个指令列表都运行在各自的线程中,而我们又可以在多个线程中并发运行各自的指令列表。一个程序在主线程中开始运行,然后会根据需要,创建新的线程,并在后台执行线程。如果之前你并没有管理过多线程,你可能在写程序的时候总是在主线程中执行指令。
主线程主要处理与用户的交互,以及界面的更新。确保主线程的响应时间是非常关键的。如果在主线程上做了太多的任务,你会明显的感觉到主界面响应迟钝。
如果你曾经使用过Twitter货Facebook,并滚动操作过它里面的内容,你可能已经感觉到后台线程在执行操作了——在滚动的过程中,并不是所有的个人图片立即显示出来,滚动过程中,程序会启动后台线程来获取图片,当图片获取成功之后,再显示到屏幕中。
如果不使用后台线程,那么table view的滚动过程中,如果在主线程上去获取个人图片,会感觉到table view被冻结住了。由于图片的获取需要一些时间,所以最好将这样耗时的操作让后台线程来做,这样就能对用户界面做平滑的操作和响应了。
那么对本文的程序有什么影响呢?之间介绍了,UIView的截屏APIs操作必须在主线程中运行。这就意味着每次截屏时,整个用户界面都会被冻结中。
对于静态模糊效果时,由于这个截屏操作很快,你不会感觉到界面的冻结。并且只需要截屏一次。然而在实时模糊效果中需要每秒中截屏60次。如果在主线程中做这样频繁的截屏操作,那么animation和transition会变得非常的迟钝。
更糟糕的时,如果用户界面复杂度增加,那么在截屏过程中就需要消耗更多的时间,那么就会导致整个程序无法使用了!
那么怎么办呢!一些潜在的实时模糊方案这里有一个关于实时模糊方案:源代码开源的,它通过降低截屏的速度来实现实时模糊效果,并不是使用每秒截屏60次,可能是20、30或者40次。即使看起来没有多大区别,但是你的眼睛还是能发现一定的迟钝——模糊效果并没有跟程序的其它部分同步起来——这样一来,界面看起会没有模糊效果更加的糟糕。
实际上苹果在它们自己的一些程序中处理实时模糊并不存在类似的问题——但是苹果并没有公开相关的API。在iOS 7中UIView的截屏方法,相比于旧方法,性能有了很大的提升,但还是不能满足实时模糊的需求。
一些开发者利用UIToolbar的模糊效果来做一些不好的操作。没错,这是有效果的,但是强烈建议不要在程序中使用它们。虽然这不是私有API,但是这并不算是一种可行的方法,苹果也可能会你的程序。也就是说在,在之后的iOS 7版本中,并不能保证还能正常使用。
苹果可以在任何时候对UIToolBar做出修改,或许你的程序就有问题了。在iOS 7.0.3更新中,苹果的修改已经影响到UIToolbar和UINavigationBar了,有些开发者也因此报告出利用相关模糊效果已经失效了!所以最好不要陷入这样潜在的陷阱里面!一个折中的方法——对视频实时模糊OK,此时你可能在想,要想在程序中做到实时模糊是不可能的了。那么还有什么方法可以突破限制,做到实时模糊效果呢?
在许多场景中,静态模糊是可以接受的。上一节中,我们对view做适当的修改,让用户看起来是对背景图做的实际模糊处理。当然,这对于静止不动的背景是合适的,并且还可以在模糊背景上实现一些不错的效果。
我们可以做一些实验,看看能不能找到一些效果来实现之前无法做到的实时模糊效果呢?
有一个方法可以试试:对实时视频做模糊处理,虽然截屏是一个非常大的瓶颈,但是GPUImage非常的强大,它能够对视频进行模糊(无论是来自摄像头的视频或者已经录制好的视频,都没问题)。利用GPUImage对视频进行模糊处理利用GPUImage对视频的模糊处理与图片的模糊处理类似。针对图片,我们实例化一个GPUImagePicture,然后将其发送给GPUImageiOSBlurFilter,接着再将其发送给GPUImageView。
类似的方法,对于视频,我们使用GPUImageVideoCamera或GPUImageMovie,将后将其发送给GPUImageiOSBlurFilter,接着再将其发送给一个GPUImageView。GPUImageVideoCamera用于设备中的实时摄像头,而GPUImageMovie用于已经录制好的视频。
在我们的starter工程中,已经实例化并配置好了GPUImageVideoCamera。现在的任务是将播放和录制按钮的灰色背景替换为视频的实时滤镜效果。
首先是将此处提供的灰色背景实例UIView替换为GPUImageView。完成之后,我们需要调整每个view的contentRect(基于view的frame)。
这听起来对每个view都需要做大量的工作。为了让任务变得简单,我们创建一个GPUImageView的子类,并把自定义的代码放进去,以便重用。
打开File/New/File…,然后选择iOS/Cocoa Touch/Objective-C class,如下所示:
将类命名为BlurView,继承自GPUImageView,如下图所示:
打开ViewController.m文件,将下面的import添加到文件顶部:#import "BlurView.h"
还是在ViewController.m中,在@implementation中找到_recordView和_controlView的声明,将其修改为BlurView类型,如下所示:BlurView *_recordV //Update this!
UIButton *_recordB
BlurView *_controlV //Update this too!
UIButton *_controlB
然后按照如下代码修改viewDidLoad方法:_recordView = [[BlurView alloc] initWithFrame:
CGRectMake(self.view.frame.size.height/2 - 50, 250, 110, 60)]; //Update this!
//_recordView.backgroundColor = [UIColor grayColor]; //Delete this!
_recordButton = [UIButton buttonWithType:UIButtonTypeCustom];
_recordButton.frame = CGRectMake(5, 5, 100, 50);
[_recordButton setTitle:@"Record" forState:UIControlStateNormal];
[_recordButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
[_recordButton setImage:[UIImage imageNamed:@"RecordDot.png"] forState:UIControlStateNormal] ;
[_recordButton addTarget:self
action:@selector(recordVideo)
forControlEvents:UIControlEventTouchUpInside];
[_recordView addSubview:_recordButton];
_recording = NO;
_recordView.hidden = YES;
[self.view addSubview:_recordView];
_controlView = [[BlurView alloc] initWithFrame:
CGRectMake(self.view.frame.size.height/2 - 40, 230, 80, 80)]; //Update this!
//_controlView.backgroundColor = [UIColor grayColor]; //Delete this!
接着,需要创建模糊图片,将其显示到上面构建的image view中。回到@implementation中,将下面的两个声明添加进去:GPUImageiOSBlurFilter *_blurF
GPUImageBuffer *_videoB
现在你已经知道GPUImageiOSBlurFilter的作用了,那么GPUImageBuffer的作用是什么呢?它的任务是获取视频的输出,并获取每一帧,这样我们就可以方便的对其做模糊处理。一个额外的好处就是它可以提升程序的性能!
一般来说,视频输出的内容会通过模糊滤镜处理,然后发送到背景视图中(被显示出来)。不过,在这里使用buffer的话,发送到buffer的视频输出内容,会被分为背景视图和模糊滤镜。}

我要回帖

更多关于 安卓游戏开发代码 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信