您的位置:首页 > 移动开发 > IOS开发

ios学习开源代码系列(二)ifengNewsOrderDemo

2014-05-23 15:26 302 查看
在code4app上面找到了ifengNewsOrderDemo这个开源代码,实现的是类似网易新闻的订阅功能,挺有趣的,于是找来研读一下,发现这工程还是有点复杂,能够慢慢研究的地方挺多

首先来看看应用的运行效果



当点击右上角的按钮的时候,就会自上而下淡入一个订阅界面



此时我们可以拖动界面上的按钮,或者点击右下角的按钮返回上一界面

下面看看工程组件之间的关系







首先看看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){

}];
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: