移动互联网服务客户端开发技巧
(一)
随着微博、LBS 等移动互联网服务的火爆普及,越来越多的苹果开发者希望制作各网络服务的 客户端。 会员 “” 的《基于网络的客户端开发技巧》系列文章详细介绍了 客户端应用的开发思路和注意事项,希望对苹果开发者们有所帮助。
一、做一个程序之前,要明确你要做的是什么。这之间道道不少,不仅仅要实现功能,还要考虑到程序功能之间是如何链接,用户如何交互。在这个基础上,才能和美工一起配合,把程序的设计做好,而仅仅的 UI,已经不能称得上是完整的设计了。利用 API 扩展是必需的,Open API 不是你自家的 API,所以肯定是有局限性的,如何将 API 与产品良好的结合,是重点,抓细节,是重点。每条微博就是一个少量信息,所以客户端要做的就是快,清楚,友好,稳定。
二、就目前而言,主流的微博特效就不再多说了,以后有可能细讲,基本上都有开源的了。
三、目前大多展示都是用的 。这里就要多说说了。 的使用方式有好多,下拉刷新,即可以将展示放到 的 上,也可以放到 cell 上,这 2 种有啥区别呢?目前没有发现实质上的区别,只是在计算的时候放在外面比较好算。
正常来说,展示的步骤为:
四、特色功能。想做出与众不同的产品,目前在我看来,只有特色功能。像微博的展示,lbs的签到,再怎么做,也已经大体定型,就算你做的再认真,也是别的程序换一套皮肤,在我看来,投入成本还是换一个方面的好。想一些实用的功能,扩展一下,在完成基本功能的同时,可以给用户一个使用自己程序的理由。
五、照片处理特效。现在基本都是用开源库,主流的开源库不说了,基本都是 C、C++的,只要能编译成功,基本没啥使用难度了。问题在于处理图片的效率上。
六、安全性。像我们公司为了加强用户密码的安全性,特意买了SSL,为的就是让用户的账号更安全。而 Open API 使得我们没法控制,所以最好使用 oauth 认证,以及不要将用户的账号暴露在缓存之中,这些都会增加风险。
(二)及正则
随着微博、LBS 等移动互联网服务的火爆普及,越来越多的苹果开发者希望制作各网络服务的 客户端。 会员 “” 的《基于网络的客户端开发技巧》系列文章详细介绍了 客户端应用的开发思路和注意事项,继第一章以后,今天发布第二章:及正则
目前微博客户端基本都使用,这篇就详细讲解一下使用利用展示微博的技巧。当然,主要以代码为主。Demo暂时就不提供了。之前也有几篇博文提到相关的,这里简单总结一下。
为什么要用来展示呢,就我目前的经验来看,为了文字链接图片混排比较方便。所以既然要混排,又不能让人觉得这是,首先就要禁止,也就是边缘滚动。方法如下:
[( *)[[ ] :0] :NO];
的各种复制,全选禁止方法:
..style. = “none”; //禁止弹出菜单
..style. = “none”;//禁止选中
具体代码可以看下
使用起来注意的问题有:
相当慢,所以要做到良好的展示效果,想办法预加载吧。当然用JS也可以。使用js的时候千万要外部导入js,不然会调试到头大都调试不好。。各种转义字符对于,记得图片尺寸要双倍。的html里面尽量用/”而不是’ 比如
而不是
下面说一下正则,将链接替换的方法,用的:
- ( *):( *)_str
* = @"@//b(//w+)//b";
* = @"@$1";
* = NULL;
= [_str : :];
NSLog(@"%@",);
;
提取链接的方法:
* = [ :@"%@ %@",[dic ],[dic ]];
* = @"//?://[a-zA-Z0-9//-.]+(?::(//d+))?(?:(?:/[a-zA-Z0-9//-._?,'+//&%$=~*!():@]*)+)?";
* = [ egex:];
解析一个字符串的方法
* = @"]+)/" rel=/"([^>]+)/">([^>]+)";
* = [ Regex:
:@"url", 1, @"",2,@"",3, NULL];
if (![[ ] :@""]) {
* = @"]+)/">([^>]+)";
= [ Regex:
:@"",1,@"",2, NULL];
为”新浪微博”
利用这些,基本可以完成微博上的需求。同时可以利用JS实现渐近渐现等效果,用CSS将布局处理的更合理。不会用CSS就td吧。
(三)上下拖动
这次来说说像那样上下拖动切换的效果是如何做的。如何触发这里就不多说了,靠 也可以,按钮也可以。这里主要介绍一下这种效果的3种做法,各有好处。
第一种:如上图,提前预加载好 3 个 view,比如往下翻的时候,就将 view C 移到 view B,view B 移到 view A,,view A 重新加载。这样做的好处就是展示的效果比较好,可控性高。代码见
第二种方法:利用 来做。在 的一层中套入一个 view,同样预加载 view A B C,每一次都只要把要显示 view 加入到 中的 view 上,并利用 cell 来刷新一下。这种做的好处就是简单,对代码水平要求不高,但是与第一次效果相比略显粗糙,可扩展性不强。比如要将下拖的时候,将 cell2 中的 view B 删除,加入 view C,然后调用
*path = [ :1 :0]; //指向cell2的path
[self. hs:[ :path] :
];//刷新
第三种方法:同样是 ,但是这次不需要预加载,优点就是对于 相当实用,处理也比较简单;缺点同方法二。比如要将 cell1 cell2 cell3 向前滚动,用 cell4,cell5,cell6 代替。可以执行以下代码
[self. ];
*path1 = [ :0 :0];
*path2 = [ :1 :0];
*path3 = [ :2 :0];
//更新 中的数据,cell数量等于删除之后的数量相同,否则会出错
[self. hs:[ :path1,path2,path3,nil] :];
//更新 ,cell的数量是添加之后的,同时数据是新的
[self. hs:[ :path1,path2,path3,nil] :];
[self. ];
这样相当于把原来该是cell4,cell5,cell6中的数据从下向上到cell1,cell2,cell3的位置。
(四)多帐号
多账号无非就是可以让你登录 N 个账号,我个人比较倾向于将个人信息存在 中,所以先讲讲我 中的结构。
中,->()->任一 ()->key:,,(获取用户信息时所返回的数据,主要保存 ID,name 等)
-> ()表示当前使用的账号
再说说缓存
就像之前所说,公共信息可以放在同一个文件夹,像个人信息就放入以 id 为标识的文件夹中方便存储。比如我现在的账号名为 aaa,密码 bbb,用户 ID 为 1001,那么必然要选择用户 id 来命名文件夹名,当然也可以选用表的,选择原则是无重复,不私密。
比如我的 里面的目录结构 -> (存用户头像,图片) (ID 为 1001 的缓存信息),(ID 为 1002 的缓存信息)
最后再说说如何处理多账号
切换账号不必多说了,根据UI设定来搞吧。这里主要提几个比较需要注意的地方:
api 的封装,最外层一定是错误处理,否则你的用户名在非本客户端修改之后,必然会出问题。增加错误检测的好处还有可以明确知道是哪的问题,不要以为只有开发者需要,用户也同样需要。在写程序之前就要提前划分好哪一些是公共数据,哪一些是个人账号私有数据,提前封装好,这样不管是修改,还是以后作扩展,都会快很多。缓存分账户存放是有一个好处的,不必每次启动程序都去检测密码,每次调用API的时候有前端错误检测,所以可以完全放心由于api引起的 crash问题。同时每个账号进入的时候都是会有缓存信息的,不会太空,既然你的密码已经不一致。坏处嘛,就是你就算修改了密码,还是能看到之前缓存过的数据。
总之一句话,多账户主要就是信息存储,存储的明确了,做起来那是相当容易。
(五)细节
1、本来这篇名字想叫“一根线”的,但是发现要说的细节还是比较多的,所以改了下名。我要说的这一根线,当然就是的cell分隔线。相当现在应用,cell的默认分隔线已经满足不了多数开发人员的要求了,所以制作分隔线再所难免。那么,这根分隔线如何去做呢?
至少我相信,基本很少人会用画的方式把线画到 Cell 上,多数还是用图片。那么图片怎么做,将是非常值得讨论的。把线合到背景上,虽然可以解决问题,但是明显是不可取的,对于现在的 内存,也许不算什么,但是对于将来 UI 变化,实在是相当之不方便,而且效果不能说是完美。
那么如何去处理这一根线呢,可以利用 1 像素的线与 - ( *):() :() 配合拉伸实现,也可以单独做一个图片,当然这 2 种方法效率谁快我还真没做过测试。。。
2、既然是基于网络服务的客户端,那么网络传输是必然少不了的。所以状态栏的网络状态必不可少。@(, =) BOOL 千万不要忘记哈~同时不要太频繁的 set Yes,太频繁,你会发现状态栏的 是一直在闪的。
3、 的左右按钮,虽说已经给定好了位置,但是对齐还是相当必要的。这就要求图片对齐,或者用代码控制image 。同时还有一点需要注意,按下状态也是要对齐一下的,不要以为手按下去看不见就无所谓,细节真的可以决定成败。
4、每个 cell 中的排版,间距一定需要注意,该对齐的地方一定要对齐,这样看起来才舒服。有条件最好研究一下视觉范围。
5、每个页面加载的速度。open api 必然会有限制发挥的地方,要合理利用 api,而不要盲目的使用 api 来达到预先的效果。尽可能的节省流量,为用户节约每一分钱。
6、比较杯具的 4 的高清屏,想要显示清楚就必然要缩小图片在屏幕上的 size,这方面只能将就了。
7、在 中不管是转发,还是 @ 以及 ##,记得尽量处于用户的角度想一下,移动好光标的位置。移动方法是修改的@()
8、在手势冲突的时候,设置好优先级。
9、使用尽量简单,尽量隐蔽的动画,来达到最好的效果。
10、没有摄像头的设备,记得不要弹出来自摄像头。。。
11、/ 的键盘类型,返回键类型
12、注重细节并不是意味着到处加效果。
13、程序员也是设计师,也有义务和美工一起搞好 UI。要重构的时候必须重构,就算你把制作程序当成工作,当成赚钱的手段,那么你也必须要和美工一起达到目前能达到的最好效果,这是对你工作的交代。当然,我一直认为这样的人不适合做一名程序员,至少我一直认为我从事的是我的兴趣,我完成的是我的创作。
14、 如果是 IB 拖出来,一直是转动状态,而代码只是控制显示和隐藏,记得测试后台返回的时候 是否还在转。
15、尽量避免出现由于加载而导致用户无法操作的情况。
16、整体风格的统一。
17、使用gps时,需要的时候再打开,用完关闭。
18、字体大小特别要注意,可以设有几套字号,但是同类微博的展示位置的字号要统一。
(六)透过现象看本质
想说这一问题好久好久了,总是想说,但是又是没啥说的。今天就算是开发技巧之中的一点点心得来说吧。
老掉牙的故事:当你难以做决定的时候就抛硬币,不是让硬币决定命运,而是在你抛出的一瞬间你就知道了你想要的。
是的,人就是这样。但是,现实,是一场没有回放的电影,每个人都是主角。如果你抛出的硬币一直在转,那你想要的答案是什么呢?
同样的,作为开发者,你需要明确知道自己想要的是什么。
就拿新浪微博来说事吧。一个新浪客户端,是给谁用,怎么用,以及有什么用。从最基本的说起,客户端,基本功能就是与服务器交互。这个在之前的几篇中说的已经差不多了,虽说不能靠几篇文章就能把微博做的出神入化,但是入门应该还是勉强够了的。现在主要说说现象与本质。
现象——基于网络的客户端。
本质——技术?功能?营销?
每一个客户端,都有自己的独道之处。像新浪官方的客户端,它作为官方的客户端,要做到最全的功能,所以作为新浪微博控,官方客户端就必装(再没有更好的出现之前)。这也就决定了官方的客户端不能更精简,连一个功能都不能少。
而在开发的时候,目的到底是什么?
有的人,就是为了做一个新浪客户端,好了,想特色功能,最大程度的精简,最佳的用户体验,至少要提供必备的功能。然后去和各种新浪客户端比拼。
有的人,看中的是新浪的用户群体,好了,开始做新浪同步,给新浪微博带来流量,其实还是想达到双赢。
有的人,想借助微博的走势,做一些小工具,来打响自己的名气。
所以在借鉴一款产品的时候,先要认清这款产品是否是适合自己的。天天看总结,总结也是人总结的,与人有关,就与人的经历有关,并不是所有人的经历都是一样的,所以也并不是所有人的总结都是适合自己的。
想做好,难。想达到目的,更难。先认清自己的实力,优势,才能去竞争。模仿,也要找对人。
所以对于准备,或已经开始做类似客户端的朋友们,我有几条建议,当然是我经过没多久的工作经验,以及极少的经历总结出来的,有不好的地方还请别见怪。
1.问自己一个问题,你想要什么。
2.第二个问题,如果你还剩一天的时间,你会去做什么。
前两道题,我的答案是,我想要做一个人人都喜欢用的客户端。第二个问题是,我会用剩下所有的时间去做这个客户端。
3.坚定的信心,好的产品,不需要朝三暮四。
4.要有取舍。冲准了一个点钻,远比你哪个点都要做强的多。
5.善于自己总结。别人的总结,总是会有个人色彩在里面,借鉴可以,完全听取,注定了你将在几十亿人口中找与自己相同经历的人。要是有数据分析,你不看,那么你就是在等和你一样经历的人找你。
6.不要小看任何对手。也不要神化对手。
7.技术永远都不是最大的难题,难题在于你如何使用技术。
8.时间永远是最宝贵的,不要浪费。
9.正确的认识gui ,叫美工不适合,叫美术也不适合,叫设计还是不适合,反正记住一点,设计,并不是只是会画画的,程序一样是设计。
10.在评价别人的作品之前,要给别人足够的尊重。这样,才能与人形成良好的交流,三人行,必有我师啊。
11.保持良好的状态,注意身体健康,佣有足够的睡眠,拼,要拼的有技巧,耗,是解决不了问题滴。。
12.不管是个人还是公司,一个人作战真的很累,你需要共同拼搏的伙伴。
13.平日里要做的3件事:学习,学习,还是学习。
(七)后台上传
这里说的后台上传当然不是真的后台上传,只是在开启程序的情况下不影响操作的上传。基本的思想就是开启一个线程,不断的处理上传操作。这里用队列比较方便一些。
主要做以下几步:
第一步:实现一个自定义类,继承
@ : {
*;
- (void):( *)_dic;
- (void):( *)_dic;
@ (,) *;
@end
@
@ ;
- (id)init
self = [super init];
if (self) {
self. = [[ alloc] init];
[self. :2];//设置同时进行的线程数量,建议为2。
self;
- (void):( *)_dic
//保存上传列表到
* = [ ];
if ([ :@""] == nil) {
*dic = [ :[ :_dic],@"",nil];
[ :dic];
[ ];
else {
*array = [ :@""];
*arr = [[ alloc] :array];
[arr :_dic];
[ :arr :@""];
[arr ];
[ ];
//添加到队列
n* theOp = [[n alloc] :self
:@(:) :_dic];
[self. :theOp];
[theOp ];
//更新UI
- (void):( *)_dic
//从读取数据,并添加到队列
* = [ ];
*array = [ :@""];
*arr = [[ alloc] :array];
int i = (int)[arr :_dic];
if (i == ) {
;
*dic = [ nary:[arr :i]];
[dic :[ :0] :@"state"];//0 等待上传 1 上传中 2 失败
[arr :i :dic];
[ :arr :@""];
[ ];
n* theOp = [[n alloc] :self
:@(:) :dic];
[self. :theOp];
[theOp ];
//更新UI
- (void):(id)_obj
//实现上传方法
//成功调用
[self :@(:) :nil :YES];
//失败调用
[self :@(:) :nil :YES];
- (void):(id)_obj
//失败的任务更改状态之后保存
*tdic = [_obj ];
*dic = [ nary:tdic];
[dic :[ :1] :@"state"];
* = [ ];
*array = [ :@""];
*arr = [[ alloc] :array];
int = (int)[arr :dic];
if ( != ) {
*tmp = [ nary:[arr :]];
[tmp :[ :2] :@"state"];//0 等待上传 1 上传中 2 失败
[arr : :tmp];
[ :arr :@""];
[arr ];
[ ];
//更新UI
- (void):(id)_obj
//成功的任务从中删除
*tdic = [_obj ];
*dic = [ nary:tdic];
[dic :[ :1] :@"state"];
* = [ ];
*array = [ :@""];
*arr = [[ alloc] :array];
int = (int)[arr :dic];
if ( != ) {
[arr :];
[ :arr :@""];
[arr ];
[ ];
//更新UI
- (void)
[ ];
[super ];
@end
第二步:在中初始化一个对象,添加的时候用(),重新开始的时候用()。
第三步:在程序重新开启的时候启动队列。同时有一点不确认,就是从后台返回貌似队列会继续进行,也就是说进入后台的时候队列处在等待状态。
需要注意的几点:
n的 方法只是设置一个标识,在进入队列的时候判断是否为 true,从而判断是否进行。如果想取消 n,可以尝试继承它。避免无限重试。