Index

转 iOS高效开源库集合

偶然看到的收藏之

网络篇:

为啥要用第三方开源库?因为系统API废话太多,用起来太啰嗦。

HTTP请求看这些:

  • AFNetWorking: 个人认为是首选,轻量、易用,开发者也在很积极的维护,拥护者也多,新的项目,就直接用它吧! https://github.com/AFNetworking/AFNetworking
  • MKNetworkKit: 用的人也不少,也是非常轻量级,使用起来比较简单。https://github.com/MugunthKumar/MKNetworkKit
  • ASIHttpRequest: 一句话,过时了。开发者已经很久没有维护了,而且用起来有点重。

Socket通信看这个:

  • CocoaAsyncSocket: 如果要使用socket进行通讯,那么肯定就用他了,支持TCP和UDP,支持GCD,支持OC接口,封装的非常不错。 https://github.com/robbiehanson/CocoaAsyncSocket

网络相关工具看这个:

  • Reachability: 用来判断和监听网络状态和变化,非常好的辅助工具库,必用。 https://github.com/tonymillion/Reachability

JSON解析篇:

  • 如果是支持iOS 5以上的系统,那么就直接使用系统提供的NSJSONSerialization吧,性能好,官方提供,可靠! 如果要用第三方的,那就选择JSONKit吧,性能好,源码就2文件,看着都舒服。 SBJson就千万别再用了,性能差,维护的也不好。

UI框架篇:

提升到框架层面,一般都比较重了,选择的时候需要慎重了,因为这个对于你的APP未必是必须的。

  • AsyncDisplayKit:强烈推荐一试。 Facebook刚刚开源发布的UI框架,被用于Facebook自家的应用Paper中。这个框架能给你带来的核心收益,是能够提高UI的流畅性,降低UI的响应时间。这是通过对UIKIt的进一步的异步封装来实现的,AsyncDisplayKit的UI元素都是线程安全的,可以在并行异步的进行操作,这样就可以让图像解码等耗时操作移出UI线程,为UI主线程减负。值得一提的是,AsyncDisplayKit的组件封装,与UIKit很类似,可以直接进行替换,接入门槛相对比较低。https://github.com/facebook/AsyncDisplayKit
  • ReactiveCocoa:如果你对响应式编程模式有兴趣,可以尝试一下这个框架,毕竟一般APP,大部分时间是在等待事件发生,等待用户操作或者网络请求返回,所以这种事件驱动模式,还是很适合的。https://github.com/ReactiveCocoa/ReactiveCocoa
  • bee Framework:国产mvc框架,将许多核心逻辑注入到了NSObject基类里,同样也是采用了响应式的设计。 https://github.com/gavinkwoe/BeeFramework
  • nimBus:Three20的作者之一开发的一套轻型框架,很多思路和Three20相似,但是轻量很多,支持命名空间,内部耦合性没那么高,很多人把他作为Three20的替代品。
  • Three20: 曾经的辉煌,Facebook自己也早已不维护,不要再使用了。

UI控件篇:

  • SDWebImage : 网络图片加载库,可以自动的管理图片的下载、缓存、处理、展示,就像使用本地图片一样简单的使用网络图片,非常好用,神器! https://github.com/rs/SDWebImage
  • MBProgressHUD: 优雅的进度显示效果,提供了很多动画效果,常备利器。 https://github.com/jdg/MBProgressHUD
  • SVProgressHUD: 和MBProgressHUD的功能效果类似,不过使用他不需要使用协议,也不需要声明实例,直接类方法调用即可。 https://github.com/samvermette/SVProgressHUD
  • MWPhotoBrowser: 一个完整的图片浏览器组件,效果和iOS自带的相片app 差不多,能滑动切换图片,点击隐藏导航条,缩放,分享,复制等。既可以管理本地图片,也可以查看网络图片,并且自带缓存策略。 https://github.com/mwaterfall/MWPhotoBrowser
  • HPGrowingTextview:增强的文本输入框,实现了增加多行文本和减少文本时输入框的伸缩和滚动效果。
  • AKSegmentedControl:可自定义外观的 Segmented Control, 包括背景颜色、segment上显示图片或者文字、按键模式等。 https://github.com/alikaragoz/AKSegmentedControl

  • EGOTableViewPullRefresh: 解决下拉刷新和上提加载更多的交互效果。 https://github.com/enormego/EGOTableViewPullRefresh

存储篇:

  • 喜欢使用 sqlite的,可以试试fmdb,封装的不错,简洁易用。 https://github.com/ccgus/fmdb
  • 如果要用CoreData, 那么用一下MagicalRecord,它降低了使用coredata的门槛。https://github.com/magicalpanda/MagicalRecord
  • 如果要使用keyChain存储敏感数据,那么可以用SFHFKeychainUtils,使用非常简单。 https://github.com/kamiro/SFHFKeychainUtils

动画篇:

  • pop: Facebook发布的大名鼎鼎的动画库(动画引擎),支持多种常见动画效果,包括弹性和衰减,也可以自定义效果,最关键的是,其接口语法上和Core Animation非常相似,就像是重新实现了系统的Core Animation,所以上手容易。https://github.com/facebook/pop
  • Canvas :可以认为就是个动画集合,提供了对Core Animation进一步的封装。 https://github.com/CanvasPod/Canvas

工具篇:

  • 日志框架: 用用 CocoaLumberjack吧,支持log分级,支持日志记录方式(控制台、文件、服务器),而且性能不错,可以简单的替换NSLog。 https://github.com/CocoaLumberjack/CocoaLumberjack
  • 数据加密和摘要: CocoaSecurity 提供了常见的AES加解密,sha1、md5系列的摘要算法。 https://github.com/kelp404/CocoaSecurity
  • zip压缩和解压: ZipArchive,非常好用,打包上传和下载的时候经常需要用到。
  • 文字效果: DTCoreText 是一个功能强大的文字效果库,可以在UITextView上实现包括文字大小、颜色、字体、下划线、链接、间距等效果。 https://github.com/Cocoanetics/DTCoreText
  • DTFoundation: 这是个强大的工具集合,百宝箱啊,里面有异步文件删除、目录监控、base64编码、日志、版本管理,以及对NSArray、NSDictionary、NSString、NSURL等系统类的一些方便使用扩展,还有对gif动画的支持和HTML解析,以及UIWebView、UIImage、UIView等UIKit的扩展和一些UI增强组件。 https://github.com/Cocoanetics/DTFoundation
  • MAZeroingWeakRef:非常棒的智能指针工具,可以自动销毁对象分配的内存,防止内存泄漏,方便内存管理。https://github.com/mikeash/MAZeroingWeakRef
  • TMCache: 一个不错的基于key/value的数据缓存库,支持内存和持久缓存,基于GCD支持多线程操作,挺好用。 https://github.com/tumblr/TMCache

扩展

94个iOS开发资源推荐,帮你加速应用开发

GitHub 上排名前 100 的 Objective-C 项目简介

加载网络图片到UIImageView中并适配retina屏

本文出自这里

UIImageView中加载一个网络图片有一些需要注意的地方。因为retina屏使iPhone的屏幕的基础坐标系系统有一个像素到点的转换。以 iPhone 5为例,像素是640×1136 而点是320×568。这中间有一个2倍的缩放系数,即[UIScreen mainScreen].scale。像素和点的关系是pixel = point × scale

还是以iPhone 5为例,假如有一张图片占据整个屏幕,并且图片命名有@2x后缀,那么这张图正好适应这个全屏的UIImageView并且不需要按照系数缩放。但是,如果这个图片的命名没有@2x后缀,iOS会放大这张图片两倍。而在加载网络图片的时候正是这个问题,因为图片名木有@2x后缀。

下面是解决方法的代码,为了使用的方便,写成了一个category

@interface UIImageView (URLImage)
- (void)loadRetinaImage: (NSString*) url;
@end

@implementation UIImageView (URLImage)
- (void)loadRetinaImageWithURL:(NSString*)urlStr {
    NSURL *url = [NSURL URLWithString:urlStr];
    NSData *data = [NSData dataWithContentsOfURL:url];

    if (data == nil) {
        NSLog(@"Failed to load data from URL: %@", urlStr);

        return;
    }

    UIImage *image = [UIImage imageWithData:data];

    //Resample the image to be for retina
    image = [UIImage imageWithCGImage:[image CGImage] 
                                scale:[UIScreen mainScreen].scale 
                          orientation:UIImageOrientationUp];

    self.image = image;
}
@end

这个方案的重点是 [UIImage imageWithCGImage]。 这个方法会设置图片为retina,等价于图片命名有@2x后缀。

转 UIWindow & UIWindowLevel详解

看到这篇blog记录下来

一、UIWindow是一种特殊的UIView,通常在一个程序中只会有一个UIWindow,但可以手动创建多个UIWindow,同时加到程序里面。UIWindow在程序中主要起到三个作用:

  • 作为容器,包含app所要显示的所有视图
  • 传递触摸消息到程序中view和其他对象
  • 与UIViewController协同工作,方便完成设备方向旋转的支持   

    二、通常我们可以采取两种方法将view添加到UIWindow中:

  • addSubview

直接将view通过addSubview方式添加到window中,程序负责维护view的生命周期以及刷新,但是并不会为view添加对应的ViewController,因此采用这种方法将view添加到window以后,我们还要保持view对应的ViewController的有效性,不能过早释放。   

  • rootViewController

