iOSApp技术整理归档——基础篇

2017-09-13 10:25:00来源:http://www.jianshu.com/p/d238866b816a作者:Even会编程人点击

分享


从事任何一行都需要了解本行业基础和核心知识,那么作为一个苹果开发者,你必须特别熟悉苹果基本库和框架;
以下知识收集整理难度不等,重要性不等,且未进行近一步分类,我会尽可能详细的去描述背景、实现过程和重点难点;



开发核心——iOS开发基本库、框架等
1. UI
[UIScroll View]:


UIScrollView的约束设置
UIScrollView的约束构成分为两方面,一是frame,二是size。frame对应的是scroView的展示大小和位置,而size则是实际大小,也就是scrollView真实的大小,这个size决定了scrollView能不能滚动。简单理解,如果设置了scrollView的frame和允许滚动,那么size的大小只要超过了frame的size,即实际大小,超过了scrollView的展示范围,就可以滚动了。那么相应的,如果实际的高度超过展示高度就可以垂直滚动,如果实际的宽度超过了展示宽度就可以水平滚动。

以上为背景,在使用Autolayout或者Masonry进行scrollView的约束设置时,除了设置scroView的frame之外还需要设置scrollView的实际大小(contentSize),一般采用以下两种方式。
一(不推荐):设置完scrollView的frame之后为其添加一个子控件(UIView——参照控件),并为其设置约束——距离父级控件上下左右间距均为0,再为其添加宽和高的约束。以设置scrollView垂直方向滚动为例,一般默认此子控件宽度等于屏幕宽度,因为这个子控件的上下左右边距都为0,那么子控件的高度即为我们最终要控制的scrollView实际的高度。在ViewController中指定一个变量来接收此高度约束,动态改变这个高度值,就可以控制scrollView的实际高度,达到随意控制滚动范围的效果了。
二(推荐):设置完scrollView的frame之后,为其添加所有子控件,并且为这些子控件设置好相应的约束,但是有一个原则:这些元素必须距离父级元素(即scrollView)左右间距为0,并且指定一个固定的高度。还是以设置scrollView垂直方向滚动为例,设置好所有子控件的约束并给定高度值之后,设置最下方的控件宽度等同于屏幕宽度,底部距离父级控件为0,这样最后一个控件的底边Y值就对应scrollView的真实高度,那么这些子控件相当于把整个scrollView撑起来了,这样也就达到了动态控制滚动范围的效果。之所以说是动态控制滚动范围,是因为这样设置之后,不需要修改任何代码,任何一个scrollView的子控件高度改变之后,scrollView的真实高度会自动变化。所以强烈推荐此种方式。



[UIButton]:


点击状态、图片、背景、高亮、按钮组
开发过程中经常遇到按钮的大小需要根据文字内容动态设定,这时候可以不给定宽高的约束,按钮自然就会根据内容自动调整宽高了。此技巧适用于大部分继承自UIView的控件(需要设置contentSize的控件不能使用此技巧),比如:UIButton、UITextField、UILabel、UIWebView等等;

但是此种方式应用在UIButton时会出现按钮贴近文字致使按钮显得很丑,这个时候就需要用到UIButton的一个特性ContentInsets,这个属性分为Left、Top、Bottom、Right四个属性值,分别代表了插入到四个边界的长度,即内容和边界之间的间距。比如设置了四个边距为10,则按钮的宽度高度分别扩大20,内容依然居中,这时按钮的样式就会变得不那么紧凑,也没有那么丑了。这种方式也适用于设置按钮内容偏向于某一边,或者某一个方向等等。具体应用场景及详细设置请自主实践。

在安卓开发中有按钮组的概念,即:任意时刻只能有一个按钮处于被选中状态。苹果开发中并无此概念,那么如果你想实现只能选中按钮组中的一个按钮的话该怎么办呢。首先设置一个中间变量来接收当前选中的按钮,然后每次点击这些按钮之后进行判断,如果点击的按钮是当前的选中的按钮,不执行任何操作;如果不是,则替换当前选中的按钮为点击的这个按钮,并且执行相应的操作即可。为按钮设置不同的tag值,设置一个变量来接收按钮的tag值,每次点击按钮检测tag值也可以实现按钮组效果,而且节省内存,因为存储UIButton的变量比存储一个数字要更占用内存。



[约束]:


绝对约束和相对约束
所谓绝对约束,即给定控件一个绝对大小或者坐标,比如:设置一个按钮的大小为h:30、w:60,坐标为x:100、y:100;这种方式在大部分时候是不建议使用的。因为第一是需要根据不同屏幕的设备动态调整Button的坐标以实现屏幕适配,第二会为App的UI实现和维护带来很多问题,第三增加了代码量、开发成本进而影响开发周期。
而相对约束就灵活的多,比如:设置一个按钮大小为根据文字内容动态变化,即:不需要设置宽高,但是针对UIButton需要设置ContentInsets四个边距为10,再设置这个按钮在屏幕垂直方向1/3屏幕位置,水平方向1/2位置。至此按钮的位置、大小已经设定完成,它会随着屏幕的大小变化而自动变化位置,按钮大小则会随着按钮的内容变化而变化。

但是绝对约束也是有用武之地的,比如为了美观我希望某个页面上所有的控件上下排列,并且左边距屏幕都为10,那这个时候我就需要把所有控件放到一个UIView上,设置这个View为距左边距为10。所以相对约束和绝对约束相辅相成,需要根据实际情况去衡量。



[事件的传递和矛盾]:


常用事件管理、解决事件冲突
所有的继承自UIView的控件都可以接收点击、触摸等事件,但是前提是你需要开启User Interaction Enabled,这样才能接收到事件,而且你需要为控件添加相应的监听事件,如:点击事件、触摸事件、滑动事件等等。
常用的事件包括:触摸页面隐藏键盘、右划返回上一页、点击或者长按图片显示大图、显示大图之后左右滑动切换图片、长按文字复制、双击放大等等;
大部分控件默认是开启User Interaction Enabled,所以如果你希望触摸事件穿透一个控件,让在下层的控件接收触摸事件的话需要关闭上层控件的User Interaction Enabled。
如果基本的事件无法满足需求,或者需要对事件做更深入的管理的话,可以实现TouchEvent事件的代理的方式来实现,通过对事件的类型,点击的坐标等参数对想要做的处理及操作进行进一步开发。



[使用最合适的控件]:


善于利用原生控件、适当修改封装新控件、尝试自定义新控件以满足特殊需求
选用适当的控件会提高你的开发效率,增强应用稳定性并且充分利用不同控件的特性。比如:如果需要显示文字,就用UILabel;如果需要一段文字有不同的颜色、大小,就采用富文本;如果需要这段文字能够响应点击事件,就用UIButton(或者添加触摸事件监听,但是不是这个部分讨论的内容);如果需要有按下的状态,只需要设置UIButton的高亮状态对应的属性就OK了;如果需要图片就使用UIImageView,虽然普通的UIView可以设置背景图的方式实现显示图片,但是请不要这样做,这不符合设计规范;如果需要一个滚动页面,请衡量这个页面里的内容是否样式一致,只是内容不同,如果是这样就用UITableView或者UICollectionView,如果不是,就用UIScrollView。

做到了这点,才能用最少的代码,最精确的实现UI设计需求和功能。另外,要恰当的自定义UI,比如:你需要让你的所有按钮都具备一个统一的样式,请扩展一个自定义的Button类型,在这个自定义的Button里来实现这些样式的初始化,这样以后每次使用Button可以直接使用这个自定义的Button,不用写任何代码也不需要设置任何属性,就可以获得你期望的样式;以此类推,当你觉得基本的UI控件满足不了你的需求的时候,适当的自定义UI控件或者扩展控件,会让你效率有很大提升。



[UI设计实现的精确性]:


精确完成设计的想法和方案、UI必须100%还原,提出UI优化方案
一个好的软件开发人员,一定具备了UI设计师最基本的素质,就是对于设计图的理解和认知。简单说就是能看懂设计图,并且能够100%实现设计方案。当然,实现UI设计并不只是仅仅看懂就行,还需要UI控件知识储备以及常用第三方UI库,甚至需要你具备扩展或者自定义UI控件的能力。不过,这里讨论的是对于UI设计的理解,以及对于设计细节的把握。比如,UI设计师给出的设计稿中你必须能够看出整个页面的层次、色调、重要性以及每个控件的基本属性。

所谓层次,就是整个页面的层级结构,哪一部分在上层,哪一部分在下层,页面能不能滚动,滚动之后那些部分会被固定;而色调则是你对于整个页面的基本色调的把握,UI设计师当然是期望你能够百分百还原设计图的,但是你必须首先把握住整个页面的主色调和主要组件的色调,只有这样,当真正需要做出一些UI方面优化的时候你才可以有自己的想法,并且能够给出一定的意见;然后就是重要性和基本属性,你需要从设计图的色调,位置,阴影等来判断每一个控件的基本属性,是Label还是Button?是UIView还是UIimageViw?并且需要根据这些属性来决定每个控件的重要性和对应的响应事件、动效等等。



[UI页面、组件动态化]:


即使抛弃热更新,也需要一定程度的让UI动态化,利用配置文件、网络数据、UI模板化
苹果封杀了热更新的方案,不谈对错,只是就这件事本身的作用来说,其实是利于开发者的。但是对于运营来说,绝对是致命的打击。首先就是无法快速改变UI页面以及业务逻辑,其次不能快速的迭代版本,也不能及时响应、处理线上版本出现的问题......总之,我们必须想办法解决这个问题,即使抛弃热更新,我们也需要让UI动态化,快速及时的解决问题,实现业务逻辑的快速转换等等。

首先说一下UI动态化,我曾经考虑过使用plist文件来配置UI页面的结构,用网络数据来改变框架的内容。简单说就是每次开启App会获取UI界面的plist配置文件,这个文件里制定了整个UI界面的结构,组件基本属性、样式等等信息,然后我们会根据plist的配置信息来重构界面,而所有的数据则是通过网络请求获取的。plist会有一个默认的结构版本,也会自动存储每一次从网络端获取的配置信息,以保证即使没有网络的情况下,UI基础框架依然正常。但是这种设想需要很庞大的UI设计规范及映射体系来支持,因为UI界面的设计和细节变化是特别大的,如果没有一套完善的UI控件及配置的映射关系,是实现不了真正意义上的UI动态化的。索性我们可以把这一思想给利用起来,至少可以实现轮播图样式,图片,内容已经业务逻辑的动态改变,方法就如之前所说,不过这里就用不到plist配置文件,一个HTTP请求接口返回对应数据,足以。也可以实现列表样式的动态改变、UI界面色调的动态改变、部分模块的UI构成动态改变等。

然后说一下业务逻辑的动态改变和Bug的及时修复。首先业务逻辑的动态改变可以参照上面说的UI动态设置,只需要把业务逻辑中固定格式的那部分从数据库里抓取并返回相应数据的方式来实现。比如我有十个获取商品信息的数据接口,它们分别获取不同类目的商品,根据入库时间和优惠券到期时间排序,这时我们就可以把排序方式和类目名称等条件的值存储到数据库,当需要对这些规则进行修改的时候只需要修改数据库中对应的值即可。但是这种修改仅限于可以抽象、归类的业务逻辑。如果需要修改整个业务逻辑的结构,就不能实现,不过我们可以退而求其次,只需要对业务逻辑进行更高层次的抽象就OK了,比如把类目、排序方式、关键词、优惠券价格、折扣力度等等这些条件都抽象出来作为参数存储在数据库,所有接口都根据这些参数去获取数据,那么我们所有的获取商品的接口都可以动态的调整。而Bug的动态修复,我目前有自己的方案,但是实现起来颇为复杂,就不做解释了,有一个比较好的方法就是多建立一些测试方法,实现对关键业务逻辑和UI的多角度验证,确保应用的健壮性,并且提前预期可能出现的意外情况,做出相应的处理或者试错方式。



[定期优化自主控件]:


自己封装控件是项目中提高效率的一个最直接的方法,所以要学会建立自己专属的控件库并且定期优化
苹果官方为我们提供了强大且丰富的UI控件库,能够满足我们绝大多说情况下的开发需求。但是,这还不够,当我希望我的UI控件具备一些特别的功能或者样式的时候,就需要扩展或者自定义UI控件,比如:一个会跳舞的文字,一个点击之后有爆炸特效的按钮,一个会自动记录历史访问页面的webView等等,都需要我们自己去扩展或者创建新的UI控件。这是一个很好的实践机会,会让你充分的发挥你的基础知识,从而达到预期的开发效果,并且提升开发效率,扩展你的知识储备。不过随之而来的是一些坑。可能你实现了预期的效果之后,还会附带一些你没有想到的“并发症”。比如文字跳动之后会出现阴影,按钮爆炸特效致使页面卡顿......这些都是不被允许的,你不能实现一个功能,但是遗留一个问题。解决这些问题是你势在必行的,所以完善你的自定义控件库是你要一直做下去的事,生命不息,填坑不止。不断地去优化你的控件库,以更好的实现你预期的功能和样式,既能让你保持技术成长的动力,也能不断地提升你的开发效率,使得项目更加完善,稳定和丰富。



[UI开发之Xib和纯代码]:


UI实现是做前端展示类应用必备的技能、请收集相关框架、源码、思想
关于Xib的背景和使用技巧不是我这里想讨论的范围,我想讨论的是应该用Xib实现UI设计还是纯代码?不说废话,直接表明我的观点。整体的UI框架请使用Xib实现,第一是更直观的展示App结构和主体,第二可以快速实现UI基本结构以供产品经理进行评估。具备样式固定、内容基本不变等特性的UI控件也是用Xib实现,因为这类控件维护很少,基本上第一次实现之后就不需要作出调整,可以直接使用Xib来实现,避免写入代码中影响编辑代码的效率和美观。高复用率的控件和模块请使用纯代码设计,因为纯代码UI设计本身就具备高复用的特点,而且可以快速精准的改变统一样式。需要动态展示数据或者根据不同情况作出调整的要使用纯代码进行设计,因为Xib不具备这方面的能力,或者说很弱,而使用纯代码来实现UI动态的动态展现绝对是最好的选择。

有这样几个简单的标准来区分使用场景:需要变化的就用纯代码,静态的就用Xib;不涉及数据的就用Xib,设计数据的尽量用纯代码;界面效果比较复杂的可以采用Xib加AutoLayout,可以提升效率;多处使用或者需要设计不同样式的,请使用纯代码;



2. 网络数据
[网络请求的封装]:


请求封装、请求管理、请求方法模块化
关于网络请求的封装,有不同的方式和程度,需要你根据项目的实际情况来定,比如你的项目中很少用到HTTP请求,那就封装到可以直接调用一个方法传入请求地址、请求参数,能够获取数据并能触发其他事件就足够了。一个比较大的好处就是可以统一格式化数据、返回值和特殊情况。但是如果请求特别多,那就需要把请求再进行一层封装,做到只需要调用一个方法,传入请求参数,就可以获取数据并进行下一步操作,这样,请求的地址、请求方式、其他参数等都可以在封装的这一层单独处理,而且便于管理。

简而言之,可以将网络请求设计为四层:


网络请求配置——配置网络请求的基本参数,格式化参数、返回值、回调事件等;
网络请求发起——配置请求参数、请求地址
网络请求管理——管理不同的请求
网络请求调用——调用不同的请求,传入请求参数


[网络数据的格式化]:


格式化数据、建立网络数据模型
在网络请求的封装部分,我提到参数、返回值的格式化。首先我们要明确一件事,为什么要格式化?格式化的好处很多,比如:更清晰的数据结构,更完整的数据描述,更便于使用的“.”语法,更容易进行序列化,也能更加安全稳定的使用这些数据等等;那么针对网络数据我们又有哪些需要特殊提到的好处呢?第一就是格式化以满足后端需求,如果我们的后台需要的参数格式规范是所有参数的key值必须加上“app-”这样的前缀,我们就必须对请求的参数进行格式化,但是不需要每一次请求都对参数进行这样的操作,而只需要在网络请求的配置层把传入的参数格式化即可。第二就是能够在其他地方快速稳定的调用相关的对象,比如我们的返回值数据格式已经规范好,那这个时候我们完全可以建立一个返回数据的model类,来存储返回值得对象,这样我可以直接使用model.name这样的方式直接获取某一个值,快速,安全,稳定,这也是一种格式化;另外还有一种方式就是我们格式化数据之后就可以在App中任何一个地方按照约定好的格式化的规范来使用或者管理这条数据,但是如果没有对数据进行格式化的话,我们就无法准确的判断这条数据的格式、构成甚至内容。



