iOS 开发小记 (六)
这一篇是最近开发遇到的问题:
一,善用Category 实现功能分块与重用
UIViewController+UIImagePickController,实现了选择照片的功能。
一个APP中选择照片的地方有很多,用category实现后,使用时每个地方就只需一行代码。
至于选中后的回调,考虑了Block、delegate、notify,最终还是选择了notify。
原因是category存属性的问题,不知道objc_setAssociatedObject 放进去block和delegate之后,ARC会发生怎样的变化。
把选中的图片,用objc_setAssociatedObject存储,再发出notify,完成catgory实现的选择照片功能。
2,图片浏览功能
APP中需要浏览大图的地方很多,网上也有很多开源的第三方图片浏览器,在网上看了下,很多功能用不到,于是决定自己弄一个。
功能:查看大图、放大缩小、有删除返回功能。
最后实现的方案:
新建一个controller,view上放置一个scrollview,scrollview上放置UIImageView。controller通过push 的形式调用,这个调用同样用category封装起来。
几个精妙的地方:
UIImageView放在scrollView里面后,要设置宽度、高度大于等于view(controller的view),图片显示是center;这样的好处是,当显示一个图片的时候,默认是放在view正中间,而且是图片的原大小。
通过scrollview的zoom来实现放大缩小,直接在IB里面填入最大最小的倍数,就可以实现放大缩小。
回调功能,删除的时候通过block回调删除方法。这里一开始采用的是notify,后来发现notify 的时候需要传参数,而且很多地方需要的参数都不一样,如果一一通过notify设置,会很复杂,于是改用block。
三,OC的继承关系
假设有类Parent,其中有方法methodA,里面有调用[self methodB];
有类Child继承Parent,同时也重写methodA,methodB。
这时候,就会出现问题:当Child调用methodA的时候,[super methodA]里面的[self methodB],调用的是Child的methodB。如果Child在methodA里面也调用了methodB,那么就会出现子methodB调用了两次,而parent的methodB没有调用到。
实际开发的场景中,Parent是BaseViewController,Child是childViewController,methodA是viewdidload,methodB是customView。
当child再viewdidload的函数中调用[super viewDidLoad] 和 [self customView]的时候,就会发生child的customView调用了两次,而parent的customView一次都没有调用到。
解决方案:1,不要用一样的名字,child的改成customChildView;
2,在child的customView中调用, [super customView]。同时child不要调用customView。
(这个也是self和this指针的不同,如果是c++的话,在[super viewdidLoad]的函数中,调用的 customView应该是parent的customView。
四,MVVM架构中的model与viewModel
viewModel的生命周期随着controller,而model一般是整个app的生命周期。
个人的开发习惯是,把model当成一个持久化数据库。network请求写在viewModel中,请求成功数据后,把数据update给model存起来。
如果是遇到有很多viewModel 需要做同一个请求的,把请求放在model,而model直接向model请求。
谈到网络请求,不得不说异步回调的问题。
APP通过网络加载数据,必然伴随着加载完毕的回调。
解决方案:
1,RACObserve Viewmodel中的属性或者model的属性(不太建议对model中使用RACObserve)
2,把请求封装在RACSignal里面,通过subscribeNext或者subscribeCompleted来进行回调。
个人是两个一起用,1用来观察常变的值,通常是绑定一个viewModel和View时使用;
2常用于用户操作后进行网络请求,回调后进行刷新,通常是reloadData的形式。
五,Websocket的实用
开发中遇到一个功能,需要用到服务器向客户端发送消息和保持长连接,同时又是一个http服务器,于是用到了websocket。
地址是ws: 或者 wss:开头,wss需要证书,这次用的是ws:。
用的是开源的SocketRocket,非常简单。使用时通过open连接服务器,如果失败,有fail。连接后收到消息有didRecive,断开连接有close。
这个地方使用一个静态的viewModel来存储webSocket,保证app生命周期不会重复创建。把服务器返回来的status放置到viewModel,controller通过RACObserve来观察这些status。很完美实现了这个功能。
六,删除功能与刷新功能
一个APP框架要和需求、功能相适应,这样当产品提出一个合适的需求的时候,不至于手忙脚乱。
最近做的这个APP,有一个功能是删除一个动态,这个动态在很多列表都会出现。这里可以采用监听者模式,删除的controller通过notify发出事件,而需要更新的controller自己监听这个事件,从而更新视图。
同时,这个在列表中用左滑删除的功能(用户侧滑删除 -- 后台请求服务器删除 -- 删除返回 -- 内部删除数据 -- UI动态显示删除) 用ReactiveCocoa来实现非常方便。
七,修改Pods第三方的代码
功能需要修改第三方的代码,这个问题比较麻烦。
1,通过podfile 把pods的路径加一个 => "github...." 定位到自己的github。
2,工程导入形式, pod "../../test" 直接修改工程文件。
3,复制代码到工程,直接修改。
用的是第二种方法,一开始尝试直接在pods里面添加文件失败。在导入的工程修改完后,再运行一个pods Install。发现.xib没有导进来,应该是自己对pods的理解还不够深。
八,测试
单元测试:对本地一些自定义方法进行测试。
UI测试:可以点录制,点击模拟器,然后生成UI测试代码(几乎不可用)。
异步测试:
1,自己实现。用runMode等待,轮询判断是否完成。
2,execption。自带的异步测试功能。
九,图片压缩
从系统中选择照片后,图片都很大。压缩成宽高 不超过一定数目。
UIGraphicsBeginImageContext 和 UIGraphicsGetImageFromCurrentImageContext , 配合UIImageJPEGRepresentation解决了问题。
图片压缩:前面两个函数是缩,后面一个函数是压。
十,视图跳转
APP中经常有页面的跳转,遇到一个需求是在TabA的页面中,需要跳到TabB的页面,同时要push一个新的页面。
一开的时候经常遇到log打出来的warning。思考了一下,发现是自己的操作太连贯了。
当跳转TabB的时候,rootController还没显示,就开始push新的页面。
解决方案:延迟0.1s进行push操作。
(最后还有一个warning,是关于UISearchController的,在stackoverflow找到答案:在dealloc中把searchController的view removeFromeSuperView。)
这次就先写十点吧,下次接着写,还有关于推送、IM聊天、地图、自动布局、发布测试等等。