rootViewController时UIWindow的一个遍历方法,通过设置该属性为要添加view对应的ViewController,UIWindow将会自动将其view添加到当前window中,同时负责ViewController和view的生命周期的维护,防止其过早释放   

三、WindowLevel

  UIWindow在显示的时候会根据UIWindowLevel进行排序的,即Level高的将排在所有Level比他低的层级的前面。下面我们来看UIWindowLevel的定义:

const UIWindowLevel UIWindowLevelNormal;
const UIWindowLevel UIWindowLevelAlert;
const UIWindowLevel UIWindowLevelStatusBar;
typedef CGFloat UIWindowLevel;

       IOS系统中定义了三个window层级,其中每一个层级又可以分好多子层级(从UIWindow的头文件中可以看到成员变量CGFloat _windowSublevel;),不过系统并没有把则个属性开出来。UIWindow的默认级别是UIWindowLevelNormal,我们打印输出这三个level的值分别如下:

2012-03-27 22:46:08.752 UIViewSample[395:f803] Normal window level: 0.000000  
2012-03-27 22:46:08.754 UIViewSample[395:f803] Alter window level: 2000.000000  
2012-03-27 22:46:08.755 UIViewSample[395:f803] StatusBar window level: 1000.000000

  这样印证了他们级别的高低顺序从小到大为Normal < StatusBar < Alert,下面请看小的测试代码: TestWindowLevel

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
    self.window.backgroundColor = [UIColor yellowColor];
    [self.window makeKeyAndVisible];
    
    UIWindow *normalWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    normalWindow.backgroundColor = [UIColor blueColor];
    normalWindow.windowLevel = UIWindowLevelNormal;
    [normalWindow makeKeyAndVisible];
    
    CGRect windowRect = CGRectMake(50, 
                                   50, 
                                   [[UIScreen mainScreen] bounds].size.width - 100, 
                                   [[UIScreen mainScreen] bounds].size.height - 100);
    UIWindow *alertLevelWindow = [[UIWindow alloc] initWithFrame:windowRect];
    alertLevelWindow.windowLevel = UIWindowLevelAlert;
    alertLevelWindow.backgroundColor = [UIColor redColor];
    [alertLevelWindow makeKeyAndVisible];
    
    UIWindow *statusLevelWindow = [[UIWindow alloc] initWithFrame:CGRectMake(0, 50, 320, 20)];
    statusLevelWindow.windowLevel = UIWindowLevelStatusBar;
    statusLevelWindow.backgroundColor = [UIColor blackColor];
    [statusLevelWindow makeKeyAndVisible];
    
    NSLog(@"Normal window level: %f", UIWindowLevelNormal);
    NSLog(@"Normal window level: %f", UIWindowLevelAlert);
    NSLog(@"Normal window level: %f", UIWindowLevelStatusBar);
    
    return YES;
}

运行结果如下图:

我们可以注意到两点:

  • 我们生成的normalWindow虽然是在第一个默认的window之后调用makeKeyAndVisible,但是仍然没有显示出来。这说明当Level层级相同的时候,只有第一个设置为KeyWindow的显示出来,后面同级的再设置KeyWindow也不会显示。
  • statusLevelWindow在alertLevelWindow之后调用makeKeyAndVisible,淡仍然只是显示在alertLevelWindow的下方。这说明UIWindow在显示的时候是不管KeyWindow是谁,都是Level优先的,即Level最高的始终显示在最前面。

UITextFieldDelegate

最近业务需求要做一个TextField的号码输入格式化,如电话号输入的时候344分组插空格。对UITextField的委托方法又熟悉了一下,每次要用的时候,都要看一遍,对基础掌握不够牢固啊,所以记下来,以后好查找。

UITextFielddelegate委托方法注释:

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{  
    //返回一个BOOL值,指定是否循序文本字段开始编辑  
    return YES;  
}  

- (void)textFieldDidBeginEditing:(UITextField *)textField{
    //开始编辑时触发,文本字段将成为first responder
}

- (BOOL)textFieldShouldEndEditing:(UITextField *)textField{
    //返回BOOL值,指定是否允许文本字段结束编辑,当编辑结束,文本字段会让出first responder
    //要想在用户结束编辑时阻止文本字段消失,可以返回NO
    //这对一些文本字段必须始终保持活跃状态的程序很有用,比如即时消息
    return NO;
}

