前言:摘录了很多网络中的大神的代码参考使用,由于这个文档编辑已久,很多代码找不到原作者,敬请原谅。
一、XCode基础使用
应用图标:icon.png (默认图标)
icon.png(iphone4之前),icon@2x.png(retina屏用),icon@3x.png(iOS8)
引导页面:default.png (默认引导页面)
Default.png(iphone4之前),Default@2x.png(iphone4s用),Default-568h@2x.png(iphone5,5s,5c retina),Default@3x.png(未知)
2013年年底前,icon图标要求切成圆角
2013年年后,icon图标必须切成方的尖角。否则不能上线
Images.xcassets—可改变图标和引导页面
第三方库的引用
方法一:直接复制所有源文件到项目中
这种方法就是把第三方类库的所有源文件复制到项目中,直接把所有.h和.m文件拖到XCode项目中即可。如果第三方类库引用了一些系统自带类库,那么在项目中还需要额外引用那些类库。
如果当前的项目启用了ARC,而引用的第三方类库未使用ARC,那还需要在项目信息的Targets – Build Parses里找到第三方类库的.m文件,并为它们加上-fno-objc-arc标记。
而对于在未启用ARC的项目用引用使用了ARC的第三方类库,则需要为第三方类库的.m文件加上-fobjc-arc标记。
另外,在源代码中可以通过一个编译器指令__has_feature(objc_arc)来检测项目是否使用了ARC,具体见。
方法二:引用.xcodeproj生成静态链接库并引用
首先,在XCode中把第三方类库的.xcodeproj文件拖到当前项目中;如果第三方类库封装了一些资源在.bundle文件里,那么.bundle文件需要和.xcodeproj一起拖到项目中。
然后,在项目的Targets – Summary – Linked Frameworks and Libraries或者在Targets – Build Phases – Link Binary With Libraries添加第三方类库生成的静态链接库引用。
接着,还需要在Targets – Build Settings – Search Paths的User Header Search Paths参数中加入第三方类库的头文件路径,可以是绝对路径如:/Users/libpath,也可以是相对路径(相对于当前项目文件夹)如:../**。
最后,有些静态链接库引用进来可能还需要增加一些标记,在Targets – Build Settings – Linking的Other Linker Flags参数中增加:-Objc, –all_load这一类标记。
通过以上几步,一般都可以成功编译。
当然还有一些例外的情况:
当前项目和第三方类库同时使用了另外的一些第三方类库,这个时候还需要额外做一些处理才能成功编译:在引用的第三方类库的.xcodeproj项目 – Targets – Build Phases中的Compile Sources和Copy Headers把重复的.m和.h文件移除掉。
第三方类库引用的一些系统自带类库,如果项目中没有引用,也可能会引起编译错误,这时还需要在项目中引用第三方类库引用的一些系统自带类库。比如:第三方类库引用了QuartzCore.framework,而项目中未引用QuartzCore.framework则可能引起编译错误,就需要在项目中也引用QuartzCore.framework。
二、OC常用方法
NSString
stringByAppendingString 拼接在字符串后
subStringToIndex/FromIndex/WithRange 从字符串头切/数字位置切到尾/NSmakeRange(切割位置和数量)
replaceCharactersInRange
deleteCharactersInRange
NSArray
compoenentsSeparatedByString查找
componentsJoinByString
NSCharacterSetcharacterSetWithCharactersInString
compoentsSeparatedByCharactersInSet
NSDictionary
removeObjectForKey
removeAllObjects
NSData
dataUsingEncoding:NSUTF8StringEncoding(Str——>Data)
[NSString alloc] initWithData:Data encoding:NSUTF8StringEncoding](Data——->Str)
[NSKeyedArchiver archivedDataWithRootObject:Array](Arr——>Data)
[NSKeyedUnarchiver unarchiverObjectWithData:Data](Data———>Arr)
NSDate
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];//一个日历对象
文件操作
[[NSBundle mainBundle] pathForResource
单例:类对象只能被实例化一次,值是不变的,并且一个类只能有一个对象
作用:可以通过被单例的对象进行传值。
写法:
static SingleClass *single = nil;
//1 做一个静态全局指针变量
@implementation SingleClass
+(SingleClass*)shareInstance
{
//2、调用GCD(多线程的写法)的once方法来只做单例
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//大括号中的代码在整个应用中只会被执行一次
//3、把上面的静态全局变量赋值,进行实例化
single = [SingleClass alloc];
});
//4、把静态全局指针变量返回
return single;
}
@end
三、UI基础控件
UIButton:button.titleLabel.text 找到按钮上的文字
UIImageView:UIImage imageNamed:读进内存,占用内存
UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"1_2" ofType:@"png" 现读文件 读取速度满
UILabel: lab.adjustsFontSizeToFitWidth = YES 文字大小适应宽度
lab.numberOfLines = 0 自动换行
UITextField:textField.autocorrectionType = UITextAutocorrectionTypeYes 自动更正英语单词
TextField.autocapitalizationType = UITextAutocapitalizationTypeAllCharacters 首字母大写
TextField.clearButtonMode = UITextFieldViewModeAlways一键删除
TextField.secureTextEntry = YES开启密文
TextField.returnKeyType = UIReturnKeyDefault Return键的类型
UITextField代理
-(BOOL)textFieldShouldReturn:(UITextField*)textField
[textField resignFirstResponder]摁Done输入框会消失
[(找到下一个输入框)becomeFirstResponder]摁Done下一个会进入编辑状态
-(void)textFieldDidBeginEditing:(UITextField*)textField
//开始编辑的时候,屏幕上移
{
self.view.frame = CGRectMake(0, -70, 320, self.view.frame.size.height);
}
当输入完成后在点击事件中:self.view.frame = CGRectMake(0, 0, 320, self.view.frame.size.height);让屏幕返回正常的高度,存在数组中
-(void)textFieldEditChange:(UITextField*)textField
{ 编辑内容改变会被调用,用于实时获取当前输入的内容
NSLog(@"%@",textField.text);
}
UITextView:文本输入框
代理-(void)textViewDidBeginEditing:(UITextView *)textView
{
UILabel *lab = (UILabel*)[self.view viewWithTag:2000];
lab.hidden = YES;
}
各种小控件
UIStepper :步进器maximumValue , stepValue(步进率) [addTarget EventValueChange]
UIProgressView:进度条progreee.progress (进度)
UISegmentedControl:单选器 selectedSegmentIndex 正被选中的下标
UISlider :滑动条maximumValue(默认1) value(滑动快的位置0-1)(根据value的变化以及upInSideDown添加按钮事件)
UISwitch:开关 UIActivityIndicatorView小转圈 (根据on的属性添加按钮事件)
UIActionSheet:上拉的菜单(代理- (void)actionSheet….)
UIAlertView:弹出警报 (代理 - (void)alertView…)
四、UI简单逻辑
1.ImageView的动画
UIImageView *animateImage = [[UIImageView alloc] initWithFrame:CGRectMake(20, 400, 60, 60)];
animateImage.animationImages = picArr;//把要动画的图片Image数组赋值
animateImage.animationDuration = 0.5;//动画持续时长,是一轮的时长
animateImage.animationRepeatCount = 0;//重复轮数
[self.window addSubview:animateImage];
[animateImage release];
//开始转
[animateImage startAnimating];
2.传值:
1、代理传值:(反向传值)
设置页面:1、声明协议
2、协议方法 协议属性
3、代理属性调用代理方法 self.delegate getXXX:要传的值
被设置页面: 1、遵循协议
2、 实现协议方法:把传进来的值给被设置VC的某个需要的地方
3、去第二个页面的点击事件中:实例化设置页面一个对象,然后把自己给设置页面的代理属性
2、单列传值:整个应用中都要用的数据,存一个全局的值
1、新建一个单列类,类的属性是要传的值
2、设置页面给单列的属性赋值,然后点击事件中self调用这个单列方法,并且将要传的值传过去
3、被设置页面:写一个单列方法,实例化一个单列类的对象,这个单列对象的属性的值就是刚刚传过来的值,将其赋给被设置页面需要用的某个属性或者用某个属性去接受传进来的值,然后由被设置页面slef调用下这个方法
3、缓存传值:常用于下次启动还有点数据账号和密码
设置页面:NSUserDefaults *user = [NSUserDefatlus stadardUserdefaults]; 实例化一个缓存的对象
[user setObject:[要传的值] forKey:@“title”];
[user synchronize];同步一下
被设置页面self调用下这个方法将要传的值传到缓存中
被设置页面:实例化一个缓存对象,用key查找传进来的值,然后将被设置页面的某个属性改变用传进来的值
4、通知传值:设置页面发送通知,被设置页面观察通知,被设置页面要在设置页面之前(缺点:占资源多)
设置页面:通知中心
通知内容
用中心post内容出去
被设置页面:传进来的参数 = [noti object] 发的时候传的参数就是这个noti 不论什么类型都用object解析,解析后发的是什么就是什么。 然后使用传进来的参数设置本页面
[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(notify:)name:等着接的通知名 截到通知用第一个参数执行第二个参数的方法
5、正向传值:用属性传值 A —>B B里设置个属性 A—>B的时候给B的属性赋值
3.NavBar TabBar
1、导航条
系统导航条:
self.navigationController.navigationBar 设置背景色等
左右按钮设定:
UIBarButtonItem *leftBtnItem = [[UIBarButtonItem alloc]initWithCustomView:自己创建得左btn]
self.navigationItem.leftBarButtonItem = leftBtnItem
将自己创建得左btn赋给系统得左btn就可以定义系统得navBar得Btn ,右btn同理
自定义导航条:就是自己写一个view,贴在(0,20,320,44)得位置,就是自定义导航条,封装起来代码复用(继承UIView)
步骤:1 隐藏系统导航条:self.navigationControlloer.navigationBarHidden = YES
2 自定义导航条 makeMyNav
2、tabBarController(集成UITabBarControlller)
系统Tabbar:最多只能显示5个按钮
自定义tabbar
隐藏:self.tabBar.hidden = YES
步骤:1 实例化VC
2 放进导航
3 导航对象放进数组
4 self.viewControllers = 数组 把数组赋给self.VCS 数组VC得顺序就是tab bar得顺序
5 制作UI
使用:实例化这个自定义tabBar
直接把根给这个tabbar得对象(此时不需要navBar)
4.UITabView代理(父类 UIScorllView)<UITableViewDataSource,UITableVIewDelegate>
dataSource和显示的内容有关,delegate和交互有关
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView ——> 返回段数
-(UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section ——>返回段内容
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section ———>返回行数
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath———>返回行内容
{
cell的复用方法:
1 准备复用标示字符串
2 用标示字符把table和cell做好关联
UITableViewCell*cell=[tableView dequeueReusableCellWithIdentifier:iden]
3 判断cell是否存在(存在——>实例化一个)
4 cell的内容复制布局
cell本身有2个属性cell.textLabel和cell.imageView(因此当自定义cell的时候不可以再使用imageView给变量命名)
5 返回cell
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath————>点击选中
-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath———>取消选中
还有一些段头高度heightForHeader 每行高度heightForRow等等非必须实现的代理方法
table的刷新方法:找到那个tableView————>[table reloadData] //刷新,每次调用这个方法,会整个重新执行一次这个table的所有代理方法
5.MVC基本思想
五、常用功能实现
1.ScollView中实现图片浏览
2.自定义搜索框的实现
思路: 一个BOOl值判断是否在输入 _isEdit
一个数据源数组 dataArr
一个搜索结果数组 resultArr
textfield代理:
开始编辑的时候:
- (void)textFieldDidBeginEditing:(UITextField *)textField
_isEdit = YES 开始编辑编辑状态变成YES
- (BOOL)textFieldShouldReturn:(UITextField *)textField
_isEdit = NO 结束编辑状态变成NO
dataArr当数据源刷新table reloadData
清空搜索框 和搜索结果数组
[textField resignFirstResponder]键盘缩回
return YES
- (void)textEditChinge:(UITextField*)text
实现实时搜索(这个方法不是代理方法,是自己写的)
清空上次的结果(removeAll resultArr 的内容)
遍历dataArr查找输入的字符串
如果!NSNotFound————>加入resultArr中
刷新table reloadData
tableView代理
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{ 判断输入框是否在编辑
if(_isEdit == NO)
{
return self.dataArr.count; 返回行数--原始的table行数
}
else
{
return self.resultArr.count;返回行数--搜索的table的行数
}
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{ 判断输入框是否在编辑
if(_isEdit == NO)
cell.textLabel.text=self.dataArr[indexPath.row];
else
cell.textLabel.text=self.resultArr[indexPath.row];
return cell;
}
3.上拉下拉刷新加载功能实现
六、网络请求与解析
七、易遗忘点与UI基础
NSArray *arr = [self.window subviews]获得self.windows的所有子视图
label.adjustsFontSizeToFitWidth = YES 文字大小适应label的宽度(文字长度超出不多用这个可以)
label.numerOfLine = 0 ;换行次数,0表示无限换行
label的大小适应所写入文字
NSDictionary *dic = @{NSFontAttributeName:[UIFont systemFontOfSize:16]}
CGRect rect = [要写入的字符串 boundingRectWithSize:CGSizeMake(最大宽度,最大高度) options:NSStringDrawingUsesLineFragmentOrigin attributes:dic context:nil]
把图片读进内存的两个方法
UIImage *image = [UIImage imageNamed:@"1_1.png"];//读进内存 加载快,占内存
UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"1" ofType:@"png"]];//现读文件 加载慢 一般加载大图使用这种方法,不占用内存
图片动画制作:
实例化N个UIImage对象—— > 把这些对象存进数组——>实例化UIImageView ——> (.animationImages = Arr;.animationDuration=0.5;表示一轮动画时长 .animationRepeatCount = 0重复几轮 startAnimating开始)
按钮是用类方法实例化的,不需要管理内存
numberOfPages = N N写几就是几,不同于numberOfLine等写着0代表无限,此处不适用
self.starView.contentMode = UIViewContentModeBottomLeft;//停靠模式
self.starView.clipsToBounds = YES;
//把UI变成圆角(添加quartzCore.framework)
//当前V上的子视图不会超越
searchBar.layer.masksToBounds = YES;
//设置圆角的角度(15 - 20就是个圆了)
searchBar.layer.cornerRadius = 5;
self.navigationController.navigationBarHidden = YES;
- (void)downLoad
{//打开其他应用,也可以打电话,发短信,邮件等。
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:self.myDic[@"itunsUrl"]]];
}
-给按钮设置边框
[btn2.layer setBorderWidth:0.5];
[btn2.layer setBorderColor:[[UIColor whiteColor] CGColor]];
八、常用第三方库
加载转圈: MBProgressHUD *mb = [[MBProgressHUD alloc]initWithView:self.view];
mb.dimBackground = YES;
mb.tag = 12345;
mb.labelText = @"超哥又在帮你加载....";
[self.view addSubview:mb];
[mb show:YES];
NSString+URLEncoding:URL去除非法字符
iCarousel:各种scrollVIew相册切换的特效。(需要添加libz.dylib systemCOnfigutation.framework,mobileCoreServices.framework)
ASIHTTPRequst:上传用
SDWebImage:从服务器下载图片时候用这个(需要这个-fobjc-arc)
FMDB:数据库 (需要添加库sqllite)
各库具体用法请等待超哥更新
九、相册图库邮件短信电话等使用(见Demo)
十、地图SDK(见Demo)
十一、分享SDK(见Demo)
十二、毛玻璃效果
为uiimage 绘制 毛玻璃效果
需要导入Accelerate.framework。
#import <Accelerate/Accelerate.h>
//加模糊效果,image是图片,blur是模糊度
- (UIImage *)blurryImage:(UIImage *)image withBlurLevel:(CGFloat)blur
{
if (image==nil)
{
NSLog(@"error:为图片添加模糊效果时,未能获取原始图片");
return nil;
}
//模糊度,
if ((blur < 0.1f) || (blur > 2.0f)) {
blur = 0.5f;
}
//boxSize必须大于0
int boxSize = (int)(blur * 100);
boxSize -= (boxSize % 2) + 1;
NSLog(@"boxSize:%i",boxSize);
//图像处理
CGImageRef img = image.CGImage;
//需要引入#import <Accelerate/Accelerate.h>
/*
This document describes the Accelerate Framework, which contains C APIs for vector and matrix math, digital signal processing, large number handling, and image processing.
本文档介绍了Accelerate Framework,其中包含C语言应用程序接口(API)的向量和矩阵数学,数字信号处理,大量处理和图像处理。
*/
//图像缓存,输入缓存,输出缓存
vImage_Buffer inBuffer, outBuffer;
vImage_Error error;
//像素缓存
void *pixelBuffer;
//数据源提供者,Defines an opaque type that supplies Quartz with data.
CGDataProviderRef inProvider = CGImageGetDataProvider(img);
// provider’s data.
CFDataRef inBitmapData = CGDataProviderCopyData(inProvider);
//宽,高,字节/行,data
inBuffer.width = CGImageGetWidth(img);
inBuffer.height = CGImageGetHeight(img);
inBuffer.rowBytes = CGImageGetBytesPerRow(img);
inBuffer.data = (void*)CFDataGetBytePtr(inBitmapData);
//像数缓存,字节行*图片高
pixelBuffer = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img));
outBuffer.data = pixelBuffer;
outBuffer.width = CGImageGetWidth(img);
outBuffer.height = CGImageGetHeight(img);
outBuffer.rowBytes = CGImageGetBytesPerRow(img);
// 第三个中间的缓存区,抗锯齿的效果
void *pixelBuffer2 = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img));
vImage_Buffer outBuffer2;
outBuffer2.data = pixelBuffer2;
outBuffer2.width = CGImageGetWidth(img);
outBuffer2.height = CGImageGetHeight(img);
outBuffer2.rowBytes = CGImageGetBytesPerRow(img);
//Convolves a region of interest within an ARGB8888 source image by an implicit M x N kernel that has the effect of a box filter.
error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer2, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
error = vImageBoxConvolve_ARGB8888(&outBuffer2, &inBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
if (error) {
NSLog(@"error from convolution %ld", error);
}
// NSLog(@"字节组成部分:%zu",CGImageGetBitsPerComponent(img));
//颜色空间DeviceRGB
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
//用图片创建上下文,CGImageGetBitsPerComponent(img),7,8
CGContextRef ctx = CGBitmapContextCreate(
outBuffer.data,
outBuffer.width,
outBuffer.height,
8,
outBuffer.rowBytes,
colorSpace,
CGImageGetBitmapInfo(image.CGImage));
//根据上下文,处理过的图片,重新组件
CGImageRef imageRef = CGBitmapContextCreateImage (ctx);
UIImage *returnImage = [UIImage imageWithCGImage:imageRef];
//clean up
CGContextRelease(ctx);
CGColorSpaceRelease(colorSpace);
free(pixelBuffer);
free(pixelBuffer2);
CFRelease(inBitmapData);
//CGColorSpaceRelease(colorSpace); //多余的释放
CGImageRelease(imageRef);
return returnImage;
}
参考:http://blog.csdn.net/rhljiayou/article/details/10232003 有效果图
http://www.zhihu.com/question/21260575 知乎上的讨论,比较全面
http://prolove10.blog.163.com/blog/static/138411843201391401054305/
UIImage 转 NSData
-(NSData *)getCoverImageDataWith:(NSString *)imgeUrl
{
NSURL *aUrl = [[NSURL alloc]initWithString:imgeUrl];
NSData *aData = [[NSData alloc]initWithContentsOfURL:aUrl];
if (aData==nil)
{
aData = [UIImagePNGRepresentation([UIImage imageNamed:@"image_default"]) retain];
}
SQRelease(aUrl)
return [aData autorelease];
}
掌游宝开发代码思想
新闻模块:
Model
子线程- 请求数据并解析数据
主线程- 执行代理讲数据返回给VC
参数- 保存在segmentInfo中
View
布局 -makeUI
传值 - refreshData(VC传进来)
ListView:布局好页面
delegate:选中某行的数据传到VC
makeUI newsCate:滚动导航条 (8个按钮60*30)
articleDetail:滚动的新闻列表
refresh:newsCate.contentSize
articleDetail.contentSize
btnClick:传回点击的按钮
AritleVIew:delegate:选中的cell,选中的图片 刷新数据 重新加载数据
makeUI:article单个table
headPicture:轮播图片
table.tableHeaderView = headPicture;
refreshData:读取article数组和picture数组 加载到本身的数组中 以及赴值操作
table代理:selected 代理传回index.row
MJ代理(下拉:清除数据源,下拉:重新加载)
segmentModel:
代理:senDataToVC
子线程:getUrl:获取网址 -url由VC传过来-prepare:解析-保存到info中 - 传到VC
ArticalModel:
代理:sendDataToVC
子线程:geturl:获取网址 - HUD
异步下载图片:
NSThread 和GCD
1.新线程下载图片,主线程更新视图
[NSThread detachNewThreadSelector:@selector(getImageByUrl:) toTarget:self withObject:array];
-(void)getImageByUrl:(id)object{
NSArray *param=(NSArray*)object;
NSString*imgUrl=[param objectAtIndex:0];
UIImage *image=nil;
NSArray*array=[imgUrl componentsSeparatedByString:@"/"];
NSString*pictName=[array objectAtIndex:[array count]-1];
NSString*imgPath=[NSString stringWithFormat:@"%@%@",BASE_API_URL,imgUrl];
NSURL *url=[NSURL URLWithString:imgPath];
NSData *data=[NSData dataWithContentsOfURL:url];
if (data) {
image=[UIImage imageWithData:data];
[self createImageFileLocal:pictName theData:data];//将图片数据缓存到本地
}else{
image=[UIImage imageNamed:@"xt"];//系统默认图片
}
UIImageView *view =[param objectAtIndex:1];
[view performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];//主线程更新视图
}
2.采用GCD的方式。代码如下
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
[self loadImage:SCROLL_IMAGE_ONE];//从网络上加载图片1
});
dispatch_group_async(group, queue, ^{
[self loadImage:SCROLL_IMAGE_TWO];//从网络上加载图片1
});
dispatch_group_async(group, queue, ^{
[self loadImage:SCROLL_IMAGE_THREE];//从网络上加载图片1
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{//三个图片全部加载完毕后更新视图
[self checkImagesArray];
});
多请求队列:
WebView:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
if ([webView.request.URL isEqual: @"链接地址"]) {
[self.navigationController popViewControllerAnimated:YES];
}
}
托盘抽屉式效果实现:
原理:托盘控制器设置为根导航控制器
托盘控制器放在左视图上。
左视图上按钮被点击-通知托盘,切换VC-改变Frame
QQ左右滑效果:单列一个左右切换的视图控制器
左视图,右视图,主视图全部都为self.bounds
手势检测-frame计算
图片缓存机制:
ASIDownloadCache
参考:http://blog.csdn.net/pjk1129/article/details/6574280
SDWebImage
1.异步加载
SDWebImageManager *manager = [SDWebImageManager sharedManager];
UIImage *cachedImage = [manager imageWithURL:url]; // 将需要缓存的图片加载进来
if (cachedImage) {
// 如果Cache命中,则直接利用缓存的图片进行有关操作
// Use the cached image immediatly
} else {
// 如果Cache没有命中,则去下载指定网络位置的图片,并且给出一个委托方法
// Start an async download
[manager downloadWithURL:url delegate:self];
}
2.实现协议 SDWebImageManagerDelegate
// 当下载完成后,调用回调方法,使下载的图片显示
- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image {
// Do something with the downloaded image
}
独立的异步图像下载
可能会单独用到异步图片下载,则一定要用downloaderWithURL:delegate:来建立一个SDWebImageDownloader实例。
downloader = [SDWebImageDownloader downloaderWithURL:url delegate:self];
这样SDWebImageDownloaderDelegate协议的方法imageDownloader:didFinishWithImage:被调用时下载会立即开始并完成。
独立的异步图像缓存
SDImageCache类提供一个创建空缓存的实例,并用方法imageForKey:来寻找当前缓存。
UIImage *myCachedImage = [[SDImageCache sharedImageCache] imageFromKey:myCacheKey];
存储一个图像到缓存是使用方法storeImage: forKey:
[[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey];
默认情况下,图像将被存储在内存缓存和磁盘缓存中。如果仅仅是想内存缓存中,要使用storeImage:forKey:toDisk:方法的第三个参数带一负值
来替代。
本地缓存机制:
参考:http://blog.csdn.net/pjk1129/article/details/6666952
概述:例如想要的效果:
这个缓存机制满足下面这些功能。
1、可以将数据缓存到本地磁盘。
2、可以判断一个资源是否已经被缓存。如果已经被缓存,在请求相同的资源,先到本地磁盘搜索。
3、可以判断文件缓存什么时候过期。这里为了简单起见这里,我们在请求url资源的时候,给每次请求的文件设定一个过期的时间。
4、可以实现:如果文件已经被缓存,而且没有过期,这将本地的数据返回,否则重新请求url。
5、可以实现:如果文件下载不成功或者下载没有完成,下次打开程序的时候,移除这些没有成功或者没有下载完成的文件。
6、可以实现:同时请求或者下载多个资源。
方法:1、设计一个CacheItem类,用来请求一个web连接,它的一个实例表示一个缓存项。这个CacheItem类,需要一个url创建一个NSURLConnection,去请求web资源。使用CacheItem类主要用来请求web资源。
2、在NSURLConnection开始请求之前,调用CachedDownloadManager类,来搜索和管理本地的缓存文件。将缓存文件的情况保存到一个字典类中。(同时保存缓存项的缓存信息:下载结束时间、下载开始时间、缓存有效时间、缓存过期时间、缓存到本地的路径。)
3、如果这个文件已经被下载,而且没有过期,则从本地获取文件的数据。如果文件已经过期,则重新下载。
4、我们设计缓存项下载成功和失败的两个委托方法(当我们下载成功的时候,修改缓存字典中的下载时间,表示已经下载完成,而且需要将请求的资源数据缓存到本地、如果下载失败我们需要从缓存字典中移除改缓存项)
5、加载缓存字典的时候,我们可以将没有下载完成的文件移除
内存缓存机制:
使用缓存的目的是为了使用的应用程序能更快速的响应用户输入,是程序高效的运行。有时候我们需要将远程web服务器获取的数据缓存起来,减少对同一个url多次请求。
内存缓存我们可以使用sdk中的NSURLCache类。NSURLRequest需要一个缓存参数来说明它请求的url何如缓存数据的,我们先看下它的CachePolicy类型。
1、NSURLRequestUseProtocolCachePolicy NSURLRequest默认的cache policy,使用Protocol协议定义。
2、NSURLRequestReloadIgnoringCacheData 忽略缓存直接从原始地址下载。
3、NSURLRequestReturnCacheDataElseLoad 只有在cache中不存在data时才从原始地址下载。
4、NSURLRequestReturnCacheDataDontLoad 只使用cache数据,如果不存在cache,请求失败;用于没有建立网络连接离线模式;
5、NSURLRequestReloadIgnoringLocalAndRemoteCacheData:忽略本地和远程的缓存数据,直接从原始地址下载,与NSURLRequestReloadIgnoringCacheData类似。
6、NSURLRequestReloadRevalidatingCacheData:验证本地数据与远程数据是否相同,如果不同则下载远程数据,否则使用本地数据。
看代码流程--------
正常请求-判断缓存-有-从缓存读取
-----无--网络请求--(这里的缓存式保存在内存中,第二次在从内存中读取)
-(IBAction) buttonPress:(id) sender
{
NSString *paramURLAsString= @"http://www.baidu.com/";
if ([paramURLAsString length] == 0){
NSLog(@"Nil or empty URL is given");
return;
}
NSURLCache *urlCache = [NSURLCache sharedURLCache];
/* 设置缓存的大小为1M*/
[urlCache setMemoryCapacity:1*1024*1024];
//创建一个nsurl
NSURL *url = [NSURL URLWithString:paramURLAsString];
//创建一个请求
NSMutableURLRequest *request =
[NSMutableURLRequest
requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0f];
//从请求中获取缓存输出
NSCachedURLResponse *response =
[urlCache cachedResponseForRequest:request];
//判断是否有缓存
if (response != nil){
NSLog(@"如果有缓存输出,从缓存中获取数据");
[request setCachePolicy:NSURLRequestReturnCacheDataDontLoad];
}
self.connection = nil;
/* 创建NSURLConnection*/
NSURLConnection *newConnection =
[[NSURLConnection alloc] initWithRequest:request
delegate:self
startImmediately:YES];
self.connection = newConnection;
[newConnection release];
}
常见crash:
1. mutating method sent to immutable object'
从字面上理解:可变的消息发给了不可变的对象。比如NSDictionary类型的对象调用setValue方法.应该把NSDictionary 改成NSMutableDictionary类型。
2.Local declaration of 'content' hides instance variable
一般是函数里面定义的变量和class属性变量重名了。很少有和系统变量重名的情况。
3.unrecognized selector sent to instance
大部分情况下是因为对象被提前release了,在不希望他release的情况下,指针还在,对象已经不在了。
很多时候,是因为init初始化函数中,对属性赋值没有使用self.foo赋值,而是直接对foo赋值,导致属性对象没有retain(心里以为retain了),而提前释放。
4.使用ASIHTTPRequest编译不通过
原因是一些类库没有加进去。把这些库加进去CFNetwork, SystemConfiguration, MobileCoreServices, and libz.dylib
5.添加在UIView中的UIButton 单击不起作用
原因是UIbutton的frame超出了UIView的frame范围。事实上UIView并没有设置frame,设置完后( 范围一定要在UIButton之外),UIButton单击就可以了
6.当使用presentViewController和dismissPresentViewController时,如果报这个错 : while presentation is in progress ,修改方法为[mainView dismissModalViewControllerAnimated:NO]; 将参数Animated改为NO;如果报这个错while a presentation or dismiss is in progress,试试这样
if (![[mainView modalViewController] isBeingDismissed]) {
[mainView dismissModalViewControllerAnimated:NO];
}
7.调用系统相册的时候,非常容易出现内存警告,加入红色代码就会好点:
UIImagePickerController * picker = [[UIImagePickerController alloc]init];
picker.delegate = self;
picker.allowsEditing = NO; //是否可编辑
picker.videoQuality=UIImagePickerControllerQualityTypeLow;
//摄像头
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
[self presentModalViewController:picker animated:YES];
[picker release];
8.ios开发者都遇见过得错误:EXC_BAD_ACCESS 。这个和第二个比较类似。通常的调试方法就是加入NSZombieEnabled变量,加入方法自行百度。
并且开发过程中使用
[[NSNotificationCenterdefaultCenter]
来发布本地消息,这个也经常会出现EXC_BAD_ACCESS错误。这个时候只需要在你的view活着viewControllers的dealloc的方法里面加入
[[NSNotificationCenterdefaultCenter]removeObserver:selfname:@"yourNotification"object:nil];就ok了
9.遇见一个蛋疼的问题"linker command failed with exit code 1 (use -v to see invocation)" 。翻遍了找不到原因。然后还有这样的警告
duplicate symbol _OBJC_CLASS 。原来是在导入某个类的时候导入.m文件,而不是.h文件
GCD初探
什么是GCD
Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。该方法在Mac OS X 10.6雪豹中首次推出,并随后被引入到了iOS4.0中。GCD是一个替代诸如NSThread, NSOperationQueue, NSInvocationOperation等技术的很高效和强大的技术,它看起来象就其它语言的闭包(Closure)一样,但苹果把它叫做blocks。
应用举例
让我们来看一个编程场景。我们要在iphone上做一个下载网页的功能,该功能非常简单,就是在iphone上放置一个按钮,点击该按钮时,显示一个转动的圆圈,表示正在进行下载,下载完成之后,将内容加载到界面上的一个文本控件中。
不用GCD前
虽然功能简单,但是我们必须把下载过程放到后台线程中,否则会阻塞UI线程显示。所以,如果不用GCD, 我们需要写如下3个方法:
- someClick 方法是点击按钮后的代码,可以看到我们用NSInvocationOperation建了一个后台线程,并且放到NSOperationQueue中。后台线程执行download方法。
- download 方法处理下载网页的逻辑。下载完成后用performSelectorOnMainThread执行download_completed 方法。
- download_completed 进行clear up的工作,并把下载的内容显示到文本控件中。
这3个方法的代码如下。可以看到,虽然 开始下载 -> 下载中 -> 下载完成 这3个步骤是整个功能的三步。但是它们却被切分成了3块。他们之间因为是3个方法,所以还需要传递数据参数。如果是复杂的应用,数据参数很可能就不象本例子中的NSString那么简单了,另外,下载可能放到Model的类中来做,而界面的控制放到View Controller层来做,这使得本来就分开的代码变得更加散落。代码的可读性大大降低。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | static NSOperationQueue * queue; - (IBAction)someClick:(id)sender { self.indicator.hidden = NO; [self.indicator startAnimating]; queue = [[NSOperationQueue alloc] init]; NSInvocationOperation * op = [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil] autorelease]; [queue addOperation:op]; } - (void)download { NSURL * url = [NSURL URLWithString:@"http://www.youdao.com"]; NSError * error; NSString * data = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error]; if (data != nil) { [self performSelectorOnMainThread:@selector(download_completed:) withObject:data waitUntilDone:NO]; } else { NSLog(@"error when download:%@", error); [queue release]; } } - (void) download_completed:(NSString *) data { NSLog(@"call back"); [self.indicator stopAnimating]; self.indicator.hidden = YES; self.content.text = data; [queue release]; } |
使用GCD后
如果使用GCD,以上3个方法都可以放到一起,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // 原代码块一 self.indicator.hidden = NO; [self.indicator startAnimating]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 原代码块二 NSURL * url = [NSURL URLWithString:@"http://www.youdao.com"]; NSError * error; NSString * data = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error]; if (data != nil) { // 原代码块三 dispatch_async(dispatch_get_main_queue(), ^{ [self.indicator stopAnimating]; self.indicator.hidden = YES; self.content.text = data; }); } else { NSLog(@"error when download:%@", error); } }); |
首先我们可以看到,代码变短了。因为少了原来3个方法的定义,也少了相互之间需要传递的变量的封装。
另外,代码变清楚了,虽然是异步的代码,但是它们被GCD合理的整合在一起,逻辑非常清晰。如果应用上MVC模式,我们也可以将View Controller层的回调函数用GCD的方式传递给Modal层,这相比以前用@selector的方式,代码的逻辑关系会更加清楚。
GCD的定义
简单GCD的定义有点象函数指针,差别是用 ^ 替代了函数指针的 * 号,如下所示:
1 2 3 4 5 6 7 8 9 | // 申明变量 (void) (^loggerBlock)(void); // 定义 loggerBlock = ^{ NSLog(@"Hello world"); }; // 调用 loggerBlock(); |
但是大多数时候,我们通常使用内联的方式来定义它,即将它的程序块写在调用的函数里面,例如这样:
1 2 3 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ // something }); |
从上面大家可以看出,block有如下特点:
- 程序块可以在代码中以内联的方式来定义。
- 程序块可以访问在创建它的范围内的可用的变量。
系统提供的dispatch方法
为了方便地使用GCD,苹果提供了一些方法方便我们将block放在主线程 或 后台线程执行,或者延后执行。使用的例子如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // 后台执行: dispatch_async(dispatch_get_global_queue(0, 0), ^{ // something }); // 主线程执行: dispatch_async(dispatch_get_main_queue(), ^{ // something }); // 一次性执行: static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // code to be executed once }); // 延迟2秒执行: double delayInSeconds = 2.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ // code to be executed on the main queue after delay }); |
dispatch_queue_t 也可以自己定义,如要要自定义queue,可以用dispatch_queue_create方法,示例如下:
1 2 3 4 5 | dispatch_queue_t urls_queue = dispatch_queue_create("blog.devtang.com", NULL); dispatch_async(urls_queue, ^{ // your code }); dispatch_release(urls_queue); |
另外,GCD还有一些高级用法,例如让后台2个线程并行执行,然后等2个线程都结束后,再汇总执行结果。这个可以用dispatch_group, dispatch_group_async 和 dispatch_group_notify来实现,示例如下:
1 2 3 4 5 6 7 8 9 10 | dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{ // 并行执行的线程一 }); dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{ // 并行执行的线程二 }); dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{ // 汇总结果 }); |
修改block之外的变量
默认情况下,在程序块中访问的外部变量是复制过去的,即写操作不对原变量生效。但是你可以加上 __block来让其写操作生效,示例代码如下:
1 2 3 4 5 6 | __block int a = 0; void (^foo)(void) = ^{ a = 1; } foo(); // 这里,a的值被修改为1 |
后台运行
GCD的另一个用处是可以让程序在后台较长久的运行。在没有使用GCD时,当app被按home键退出后,app仅有最多5秒钟的时候做一些保存或清理资源的工作。但是在使用GCD后,app最多有10分钟的时间在后台长久运行。这个时间可以用来做清理本地缓存,发送统计数据等工作。
让程序在后台长久运行的示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // AppDelegate.h文件 @property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundUpdateTask; // AppDelegate.m文件 - (void)applicationDidEnterBackground:(UIApplication *)application { [self beingBackgroundUpdateTask]; // 在这里加上你需要长久运行的代码 [self endBackgroundUpdateTask]; } - (void)beingBackgroundUpdateTask { self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ [self endBackgroundUpdateTask]; }]; } - (void)endBackgroundUpdateTask { [[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask]; self.backgroundUpdateTask = UIBackgroundTaskInvalid; } |
总结
总体来说,GCD能够极大地方便开发者进行多线程编程。如果你的app不需要支持iOS4.0以下的系统,那么就应该尽量使用GCD来处理后台线程和UI线程的交互。