[iOS]给UIWebView头尾插入自定义View

2012-2-25 评论(4) 分类:技术文章 Tags:

src与demo:https://github.com/bang590/iOSPlayground/tree/master/TWebview

经常有这样的需求:需要给webview头部加个自定义view,这个view不是固定的而是要跟webview一起滚动。例如iMail里的读邮件页面,头部有收件人等信息,下面是一个webview。如浏览器,顶部的地址栏要随页面滚动。如阅读类软件,需要给正文上面加一个标题。

实现方法可以通过设webview里scrollView的contentInset,在头部留好自定义view的空间,再把view添加进来,随着webview的滚动缩放细调这个view的位置。这里把这些行为封装了起来做成一个扩展的UIWebView,主要解决的问题是:

1.直接通过设headerView和footerView完成头尾view的插入。
2.webView滚动和缩放时自动调整headerView和footerView的位置,使之看起来自然。
3.提供接口实现头部高度改变时进行动画

有个问题未解决:

设置contentInset后,webview顶点的Y坐标不是0,而是小于0的一个数(负的headerView的高度) 。
在webview跳转页面时,会自动滚动到(0,0)位置,也就是跳到webview内容的顶部,也就是这时候headerView被隐藏了。

从调用堆栈来看这次跳转是私有的UIWebDocumentView调起,无法截获阻止。只能在触发scrollViewDidScroll时让它跳回去。但还没找到方法判断什么时候该跳什么时候不该跳。

[iOS]自动添加NavigationController自定义按钮动画

2012-2-21 评论(0) 分类:技术文章 Tags:

问题

UINavigationController顶部的返回按钮在切换视图时有左右滑动的动画(效果见iMail),但在自定义了这个按钮后(通过设self.navigationItem.leftBarButtonItem自定义),切换视图时按钮没有了动画。

最初解决方法

在每个viewController的viewWillAppear和viewDisappear方法上手动让按钮左右动。由于每个视图出现/消失的方向都有左/右两种,在这里难以判断,需要各种变量辅助,实现十分恶心,中间需要新加view时逻辑还要重新修改,被折腾。

最终解决方法

继承UINavigationController,重写pushViewController和popViewController方法,在这两个方法里面获取NavigationController里的view栈,因为push和pop总是操作最前面的View,所以可以从栈里知道哪两个view是要做动画的。调用这些view相应的方法使他们让自己的按钮动。这些方法可以写在UIViewController的扩展里,不用修改每一个viewController。

换言之,把下面的代码加入项目里,把项目里的UINavigationController换成下面的类,那些自定义的返回按钮就会自动做动画了。

代码

https://github.com/bang590/iOSPlayground/tree/master/NavigationButtonAnimate
(更多…)

[iOS]iOS开发初学者记录

2012-2-6 评论(4) 分类:技术文章 Tags:

做完了项目,想总结下,不知怎么写合适,只列出一些iOS开发的基础点,开发前因为不知道这些点,导致走了一些弯路,希望对初学者有帮助。

基础篇

为什么对一个变量release后还要设为nil

对一个变量release后,这个变量指向的内存释放了,但这个变量本身没变,仍指向原来的内存地址。若这个变量在释放后被访问,或者被重复release,就会导致应用崩溃。设为nil后这个变量指向0x00,可以保证程序以后访问不到原先的内存地址,对nil进行release也没任何问题。

使用类成员时,前面加不加self.有什么区别

不加self.调用的是成员本身,加self.后实际上调用了其成员的get set方法。
例:

//.h
@property (nonatomic, retain) NSString *name

//.m
name = @"bang";  //没有retain,随时会被释放
NSString *str = self.name  //等于NSString *str = [self name];
self.name = @"bang"; //等于[self setName:@"bang"]; 这时在set方法里retain了这个字符串

技巧篇

内存泄漏

可以通过xcode的编译工具Product-Analyze检查函数块范围内可能的泄漏点(外带会提示一些可能有的错误)。

用leaks工具监测出来的泄漏查找方法是跟踪其代码提示中出现的变量,经常这个变量是在提示的调用堆栈以外的地方泄漏的。若实在查不到,最终办法是重写这个变量的retain和release方法,debug,从调用堆栈看是谁retain了它而没有release。

要注意的是,用CFXXCreate(例如CFArrayCreate)生成的变量要用CFRelease释放。

数据存储

如无搜索需要,可以将一个数据对象直接序列化后存到sqlite,取出时直接反序列化为对象使用。序列化需要数据类实现NSCoding协议,实现encodeWithCoder和initWithCoder两个方法就行,若有多个数据对象,可以写个基类实现这两个方法,并在这里面利用反射枚举自身所有变量去encode和decode,一劳永逸,具体实现网上找找就有了。

组件篇

UINavigationController头尾显示隐藏

在用NavigationController去管理view的push和pop时,需要根据不同的view设置是否显示NavigationBar和ToolBar,一开始在错误的地方设置了,导致有时该显示NavigationBar和ToolBar时不显示的情况,后来发现在viewWillAppear上设置万无一失。别笑我土鳖,没好好去理解它整个流程,一直没发现。

- (void) viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    [self.navigationController setToolbarHidden:NO];
    [self.navigationController setNavigationBarHidden:NO];
}

UITableView游标式渲染

tableView的机制大概是:先定好总行数,某一行滚入视图范围时,回调一个函数去取view出来显示。这一行滚出视图再滚入时仍会继续回调这一函数取view。有这样的机制就是说无论你table里的数据有多少,都可以全部放入table中不用分页,因为不用一次性把所有数据都取出来,只在需要显示的时候根据游标去取对应的数据就行了。

可能这是APP组件很自然的方式不用说明,但在web上页面上的数据和元素都是要一次性载入内存的,做久了web,一开始没想到它这样的实现机制,导致我们走了不少弯路。

UIWebView渲染范围

UIWebView不是根据可视范围决定每次的渲染范围,而是根据自身控件的frame大小决定。

曾尝试webview嵌在tableview里,为了让webview跟tableview一起滚动,把webview的大小设为webview里的内容大小,让webview不出滚动条,从而能跟着tableview的滚动条一起滚。这样做的后果是每次webview都一次性渲染整个页面,内存占用多性能很差,而且在放大缩小这个webview时,渲染放大的整个页面更吃力,出现不能忍受的性能。解决办法是让webview定住高度为一整屏iphone的高度,限制了webview每次的渲染范围为可视范围,性能大好。带来的问题是无法随tableview滚动,但可以以其他方式优化体验。最近看到新版的ZAKER也是这样做的。

个人感觉篇

界面布局调整非常麻烦,让人怀念web了。界面描述方法XIB感觉晦涩难学,至今不会,没有CSS+HTML来得方便。

有编译器把关,少了像写js时多写or写错一个字符查半天的问题。

Object-C写起来各种变量函数和变量调用很长,没有js的短小精悍来得爽。

第一次编写涉及手动内存管理的程序,挺有意思,没想象中难,但有些内存管理导致的bug很难查。

虽然APP不像web那样随时更新,但也不像传统PC客户端升级那么麻烦,用户更新意愿更强,还是适合快速迭代的。

细节是可以决定成败,但得看你把什么定成细节。

最后,0bug的程序不存在,极致是把最主要的事做好。done is better than perfect。

为什么有困难不愿找警察

2012-2-5 评论(7) 分类:生活

去姐家吃饭,途径一摊位,写明移动联通电信充100送100,手机卡快没钱了,遂停下询问,他们说他们是通信公司的,现在有充100送100活动,移动联通电信手机卡都可以。我很笨,没戒心没细问,因为他的说辞跟校园里那些充值优惠活动一样啊。给两个手机号都充了,结果发现他是类似200卡的网络电话,钱是充进他们公司的不是手机卡里的,只能通过拨打他们的电话再输要拨的号码再挂机再等它来电才能接通电话,而且因为是网络电话,延迟很大。非常生气,理论了很久,不给退款,我说是诈骗,他说怎么会是诈骗我们有公司有营业执照打电话查询也查到账户里有200元,说我没问清楚这钱是不是充到移动的,那是我的事。周旋了半个钟无果,只能走了。

任何人都可以随便架一个服务器跟运营商内部的人秘密合作搞网络电话中转中心,成本很低,上街搞这些活动半骗着让人充值,充完确实有钱确实能打电话,对方就算觉得有被骗的感觉,也没办法,因为金额小,也不想再浪费时间惹麻烦,想着至少这100块还能用,事实是这些公司捞够钱后把后端的服务器一撤,就什么都没有了,电话肯定也是打不了了,什么责任都不用付。

如果他们一开始说这钱充完后是要怎样怎样打电话,不是充进移动卡的,然后我们买,那是完全没问题。问题是一开始就以技巧模糊地让我们觉得这跟普通移动充值没什么区别,只要没经验或不细心询问问到点子上就不会发现。事后他只要说你没问就行了,客户回想起来还只能怪自己,我觉得这就是诈骗。运营商跟这些公司是没有合作关系的,他们也痛恨这些做法,但太分散也难处理,若打电话问运营商客服,会被告知他们也知道这些行为违规,但也拿他们没办法,建议报警处理。

我是趋于不惹事不报警,但最后姐夫说吞不下这口气,报了警,经一小时处理后接受调解取消报警,对方还了钱了事。

说说为啥面对这些小额被骗的情况大多数人不愿报警。

1.畏警察

警察的形象怎样?不是全心全意为人民服务,而是能不接触就不接触,谁知道你遇到的警察是什么类型的,会不会反过来找你麻烦,公安局派出所这样的地方肯定是能不去就不去,最好一辈子都不跟他们打交道。

2.无所谓

以和为贵,报警给人感觉是很大件事的,一两百块的小问题不好意思麻烦警察局,想想也就算了。

3.怕麻烦,费时间

我报警时本来以为阿sir过来调解一下就完事了,没想到他就要求大家交身份证,收档口,一起到公安局走一趟。到了后还要等他整理资料半小时,最后我们是和解了取消报警了还简单些,否则就要录口供走流程什么的,极其麻烦和费时。

4.怕没用

你不知道这里的潜规则是什么,你会怀疑他们是否已经做好了公安那边的工作,说白了就是不信任警察,报警也没用。

5.怕报复

你找人麻烦,不给他好过,你又不知道对方的习性,怎么知道是不是黑社会,会不会仇恨心太强找你报复,你多少会有一些资料在他手上,进公安局的时候要登记身份证手机甚至家庭地址,只要他们想那是能报复到的,有这个可能性就足以让人畏缩。

最后说下,其实充话费的那三个人,除了其中一个比较叼外,另外两个还是比较和善的。阿sir们也挺不错,都挺友好的,会跟你聊聊天,说是学校刚毕业的吧,这些摆摊充话费的骗人的很多,一般人都不会信,但把他们赶走了下次还是再来,没用。