- (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
    //当用户使用自动更正功能,把输入的文字修改为推荐的文字时,就会调用这个方法。
    //这对于想要加入撤销选项的应用程序特别有用
    //可以跟踪字段内所做的最后一次修改,也可以对所有编辑做日志记录,用作审计用途。   
    //要防止文字被改变可以返回NO
    //这个方法的参数中有一个NSRange对象,指明了被改变文字的位置,建议修改的文本也在其中
    return YES;
}
- (BOOL)textFieldShouldClear:(UITextField *)textField{
    //返回一个BOOL值指明是否允许根据用户请求清除内容
    //可以设置在特定条件下才允许清除内容
    return YES;
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
    //返回一个BOOL值,指明是否允许在按下回车键时结束编辑
    //如果允许要调用resignFirstResponder 方法,这回导致结束编辑,而键盘会被收起
    [textField resignFirstResponder];//查一下resign这个单词的意思就明白这个方法了
    return YES;
}

NSURLProtocol

NSURLProtocol的使用讲解

开发iOS的应用一定离不开网络。iOS的常用网络框架主要分为两种:基于NSURLConnection和基于CFNetwork。基于NSURLConnection的有apple原生和AFNetworking等,基于CFNetwork框架有ASIHttpRequest等。而NSURLProtocol能够拦截处理基于NSURLConnection的一切网络请求和UIWebView,但是对于基于CFNetwork的网络请求没有作用。

什么是NSURLProtocol?

NSURLProtocol is an abstract class which provides the basic structure for performing protocol-specific loading of URL data. 它是一个抽象类,为载入URL的data的一些特定协议提供基础的结构。

NSURLProtocol是URL加载系统中的的协议支持部分,它功能强大却晦涩。它是一个抽象类,在使用的使用必须子类化来实现新的或者定制已经存在的URL加载行为,实现自己业务的需求。

NSURLProtocol的使用举例

  • 拦截图片加载请求,转为从本地文件加载
  • 为了测试对HTTP返回内容进行mock和stub
  • 对发出请求的header进行格式化
  • 对发出的媒体请求进行签名
  • 创建本地代理服务,用于数据变化时对URL请求的更改
  • 故意制造畸形或非法返回数据来测试程序的鲁棒性
  • 过滤请求和返回中的敏感信息
  • 在既有协议基础上完成对 NSURLConnection 的实现且与原逻辑不产生矛盾

NSURLProtocol的核心思想是 通过使用它,不用改变网络调用的其他部分,就可以改变URL加载行为的全部细节。简言之,NSURLProtocol就是一个苹果允许的网络调用的中间人。

NSURLProtocol的使用方法

使用NSURLProtocol必须先子类化,否则不能使用。

注册自定义的子类,代码如下

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    //注册protocol
    [NSURLProtocol registerClass:[CustomURLProtocol class]];
    return YES;
}

子类化时要override以下方法,逻辑顺序如下:

  • +canInitWithRequest: 这个方法是告诉protocol要控制什么样的网络请求。这个方法返回YES 该请求会被控制,否则以下的委托方法不会在被调用,直接进入下一步的处理。
  • +canonicalRequestForRequest: 这个方法是对请求的规范处理,这个规范就是自己想要的处理规范,如替换URL、修改URL的参数等。
  • -startLoading 和 -stopLoading 前者对应于loading开始从web抓取数据,后者是抓取数据完毕的回调。这两个方法的方法体处理的都是针对protocol客户端进行操作。在这两个方法内要做初始化self.connection等操作。
  • 每个NSURLProtocol的子类实例都有一个client属性,该属性对URL加载系统进行相关操作。它不是NSURLConnection,但看起来和实现了NSURLConnectionDelegate协议的对象非常相似。<NSURLProtocolClient>要实现的方法如下:
    • -URLProtocol:cachedResponseIsValid:
    • -URLProtocol:didCancelAuthenticationChallenge:
    • -URLProtocol:didFailWithError:
    • -URLProtocol:didLoadData:
    • -URLProtocol:didReceiveAuthenticationChallenge:
    • -URLProtocol:didReceiveResponse:cacheStoragePolicy:
    • -URLProtocol:wasRedirectedToRequest:redirectResponse:
    • -URLProtocolDidFinishLoading:

    这几个委托方法内要做的就是在恰当的时候让client调用每一个委托方法。

以上方法并不是全部可以overide方法,还有很多其他的方法,可以实现更加强大的功能。

具体的使用实例就在赘述的了,参看扩展阅读的 使用NSURLProtocol拦截替换http请求iOS开发之— NSURLProtocol 就会明白,其实很简单,就是按照使用方法 根据自己的需求重写若干个方法而已。

几点注意:

  • URLProtocol会被以注册顺序的反序访问,所以自己写的protocol比其他内建的protocol拥有更高的优先级。
  • 无论对webView还是native的,只要是基于NSURLConnection的网络请求的,可以根据相关特征进行URL拦截并处理。

扩展阅读

NSURLProtocol

NSURLProtocol与NSURLProtocolClient简介

iOS开发之— NSURLProtocol

使用NSURLProtocol拦截替换http请求

NSURLProtocol Tutorial