[网络请求过程优化]:


获取进度、请求参数规范化、后端接口规范、请求动画及页面友好
获取网络数据有两种不同的表现形式,一种是后台获取,即不影响UI操作,不影响业务流程,二是需要获取页面展示的数据,会造成UI页面的操作。我们这里主要针对第二种情况进行讨论,虽然这个应该是UI设计师应该去考虑的事情,但是我还是想从开发的角度去探讨一下关于数据加载过程优化的问题。一般来说我们会在UI设计层面进行加载状态的展示,以消除用户等待过程中的枯燥、急迫感、和反感。这里又有几种不同的形式,一是显示一个加载中的图标,让用户知道在加载数据,但是这个只能告诉用户在加载,不能起到缓解用户情绪的作用,最次!二是显示加载的进度条,这样用户会很清楚的知道需要加载的数据当前加载了多少,并预期会在多久之后加载完毕,看上去挺棒,能够缓解用户焦虑的情绪,至少能转移一下注意力。但是实际上这种做往往会出现这样那样的问题,比如网速太慢或者数据过大致使加载的进度特别慢,这样用户会觉得你的App反应很慢。或者加载过程中出现不可描述的情况突然加载进度卡住不动了,而你又没有做好相应的处理,用户会直接爆粗口的,备选。第三种则是引入一个加载的动画特效,类似一个小动漫人往某一个方向走路或者跳动或者爬楼梯等等,并且在出现意外断网或者服务器连接失败等特殊情况时出现提前设计的“加载出现问题”提醒图标,这样既满足了转移用户注意力,又不至于永无止境的加载下去。这种方式更利于用户接受,至少我是能接受这种设计的。

那么从开发角度我们有哪些可以优化的地方呢?首先是提升加载速度,从网络层面入手,减少不必要的参数,设置等等;其次从UI加载的角度出发,加快数据解析和UI重绘的速度,优化UI布局、样式和代码的书写;再者可以进行适当的预加载,用以满足数据的预览,但是请慎用,即使要用,也需要专门的接口来实现,不能像获取完整信息那样直接拿到所有的数据,否则会浪费手机性能,并且特别消耗用户流量,影响App的使用体验。



[预加载数据]:


适当的预加载数据可以让应用体验提升很高的体验
预加载的使用一直是我所提倡的,但是真正运用在App项目开发中却是慎之又慎,最主要的原因就是预加载的数据是不是用户需要的,用户下一次的操作是否真的需要使用到这些数据,也就是对于用户意图的预判。当然这点可以通过设计App的流程来解决,比如,我点击进入一个页面之后只有一个“查看详情”按钮,那么我可以提前把这个详情页的内容获取到,因为用户没有其他的操可能。但是这个时候也会有很对情况需要去思考和处理,假设这个详情的数据过于庞大,我们是绝对不能在这个页面显示时就获取详情数据的,因为这会大量的消耗流量,但是用户可能不需要点击进入详情页面,这个时候我们可以采用多设计一个预览信息数据接口的方式来实现预加载,即只请求一小部分信息,确保进入下一个页面的时候有一定的数据已经展示出来了,会改善一定的用户体验,又不至于浪费太多的性能和流量。

还有一种情况是UI界面的架构和内容都会随着请求获取的数据而改变,这个时候我们不能用让UI界面卡顿的形式来获取这些数据,改变UI布局,二是获取数据和UI展示并行的方式进行。也就是说我们在获取到新的UI结构设置和数据之前,需要先按照旧的布局和数据显示,然后在适当的机会重汇UI和内容,这也是预加载的一种表现形式。类似的方法还有很多,但是有一个原则必须要遵守,在尽量不损耗手机性能和流量的情况下,尽一切努力减少用户等待的时间和UI的卡顿。预加载是为了提升用户体验而实现的,而不是简单为了提前获取信息,方便数据加载和展示用的,需要多方面、多角度去考量。





最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台