ios学习开源代码系列(二)ifengNewsOrderDemo
2014-05-23 15:26
302 查看
在code4app上面找到了ifengNewsOrderDemo这个开源代码,实现的是类似网易新闻的订阅功能,挺有趣的,于是找来研读一下,发现这工程还是有点复杂,能够慢慢研究的地方挺多
首先来看看应用的运行效果
当点击右上角的按钮的时候,就会自上而下淡入一个订阅界面
此时我们可以拖动界面上的按钮,或者点击右下角的按钮返回上一界面
下面看看工程组件之间的关系
首先看看RootViewController做了些什么,从viewDidLoad入手
还有在“我的订阅”视图右下方的返回按键也是类似的原理
所以OrderViewController是在每次显示的时候被创建,又在每次消失时删除
在看OrderViewController前我们给界面划分一下逻辑关系,如下图
下面我们来看看OrderViewController的viewDidLoad方法
第一步首先是把“订阅”和“跟多频道”的数据序列化到Library目录下
然后定义了两个model数组和两个view数组
接下来是绘制“我的订阅”和“更多频道”的label
最后是分别循环绘制区域1和区域2的views
最后的重点是TouchView,TouchView里面做了许多工作,主要包括调整分组和完成动画效果
首先我们看看头文件
TouchView用了_point和_point2来标识一些位置信息,用_sign表示TouchView是出于移动状态还是静止状态,利用_array来标识自己属于哪个区域,另外也用_viewArr11和_viewArr22来引用两个区域的view的集合
接下来是看看它的实现,首先是一些初始化操作
初始化的时候它会往自己添加一个label,用来显示文字标题,如“头条”、“热点”等等
重点是touchesBegan、touchesMoved和touchesEnded方法,相关解析我都写了注释
如果用户只是点击了一下touchview,那么就不会调用touchesMoved方法,直接就调用touchesEnded方法
移动过程中的绘制逻辑和数据交换都很大部分发生在touchedMoved里面
下面给出其中调用到的Animation函数的注解
完
首先来看看应用的运行效果
当点击右上角的按钮的时候,就会自上而下淡入一个订阅界面
此时我们可以拖动界面上的按钮,或者点击右下角的按钮返回上一界面
下面看看工程组件之间的关系
首先看看RootViewController做了些什么,从viewDidLoad入手
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. OrderButton * orderButton = [OrderButton orderButtonWithViewController:self titleArr:[NSArray arrayWithObjects:KChannelList, nil] urlStringArr:[NSArray arrayWithObjects:KChannelUrlStringList, nil]]; [self.view addSubview:orderButton]; [orderButton addTarget:self action:@selector(orderViewOut:) forControlEvents:UIControlEventTouchUpInside]; }代码里面就是在view上创建了一个按钮(右上角那个),然后绑定了一个点击事件,我们看看那个点事件
- (void)orderViewOut:(id)sender{ OrderButton * orderButton = (OrderButton *)sender; if([[orderButton.vc.view subviews] count]>1){ // [[[orderButton.vc.view subviews]objectAtIndex:1] removeFromSuperview]; NSLog(@"%@",[orderButton.vc.view subviews]); } OrderViewController * orderVC = [[[OrderViewController alloc] init] autorelease]; orderVC.titleArr = orderButton.titleArr; orderVC.urlStringArr = orderButton.urlStringArr; UIView * orderView = [orderVC view]; [orderView setFrame:CGRectMake(0, - orderButton.vc.view.bounds.size.height, orderButton.vc.view.bounds.size.width, orderButton.vc.view.bounds.size.height)]; [orderView setBackgroundColor:[UIColor colorWithRed:239/255.0 green:239/255.0 blue:239/255.0 alpha:1.0]]; [orderVC.backButton addTarget:self action:@selector(backAction) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:orderView]; [self addChildViewController:orderVC]; [UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{ [orderView setFrame:CGRectMake(0, 0, orderButton.vc.view.bounds.size.width, orderButton.vc.view.bounds.size.height)]; } completion:^(BOOL finished){ }]; }方法首先新建了OrderViewController,然后把他加入到RootViewController的ChildViewController里面,最后通过动画效果把view展示出来,view开始是放置在屏幕外面的,然后才通过Animation再把他显示出来
还有在“我的订阅”视图右下方的返回按键也是类似的原理
- (void)backAction{ OrderViewController * orderVC = [self.childViewControllers objectAtIndex:0]; [UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{ [orderVC.view setFrame:CGRectMake(0, - self.view.bounds.size.height, self.view.bounds.size.width, self.view.bounds.size.height)]; } completion:^(BOOL finished){ NSString * string = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]; NSString * filePath = [string stringByAppendingString:@"/modelArray0.swh"]; NSString * filePath1 = [string stringByAppendingString:@"/modelArray1.swh"]; NSMutableArray * modelArr = [NSMutableArray array]; for (TouchView * touchView in orderVC->_viewArr1) { [modelArr addObject:touchView.touchViewModel]; } NSData * data = [NSKeyedArchiver archivedDataWithRootObject:modelArr]; [data writeToFile:filePath atomically:YES]; [modelArr removeAllObjects]; for (TouchView * touchView in orderVC->_viewArr2) { [modelArr addObject:touchView.touchViewModel]; } data = [NSKeyedArchiver archivedDataWithRootObject:modelArr]; [data writeToFile:filePath1 atomically:YES]; [[[self.childViewControllers objectAtIndex:0] view] removeFromSuperview]; [orderVC removeFromParentViewController]; }]; }通过Animation把view推到可见区域的上方,然后把OrderViewController从ParentViewController中删除掉
所以OrderViewController是在每次显示的时候被创建,又在每次消失时删除
在看OrderViewController前我们给界面划分一下逻辑关系,如下图
下面我们来看看OrderViewController的viewDidLoad方法
第一步首先是把“订阅”和“跟多频道”的数据序列化到Library目录下
[super viewDidLoad]; // Do any additional setup after loading the view. NSString * string = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]; NSLog(@"%@", string); NSString * filePath = [string stringByAppendingString:@"/modelArray0.swh"]; NSString * filePath1 = [string stringByAppendingString:@"/modelArray1.swh"]; if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) { NSArray * channelListArr = self.titleArr; NSArray * channelUrlStringListArr = self.urlStringArr; NSMutableArray * mutArr = [NSMutableArray array]; for (int i = 0; i < [channelListArr count]; i++) { NSString * title = [channelListArr objectAtIndex:i]; NSString * urlString = [channelUrlStringListArr objectAtIndex:i]; TouchViewModel * touchViewModel = [[TouchViewModel alloc] initWithTitle:title urlString:urlString]; [mutArr addObject:touchViewModel]; [touchViewModel release]; if (i == KDefaultCountOfUpsideList - 1) { NSData * data = [NSKeyedArchiver archivedDataWithRootObject:mutArr]; [data writeToFile:filePath atomically:YES]; [mutArr removeAllObjects]; } else if(i == [channelListArr count] - 1){ NSData * data = [NSKeyedArchiver archivedDataWithRootObject:mutArr]; [data writeToFile:filePath1 atomically:YES]; } } }
然后定义了两个model数组和两个view数组
_modelArr1 = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath]; NSArray * modelArr2 = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath1]; //区域1的views _viewArr1 = [[NSMutableArray alloc] init]; //区域2的views _viewArr2 = [[NSMutableArray alloc] init];
接下来是绘制“我的订阅”和“更多频道”的label
_titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(110, 25, 100, 40)]; _titleLabel.text = @"我的订阅"; [_titleLabel setTextAlignment:NSTextAlignmentCenter]; [_titleLabel setTextColor:[UIColor colorWithRed:187/255.0 green:1/255.0 blue:1/255.0 alpha:1.0]]; [self.view addSubview:_titleLabel]; _titleLabel2 = [[UILabel alloc] initWithFrame:CGRectMake(110, KTableStartPointY + KButtonHeight * ([self array2StartY] - 1) + 22, 100, 20)]; _titleLabel2.text = @"更多频道"; [_titleLabel2 setFont:[UIFont systemFontOfSize:10]]; [_titleLabel2 setTextAlignment:NSTextAlignmentCenter]; [_titleLabel2 setTextColor:[UIColor grayColor]]; [self.view addSubview:_titleLabel2];
最后是分别循环绘制区域1和区域2的views
for (int i = 0; i < _modelArr1.count; i++) { TouchView * touchView = [[TouchView alloc] initWithFrame:CGRectMake(KTableStartPointX + KButtonWidth * (i%5), KTableStartPointY + KButtonHeight * (i/5), KButtonWidth, KButtonHeight)]; [touchView setBackgroundColor:[UIColor colorWithRed:210/255.0 green:210/255.0 blue:210/255.0 alpha:1.0]]; [_viewArr1 addObject:touchView]; [touchView release]; touchView->_array = _viewArr1; if (i == 0) { [touchView.label setTextColor:[UIColor colorWithRed:187/255.0 green:1/255.0 blue:1/255.0 alpha:1.0]]; } else{ [touchView.label setTextColor:[UIColor colorWithRed:99/255.0 green:99/255.0 blue:99/255.0 alpha:1.0]]; } touchView.label.text = [[_modelArr1 objectAtIndex:i] title]; [touchView.label setTextAlignment:NSTextAlignmentCenter]; [touchView setMoreChannelsLabel:_titleLabel2]; touchView->_viewArr11 = _viewArr1; touchView->_viewArr22 = _viewArr2; [touchView setTouchViewModel:[_modelArr1 objectAtIndex:i]]; [self.view addSubview:touchView]; } for (int i = 0; i < modelArr2.count; i++) { TouchView * touchView = [[TouchView alloc] initWithFrame:CGRectMake(KTableStartPointX + KButtonWidth * (i%5), KTableStartPointY + [self array2StartY] * KButtonHeight + KButtonHeight * (i/5), KButtonWidth, KButtonHeight)]; [touchView setBackgroundColor:[UIColor colorWithRed:210/255.0 green:210/255.0 blue:210/255.0 alpha:1.0]]; [_viewArr2 addObject:touchView]; touchView->_array = _viewArr2; touchView.label.text = [[modelArr2 objectAtIndex:i] title]; [touchView.label setTextColor:[UIColor colorWithRed:99/255.0 green:99/255.0 blue:99/255.0 alpha:1.0]]; [touchView.label setTextAlignment:NSTextAlignmentCenter]; [touchView setMoreChannelsLabel:_titleLabel2]; touchView->_viewArr11 = _viewArr1; touchView->_viewArr22 = _viewArr2; [touchView setTouchViewModel:[modelArr2 objectAtIndex:i]]; [self.view addSubview:touchView]; [touchView release]; }
最后的重点是TouchView,TouchView里面做了许多工作,主要包括调整分组和完成动画效果
首先我们看看头文件
#import <UIKit/UIKit.h> @class TouchViewModel; @interface TouchView : UIImageView { CGPoint _point; CGPoint _point2; //代表是否在移动中 0否 1是 NSInteger _sign; @public //所在的view的集合(“已订阅”的view集合或者“跟多频道”的view集合) NSMutableArray * _array; //“已订阅”的view集合 NSMutableArray * _viewArr11; //“跟多频道”的view集合 NSMutableArray * _viewArr22; } @property (nonatomic,retain) UILabel * label; @property (nonatomic,retain) UILabel * moreChannelsLabel; //每个TouchView对应一个Model @property (nonatomic,retain) TouchViewModel * touchViewModel; @end
TouchView用了_point和_point2来标识一些位置信息,用_sign表示TouchView是出于移动状态还是静止状态,利用_array来标识自己属于哪个区域,另外也用_viewArr11和_viewArr22来引用两个区域的view的集合
接下来是看看它的实现,首先是一些初始化操作
- (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // Initialization code self.multipleTouchEnabled = YES; self.userInteractionEnabled = YES; UILabel *l = [[UILabel alloc] initWithFrame:CGRectZero]; self.label = l; [l release]; _sign = 0; } return self; } - (void)layoutSubviews{ //往TouchView内部添加label [self.label setFrame:CGRectMake(1, 1, KButtonWidth - 2, KButtonHeight - 2)]; [self.label setBackgroundColor:[UIColor colorWithRed:239/255.0 green:239/255.0 blue:239/255.0 alpha:1.0]]; [self addSubview:self.label]; }
初始化的时候它会往自己添加一个label,用来显示文字标题,如“头条”、“热点”等等
重点是touchesBegan、touchesMoved和touchesEnded方法,相关解析我都写了注释
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch * touch = [touches anyObject]; //相对于内部的位移 _point = [touch locationInView:self]; //相对于父视图的位移 _point2 = [touch locationInView:self.superview]; //把当前点击的view放到最顶层,当移动的时候,就不会被其他按钮阻挡了 [self.superview exchangeSubviewAtIndex:[self.superview.subviews indexOfObject:self] withSubviewAtIndex:[[self.superview subviews] count] - 1]; }
如果用户只是点击了一下touchview,那么就不会调用touchesMoved方法,直接就调用touchesEnded方法
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch *touch = [touches anyObject]; CGPoint point = [touch locationInView:self.superview]; int a = point.x - _point.x; int b = point.y - _point.y; if (![self.label.text isEqualToString:@"头条"]) { [self.label setBackgroundColor:[UIColor colorWithRed:239/255.0 green:239/255.0 blue:239/255.0 alpha:1.0]]; [self setImage:nil]; //这里_sign是用来判断touchesEnded之前touchView是出于什么状态的,如果用户只是点击了一下,并没有拖动,那么_sign就为0,并且touchView应该添加到相反区域上,如果之前用户是拖动的,那么_sign就为1,这时候不能简单地把touchView添加到相反区域上,因为用户有可能没有拖动到相反区域上,而是在原来的区域里面释放 if (_sign == 0) { if (_array == _viewArr11) { [_viewArr11 removeObject:self]; [_viewArr22 insertObject:self atIndex:_viewArr22.count]; _array = _viewArr22; [self animationAction]; } else if ( _array == _viewArr22){ [_viewArr22 removeObject:self]; [_viewArr11 insertObject:self atIndex:_viewArr11.count]; _array = _viewArr11; [self animationAction]; } } //此时_sign=1,那么就 else if (([self buttonInArrayArea1:_viewArr11 Point:point] || [self buttonInArrayArea2:_viewArr22 Point:point])&&!(point.x - _point.x > KTableStartPointX && point.x - _point.x < KTableStartPointX + KButtonWidth && point.y - _point.y > KTableStartPointY && point.y - _point.y < KTableStartPointY + KButtonHeight)){ if (point.x < KTableStartPointX || point.y < KTableStartPointY) { [self setFrame:CGRectMake(_point2.x - _point.x, _point2.y - _point.y, self.frame.size.width, self.frame.size.height)]; } else{ [self setFrame:CGRectMake(KTableStartPointX + (a + KButtonWidth/2 - KTableStartPointX)/KButtonWidth*KButtonWidth, KTableStartPointY + (b + KButtonHeight/2 - KTableStartPointY)/KButtonHeight*KButtonHeight, self.frame.size.width, self.frame.size.height)]; } } else{ [self animationAction]; } _sign = 0; } [self.label setBackgroundColor:[UIColor colorWithRed:239/255.0 green:239/255.0 blue:239/255.0 alpha:1.0]]; [self setImage:nil]; }
移动过程中的绘制逻辑和数据交换都很大部分发生在touchedMoved里面
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ _sign = 1; UITouch *touch = [touches anyObject]; CGPoint point = [touch locationInView:self.superview]; if (![self.label.text isEqualToString:@"头条"]) { [self.label setBackgroundColor:[UIColor clearColor]]; [self setImage:[UIImage imageNamed:@"order_drag_move_bg.png"]]; [self setFrame:CGRectMake( point.x - _point.x, point.y - _point.y, self.frame.size.width, self.frame.size.height)]; //中心点 CGFloat newX = point.x - _point.x + KButtonWidth/2; CGFloat newY = point.y - _point.y + KButtonHeight/2; //如果点击的是“头条”框,则不做处理 if (!CGRectContainsPoint([[_viewArr11 objectAtIndex:0] frame], CGPointMake(newX, newY)) ) { //如果自己是属于区域二的view if ( _array == _viewArr22) { //当前处于区域一 if ([self buttonInArrayArea1:_viewArr11 Point:point]) { //计算出当前位置属于viewArr11数组中的哪个索引 int index = ((int)newX - KTableStartPointX)/KButtonWidth + (5 * (((int)newY - KTableStartPointY)/KButtonHeight)); //把自己从原来所属的区域中删除 [ _array removeObject:self]; //把自己插入到区域一中去 [_viewArr11 insertObject:self atIndex:index]; //把自己所属标识改为区域一 _array = _viewArr11; [self animationAction1a]; [self animationAction2]; } //如果超过了“更多频道”以上水平线以上,则自动认为是加入到区域一 else if (newY < KTableStartPointY + [self array2StartY] * KButtonHeight &&![self buttonInArrayArea1:_viewArr11 Point:point]){ [ _array removeObject:self]; [_viewArr11 insertObject:self atIndex:_viewArr11.count]; _array = _viewArr11; [self animationAction2]; } //如果仍然是在区域二里面移动,则只是调整一下显示的位置 else if([self buttonInArrayArea2:_viewArr22 Point:point]){ unsigned long index = ((unsigned long )(newX) - KTableStartPointX)/KButtonWidth + (5 * (((int)(newY) - [self array2StartY] * KButtonHeight - KTableStartPointY)/KButtonHeight)); [ _array removeObject:self]; [_viewArr22 insertObject:self atIndex:index]; [self animationAction2a]; } else if(newY > KTableStartPointY + [self array2StartY] * KButtonHeight &&![self buttonInArrayArea2:_viewArr22 Point:point]){ [ _array removeObject:self]; [_viewArr22 insertObject:self atIndex:_viewArr22.count]; [self animationAction2a]; } } //如果自己是属于区域一的view else if ( _array == _viewArr11) { if ([self buttonInArrayArea1:_viewArr11 Point:point]) { int index = ((int)newX - KTableStartPointX)/KButtonWidth + (5 * (((int)(newY) - KTableStartPointY)/KButtonHeight)); [ _array removeObject:self]; [_viewArr11 insertObject:self atIndex:index]; _array = _viewArr11; [self animationAction1a]; [self animationAction2]; } else if (newY < KTableStartPointY + [self array2StartY] * KButtonHeight &&![self buttonInArrayArea1:_viewArr11 Point:point]){ [ _array removeObject:self]; [_viewArr11 insertObject:self atIndex: _array.count]; [self animationAction1a]; [self animationAction2]; } else if([self buttonInArrayArea2:_viewArr22 Point:point]){ unsigned long index = ((unsigned long)(newX) - KTableStartPointX)/KButtonWidth + (5 * (((int)(newY) - [self array2StartY] * KButtonHeight - KTableStartPointY)/KButtonHeight)); [ _array removeObject:self]; [_viewArr22 insertObject:self atIndex:index]; _array = _viewArr22; [self animationAction2a]; } else if(newY > KTableStartPointY + [self array2StartY] * KButtonHeight &&![self buttonInArrayArea2:_viewArr22 Point:point]){ [ _array removeObject:self]; [_viewArr22 insertObject:self atIndex:_viewArr22.count]; _array = _viewArr22; [self animationAction2a]; } } } } }
下面给出其中调用到的Animation函数的注解
- (void)animationAction{ //先循环绘制“我的订阅”的内容 for (int i = 0; i < _viewArr11.count; i++) { [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{ [[_viewArr11 objectAtIndex:i] setFrame:CGRectMake(KTableStartPointX + (i%5) * KButtonWidth, KTableStartPointY + (i/5)* KButtonHeight, KButtonWidth, KButtonHeight)]; } completion:^(BOOL finished){ }]; } //再循环绘制“更多频道”的内容 for (int i = 0; i < _viewArr22.count; i++) { [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{ [[_viewArr22 objectAtIndex:i] setFrame:CGRectMake(KTableStartPointX + (i%5) * KButtonWidth, KTableStartPointY + [self array2StartY] * KButtonHeight + (i/5)* KButtonHeight, KButtonWidth, KButtonHeight)]; } completion:^(BOOL finished){ }]; } //调整“更多频道”label的位置 [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{ [self.moreChannelsLabel setFrame:CGRectMake(self.moreChannelsLabel.frame.origin.x, KTableStartPointY + KButtonHeight * ([self array2StartY] - 1) + 22, self.moreChannelsLabel.frame.size.width, self.moreChannelsLabel.frame.size.height)]; } completion:^(BOOL finished){ }]; }
//在区域一中给除自己之外的view重绘 - (void)animationAction1a{ for (int i = 0; i < _viewArr11.count; i++) { if ([_viewArr11 objectAtIndex:i] != self) { [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{ [[_viewArr11 objectAtIndex:i] setFrame:CGRectMake(KTableStartPointX + (i%5) * KButtonWidth, KTableStartPointY + (i/5)* KButtonHeight, KButtonWidth, KButtonHeight)]; } completion:^(BOOL finished){ }]; } } }
//区域2以及区域2的label进行重绘 - (void)animationAction2{ for (int i = 0; i < _viewArr22.count; i++) { [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{ [[_viewArr22 objectAtIndex:i] setFrame:CGRectMake(KTableStartPointX + (i%5) * KButtonWidth, KTableStartPointY + [self array2StartY] * KButtonHeight + (i/5)* KButtonHeight, KButtonWidth, KButtonHeight)]; } completion:^(BOOL finished){ }]; } [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{ [self.moreChannelsLabel setFrame:CGRectMake(self.moreChannelsLabel.frame.origin.x, KTableStartPointY + KButtonHeight * ([self array2StartY] - 1) + 22, self.moreChannelsLabel.frame.size.width, self.moreChannelsLabel.frame.size.height)]; } completion:^(BOOL finished){ }]; }
//在区域二中给除自己之外的view重绘 - (void)animationAction2a{ for (int i = 0; i < _viewArr22.count; i++) { if ([_viewArr22 objectAtIndex:i] != self) { [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{ [[_viewArr22 objectAtIndex:i] setFrame:CGRectMake(KTableStartPointX + (i%5) * KButtonWidth, KTableStartPointY + [self array2StartY] * KButtonHeight + (i/5)* KButtonHeight, KButtonWidth, KButtonHeight)]; } completion:^(BOOL finished){ }]; } } [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{ [self.moreChannelsLabel setFrame:CGRectMake(self.moreChannelsLabel.frame.origin.x, KTableStartPointY + KButtonHeight * ([self array2StartY] - 1) + 22, self.moreChannelsLabel.frame.size.width, self.moreChannelsLabel.frame.size.height)]; } completion:^(BOOL finished){ }]; }
完
相关文章推荐
- ios学习开源代码系列(一)LHLoadingView
- iOS/iPhone学习系列、代码教程
- [原][学习笔记][2011.11.14] 实现ios 主要的几个控件demo
- iOS学习系列 - MonoTouch绑定原生Obj-C静态库的实现
- ios开发学习 --基础知识--系列教程
- iOS学习系列 - 扩展机制category与associative
- PHP学习系列$第三章:IF基本循环
- ios开发学习--动画(Animation)效果源码分享--系列教程1
- iOS学习系列 - 利用ASIHTTPRequest实现异步队列
- iOS学习系列 - 扩展机制category与associative
- ios开发学习 --基础知识--系列教程
- iOS/iPhone学习系列、代码教程----~~~持续更新中~~~
- 【iOS-Cocos2d游戏开发】系列(总结了多篇文章,可以好好学习
- 【iOS-Cocos2d游戏开发】系列(总结了多篇文章,可以好好学习 )
- iOS学习系列 - 在iOS客户端实现google oauth2登录以及在asp.net服务端上form认证
- iOS/iPhone学习系列、代码教程
- iOS/iPhone学习系列、代码教程
- iOS 5 Storyboard 的学习 系列
- iOS学习系列 - UITableView下拉更新/上提加载的实现
- ios开发学习--音频声效(Audio)效果源码分享--系列教程