完整cocos2d实现scrollView
作者:Clear 日期:2012-01-04
前面实现了一部分的scrollView,今天将这个类的功能完整实现。
转帖请注明出处:
http://www.cnrui.cn/blog/article.asp?id=267
#ScrollViewUtilty.h
//
// ScrollViewUtilty.h
// Shop
//
// Created by 胡 东平 on 12-1-1.
// Copyright (c) 2012年 Magnetjoy. All rights reserved.
//
#import "Utilties.h"
/** 溢出隐藏节点
*/
@interface OverflowNode : CCNode
// 绘制前
-(void)beforeDraw;
// 绘制后
-(void)afterDraw;
@end
// 触摸方法
typedef enum{
// 先选中子项目
fmScrollBeforeTouch,
// 后选中子项目
fmScrollAfterTouch
}fmScrollTouchFunc;
// 滚动方向
typedef enum{
// 从左到右滚动
fmScrollOrientationLeftToRight,
// 从右到左滚动
fmScrollOrientationRightToLeft,
// 从上到下滚动
fmScrollOrientationTopToBottom,
// 从下到上滚动
fmScrollOrientationBottomToTop
}fmScrollOrientation;
/**************************************************************/
/** 滚动视图
*/
@interface FMScrollView : OverflowNode<CCTargetedTouchDelegate>{
// 内部滚动栏
CCSprite* inner;
// 包含项目
CCArray* items;
// 视图大小
CGSize size;
// 间隔
CGFloat itemSpace;
// 触摸起始点
CGPoint startPoint;
// 摩擦系数
CGPoint friction;
// 摩擦阻力 默认0.03
CGFloat resistance;
// 惯性 默认20
CGFloat inertia;
// 前一次触摸纪录时间
NSTimeInterval preTimestamp;
// 前一次触摸点纪录
CGPoint prePoint;
// 可视大小
CGPoint viewPosit;
// 触摸方法
fmScrollTouchFunc touchFunc;
// 滚动方向
fmScrollOrientation scrollOrientation;
// 触摸范围
CGRect touchRect;
// 开始的触摸点
UITouch* beginTouch;
// 开始的事件
UIEvent* beginEvent;
// 当前选中的节点
CCNode<CCTargetedTouchDelegate>* currentItem;
}
// 摩擦阻力 默认0.03
@property(nonatomic)CGFloat resistance;
// 惯性 默认20
@property(nonatomic)CGFloat inertia;
// 使用视图大小初始化 默认先选中子项 左右滚动
+(FMScrollView*)viewWithViewSize:(CGSize)viewSize space:(CGFloat)space;
// 使用视图大小初始化 默认 左右滚动
+(FMScrollView*)viewWithViewSize:(CGSize)viewSize space:(CGFloat)space touchFunc:(fmScrollTouchFunc)func;
// 使用视图大小初始化 touchRect触摸范围
+(FMScrollView*)viewWithViewSize:(CGSize)viewSize space:(CGFloat)space touchFunc:(fmScrollTouchFunc)func orientation:(fmScrollOrientation)orientation touchRect:(CGRect)rect;
// 使用视图大小初始化 touchRect触摸范围
-(id)initWithViewSize:(CGSize)viewSize space:(CGFloat)space touchFunc:(fmScrollTouchFunc)func orientation:(fmScrollOrientation)orientation touchRect:(CGRect)rect;
// 添加一个项目节点
-(void)addItemNode:(CCNode*)node;
// 保存触摸开始信息
-(void)saveTouchStartInfo:(UITouch *)touch withEvent:(UIEvent *)event;
// 清楚触摸开始信息
-(void)clearTouchStartInfo;
// 等待子项触摸开始
-(void)waitItemTouchBegin;
// 通知触摸包含项目触摸事件开始
-(BOOL)containsItemTouchBegin:(UITouch *)touch withEvent:(UIEvent *)event;
@end
#ScrollViewUtilty.m
//
// ScrollViewUtilty.m
// Shop
//
// Created by 胡 东平 on 12-1-1.
// Copyright (c) 2012年 Magnetjoy. All rights reserved.
//
#import "ScrollViewUtilty.h"
/** 溢出隐藏节点
*/
@implementation OverflowNode
// 使用视图大小初始化
-(id) init
{
self = [super init];
if (self) {
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
// 绘制前
-(void)beforeDraw
{
GLfloat planeTop[] = {0.0f, -1.0f, 0.0f, self.boundingBoxInPixels.size.height};
GLfloat planeBottom[] = {0.0f, 1.0f, 0.0f, 0.0f};
GLfloat planeLeft[] = {1.0f, 0.0f, 0.0f, 0.0f};
GLfloat planeRight[] = {-1.0f, 0.0f, 0.0f, self.boundingBoxInPixels.size.width};
glClipPlanef(GL_CLIP_PLANE0, planeTop);
glClipPlanef(GL_CLIP_PLANE1, planeBottom);
glClipPlanef(GL_CLIP_PLANE2, planeLeft);
glClipPlanef(GL_CLIP_PLANE3, planeRight);
glEnable(GL_CLIP_PLANE0);
glEnable(GL_CLIP_PLANE1);
glEnable(GL_CLIP_PLANE2);
glEnable(GL_CLIP_PLANE3);
}
// 绘制后
-(void)afterDraw
{
glDisable(GL_CLIP_PLANE0);
glDisable(GL_CLIP_PLANE1);
glDisable(GL_CLIP_PLANE2);
glDisable(GL_CLIP_PLANE3);
}
// 绘制
-(void) visit
{
// quick return if not visible
if (!visible_)
return;
glPushMatrix();
if ( grid_ && grid_.active) {
[grid_ beforeDraw];
[self transformAncestors];
}
[self transform];
[self beforeDraw];
if(children_) {
ccArray *arrayData = children_->data;
NSUInteger i = 0;
// draw children zOrder < 0
for( ; i < arrayData->num; i++ ) {
CCNode *child = arrayData->arr[i];
if ( [child zOrder] < 0 )
[child visit];
else
break;
}
// self draw
[self draw];
// draw children zOrder >= 0
for( ; i < arrayData->num; i++ ) {
CCNode *child = arrayData->arr[i];
[child visit];
}
} else
[self draw];
[self afterDraw];
if ( grid_ && grid_.active)
[grid_ afterDraw:self];
glPopMatrix();
}
@end
/**************************************************************/
/** 滚动视图
*/
@implementation FMScrollView
// 摩擦阻力
@synthesize resistance;
// 惯性
@synthesize inertia;
// 使用视图大小初始化 默认先选中子项 左右滚动
+(FMScrollView*)viewWithViewSize:(CGSize)viewSize space:(CGFloat)space
{
return [self viewWithViewSize:viewSize space:space touchFunc:fmScrollBeforeTouch
orientation:fmScrollOrientationLeftToRight touchRect:CGRectZero];
}
// 使用视图大小初始化 默认 左右滚动
+(FMScrollView*)viewWithViewSize:(CGSize)viewSize space:(CGFloat)space touchFunc:(fmScrollTouchFunc)func
{
return [[[self alloc]initWithViewSize:viewSize space:space touchFunc:func
orientation:fmScrollOrientationLeftToRight touchRect:CGRectZero] autorelease];
}
// 使用视图大小初始化 touchRect触摸范围
+(FMScrollView*)viewWithViewSize:(CGSize)viewSize space:(CGFloat)space touchFunc:(fmScrollTouchFunc)func
orientation:(fmScrollOrientation)orientation touchRect:(CGRect)rect
{
return [[[self alloc]initWithViewSize:viewSize space:space touchFunc:func
orientation:orientation touchRect:rect] autorelease];
}
// 使用视图大小初始化 touchRect触摸范围
-(id)initWithViewSize:(CGSize)viewSize space:(CGFloat)space touchFunc:(fmScrollTouchFunc)func
orientation:(fmScrollOrientation)orientation touchRect:(CGRect)rect
{
self = [super init];
if (self) {
// 摩擦阻力
resistance = 0.03;
// 惯性
inertia = 20;
size = viewSize;
itemSpace = space;
touchFunc = func;
scrollOrientation = orientation;
touchRect.origin = rect.origin;
touchRect.size.width = (rect.size.width == 0) ? size.width : rect.size.width;
touchRect.size.height = (rect.size.height == 0) ? size.height : rect.size.height;
[self setContentSize:size];
inner = [[CCNode node]retain];
inner.position = touchRect.origin;
// 从右到左
if (scrollOrientation == fmScrollOrientationRightToLeft) {
[inner setLeft:size.width];
}
// 从上到下
if(scrollOrientation == fmScrollOrientationTopToBottom){
[inner setTop:size.height];
}
[self addChild:inner];
items = [[CCArray array]retain];
}
return self;
}
// 添加一个项目节点
-(void)addItemNode:(CCNode*)node
{
if(scrollOrientation == fmScrollOrientationLeftToRight){
// 从左向右排列
[node setLeft:inner.contentSize.width + itemSpace];
}else if(scrollOrientation == fmScrollOrientationRightToLeft){
// 从右向左排列 最后一个插入的应该在最左面 前面的对象都要向右排列
CGFloat left = node.contentSize.width + itemSpace;
for (int i = items.count - 1; i > -1; i--) {
CCNode* item = [items objectAtIndex:i];
[item setLeft:item.position.x + left];
}
[inner setLeft:inner.position.x - left];
}
if (scrollOrientation == fmScrollOrientationBottomToTop) {
// 从下到上排列
[node setTop:inner.contentSize.height + itemSpace];
}else if(scrollOrientation == fmScrollOrientationTopToBottom){
// 从上到下排列 最后一个插入的应该在最下面 前面的对象都要向上排列
CGFloat top = node.contentSize.height + itemSpace;
for (int i = items.count - 1; i > -1; i--) {
CCNode* item = [items objectAtIndex:i];
[item setTop:item.position.y + top];
}
[inner setTop:inner.position.y - top];
}
[items addObject:node];
[inner addChild:node];
[inner setContentSizeWithChilds];
// 设置可视区域
viewPosit.x = inner.contentSize.width - touchRect.size.width + touchRect.origin.x;
viewPosit.y = inner.contentSize.height - touchRect.size.height + touchRect.origin.y;
}
- (void) onEnter
{
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MAX swallowsTouches:YES];
[super onEnter];
}
- (void) onExit
{
[[CCTouchDispatcher sharedDispatcher] removeDelegate: self];
[super onExit];
}
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
if(![self containsTouchLocation:touch touchRect:touchRect])return NO;
[self stopAllActions];
// 摩擦系数
friction.x = 1;
friction.y = 1;
prePoint = startPoint = [touch locationInView:[touch view]];
preTimestamp = touch.timestamp;
// 设置定时器,如果用户在包含的某一项停留超过1秒,将触发该项的触摸行为,进而阻止滚动栏移动
[self saveTouchStartInfo:touch withEvent:event];
return YES;
}
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
if (currentItem != nil) {
// 先选中子项
if (touchFunc == fmScrollBeforeTouch) {
// 触摸发生移动行为,取消子项的触摸
if([currentItem respondsToSelector:@selector(ccTouchCancelled:withEvent:)])
[currentItem ccTouchCancelled:touch withEvent:event];
[self clearTouchStartInfo];
}else if(touchFunc == fmScrollAfterTouch){
// 转发触摸事件给选中项
if([currentItem respondsToSelector:@selector(ccTouchMoved:withEvent:)])
[currentItem ccTouchMoved:touch withEvent:event];
return;
}
}else{
// 取消对子项的选择
[self unschedule:@selector(waitItemTouchBegin)];
}
CGPoint touchPoint = [touch locationInView:[touch view]];
prePoint = startPoint;
preTimestamp = touch.timestamp;
// 当前位置
CGPoint endPoint = inner.position;
// 距离差距
CGPoint dfPoint = ccp(touchPoint.x - startPoint.x, touchPoint.y - startPoint.y);
// 需要移动到的结束坐标
CGPoint targetPoint = ccp(endPoint.x + dfPoint.x, endPoint.y + dfPoint.y);
startPoint = touchPoint;
if(scrollOrientation == fmScrollOrientationLeftToRight){
// 从左向右排列 移动时超出边界增加莫摩擦系数
if (targetPoint.x > touchRect.origin.x || targetPoint.x < -viewPosit.x) {
friction.x = MAX(0, friction.x - resistance);
dfPoint.x = dfPoint.x * friction.x;
}
endPoint.x += dfPoint.x;
}else if(scrollOrientation == fmScrollOrientationRightToLeft){
// 从右向左排列 最后一个插入的应该在最左面 前面的对象都要向右排列
if (targetPoint.x > MAX(touchRect.origin.x, -viewPosit.x) || targetPoint.x < -viewPosit.x) {
friction.x = MAX(0, friction.x - resistance);
dfPoint.x = dfPoint.x * friction.x;
}
endPoint.x += dfPoint.x;
}
if (scrollOrientation == fmScrollOrientationBottomToTop) {
// 从下到上排列 移动时超出边界增加莫摩擦系数
if (targetPoint.y > touchRect.origin.y || targetPoint.y < -viewPosit.y) {
friction.y = MAX(0, friction.y - resistance);
dfPoint.y = dfPoint.y * friction.y;
}
endPoint.y -= dfPoint.y;
}else if(scrollOrientation == fmScrollOrientationTopToBottom){
// 从上到下排列 最后一个插入的应该在最下面 前面的对象都要向上排列
if (targetPoint.y > MAX(touchRect.origin.y, -viewPosit.y) || targetPoint.y < -viewPosit.y) {
friction.y = MAX(0, friction.y - resistance);
dfPoint.y = dfPoint.y * friction.y;
}
endPoint.y -= dfPoint.y;
}
inner.position = endPoint;
}
- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
// 转发触摸事件给选中项
if (currentItem != nil) {
if([currentItem respondsToSelector:@selector(ccTouchEnded:withEvent:)])
[currentItem ccTouchEnded:touch withEvent:event];
[self clearTouchStartInfo];
return;
}else{
// 取消对子项的选择
[self unschedule:@selector(waitItemTouchBegin)];
}
CGPoint touchPoint = [touch locationInView:[touch view]];
NSTimeInterval timedef = touch.timestamp - preTimestamp;
// 当前位置
CGPoint endPoint = inner.position;
// 距离差距
CGPoint dfPoint = ccp(touchPoint.x - prePoint.x, touchPoint.y - prePoint.y);
// 需要移动到的结束坐标
CGPoint targetPoint = ccp(endPoint.x + (dfPoint.x / (timedef * inertia)), endPoint.y - (dfPoint.y / (timedef * inertia)));
if(scrollOrientation == fmScrollOrientationLeftToRight){
// 从左向右排列 移动时超出边界增加莫摩擦系数
endPoint.x = targetPoint.x;
if (targetPoint.x > touchRect.origin.x) {
endPoint.x = touchRect.origin.x;
}else if(endPoint.x < MIN(touchRect.origin.x, -viewPosit.x)){
endPoint.x = MIN(touchRect.origin.x, -viewPosit.x);
}
}else if(scrollOrientation == fmScrollOrientationRightToLeft){
// 从右向左排列 最后一个插入的应该在最左面 前面的对象都要向右排列
endPoint.x = targetPoint.x;
if (targetPoint.x < -viewPosit.x) {
endPoint.x = -viewPosit.x;
}else if (targetPoint.x > MAX(touchRect.origin.x, -viewPosit.x)) {
endPoint.x = MAX(touchRect.origin.x, -viewPosit.x);
}
}
if (scrollOrientation == fmScrollOrientationBottomToTop) {
// 从下到上排列
endPoint.y = targetPoint.y;
if (targetPoint.y > touchRect.origin.y) {
endPoint.y = touchRect.origin.y;
}else if(endPoint.y < MIN(touchRect.origin.y, -viewPosit.y)){
endPoint.y = MIN(touchRect.origin.y, -viewPosit.y);
}
}else if(scrollOrientation == fmScrollOrientationTopToBottom){
// 从上到下排列 最后一个插入的应该在最下面 前面的对象都要向上排列
endPoint.y = targetPoint.y;
if (targetPoint.y < -viewPosit.y) {
endPoint.y = -viewPosit.y;
}else if (targetPoint.y > MAX(touchRect.origin.y, -viewPosit.y)) {
endPoint.y = MAX(touchRect.origin.y, -viewPosit.y);
}
}
if (endPoint.x != inner.position.x || endPoint.y != inner.position.y) {
CCActionEase * action = [CCEaseElasticOut actionWithAction:[CCMoveTo actionWithDuration:1 position:endPoint] period:1];
[inner runAction: action];
}
}
// 保存触摸开始信息
-(void)saveTouchStartInfo:(UITouch *)touch withEvent:(UIEvent *)event
{
// 先选中子项
if (touchFunc == fmScrollBeforeTouch) {
// 查找是否选中某个实现触摸协议的子项
[self containsItemTouchBegin:touch withEvent:event];
}else if(touchFunc == fmScrollAfterTouch){
beginTouch = [touch retain];
beginEvent = [event retain];
[self schedule:@selector(waitItemTouchBegin) interval:1];
}
}
// 清除触摸开始信息
-(void)clearTouchStartInfo
{
[beginTouch release]; beginTouch= nil;
[beginEvent release]; beginEvent = nil;
[currentItem release]; currentItem = nil;
}
// 等待子项触摸开始
-(void)waitItemTouchBegin
{
[self unschedule:@selector(waitItemTouchBegin)];
// 查找是否选中某个实现触摸协议的子项
[self containsItemTouchBegin:beginTouch withEvent:beginEvent];
[beginTouch release]; beginTouch= nil;
[beginEvent release]; beginEvent = nil;
}
// 通知触摸包含项目触摸事件开始
-(BOOL)containsItemTouchBegin:(UITouch *)touch withEvent:(UIEvent *)event
{
for (id item in items) {
if ([item conformsToProtocol:@protocol(CCTargetedTouchDelegate)]
&& [item containsTouchLocation:touch]) {
currentItem = [item retain];
[currentItem ccTouchBegan:touch withEvent:event];
return YES;
}
}
return NO;
}
- (void)dealloc
{
[inner release]; inner = nil;
[items release]; items = nil;
[currentItem release]; currentItem = nil;
[beginTouch release]; beginTouch= nil;
[beginEvent release]; beginEvent = nil;
[super dealloc];
}
@end
转帖请注明出处:
http://www.cnrui.cn/blog/article.asp?id=267
#ScrollViewUtilty.h
复制内容到剪贴板
程序代码
//
// ScrollViewUtilty.h
// Shop
//
// Created by 胡 东平 on 12-1-1.
// Copyright (c) 2012年 Magnetjoy. All rights reserved.
//
#import "Utilties.h"
/** 溢出隐藏节点
*/
@interface OverflowNode : CCNode
// 绘制前
-(void)beforeDraw;
// 绘制后
-(void)afterDraw;
@end
// 触摸方法
typedef enum{
// 先选中子项目
fmScrollBeforeTouch,
// 后选中子项目
fmScrollAfterTouch
}fmScrollTouchFunc;
// 滚动方向
typedef enum{
// 从左到右滚动
fmScrollOrientationLeftToRight,
// 从右到左滚动
fmScrollOrientationRightToLeft,
// 从上到下滚动
fmScrollOrientationTopToBottom,
// 从下到上滚动
fmScrollOrientationBottomToTop
}fmScrollOrientation;
/**************************************************************/
/** 滚动视图
*/
@interface FMScrollView : OverflowNode<CCTargetedTouchDelegate>{
// 内部滚动栏
CCSprite* inner;
// 包含项目
CCArray* items;
// 视图大小
CGSize size;
// 间隔
CGFloat itemSpace;
// 触摸起始点
CGPoint startPoint;
// 摩擦系数
CGPoint friction;
// 摩擦阻力 默认0.03
CGFloat resistance;
// 惯性 默认20
CGFloat inertia;
// 前一次触摸纪录时间
NSTimeInterval preTimestamp;
// 前一次触摸点纪录
CGPoint prePoint;
// 可视大小
CGPoint viewPosit;
// 触摸方法
fmScrollTouchFunc touchFunc;
// 滚动方向
fmScrollOrientation scrollOrientation;
// 触摸范围
CGRect touchRect;
// 开始的触摸点
UITouch* beginTouch;
// 开始的事件
UIEvent* beginEvent;
// 当前选中的节点
CCNode<CCTargetedTouchDelegate>* currentItem;
}
// 摩擦阻力 默认0.03
@property(nonatomic)CGFloat resistance;
// 惯性 默认20
@property(nonatomic)CGFloat inertia;
// 使用视图大小初始化 默认先选中子项 左右滚动
+(FMScrollView*)viewWithViewSize:(CGSize)viewSize space:(CGFloat)space;
// 使用视图大小初始化 默认 左右滚动
+(FMScrollView*)viewWithViewSize:(CGSize)viewSize space:(CGFloat)space touchFunc:(fmScrollTouchFunc)func;
// 使用视图大小初始化 touchRect触摸范围
+(FMScrollView*)viewWithViewSize:(CGSize)viewSize space:(CGFloat)space touchFunc:(fmScrollTouchFunc)func orientation:(fmScrollOrientation)orientation touchRect:(CGRect)rect;
// 使用视图大小初始化 touchRect触摸范围
-(id)initWithViewSize:(CGSize)viewSize space:(CGFloat)space touchFunc:(fmScrollTouchFunc)func orientation:(fmScrollOrientation)orientation touchRect:(CGRect)rect;
// 添加一个项目节点
-(void)addItemNode:(CCNode*)node;
// 保存触摸开始信息
-(void)saveTouchStartInfo:(UITouch *)touch withEvent:(UIEvent *)event;
// 清楚触摸开始信息
-(void)clearTouchStartInfo;
// 等待子项触摸开始
-(void)waitItemTouchBegin;
// 通知触摸包含项目触摸事件开始
-(BOOL)containsItemTouchBegin:(UITouch *)touch withEvent:(UIEvent *)event;
@end
#ScrollViewUtilty.m
复制内容到剪贴板
程序代码
//
// ScrollViewUtilty.m
// Shop
//
// Created by 胡 东平 on 12-1-1.
// Copyright (c) 2012年 Magnetjoy. All rights reserved.
//
#import "ScrollViewUtilty.h"
/** 溢出隐藏节点
*/
@implementation OverflowNode
// 使用视图大小初始化
-(id) init
{
self = [super init];
if (self) {
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
// 绘制前
-(void)beforeDraw
{
GLfloat planeTop[] = {0.0f, -1.0f, 0.0f, self.boundingBoxInPixels.size.height};
GLfloat planeBottom[] = {0.0f, 1.0f, 0.0f, 0.0f};
GLfloat planeLeft[] = {1.0f, 0.0f, 0.0f, 0.0f};
GLfloat planeRight[] = {-1.0f, 0.0f, 0.0f, self.boundingBoxInPixels.size.width};
glClipPlanef(GL_CLIP_PLANE0, planeTop);
glClipPlanef(GL_CLIP_PLANE1, planeBottom);
glClipPlanef(GL_CLIP_PLANE2, planeLeft);
glClipPlanef(GL_CLIP_PLANE3, planeRight);
glEnable(GL_CLIP_PLANE0);
glEnable(GL_CLIP_PLANE1);
glEnable(GL_CLIP_PLANE2);
glEnable(GL_CLIP_PLANE3);
}
// 绘制后
-(void)afterDraw
{
glDisable(GL_CLIP_PLANE0);
glDisable(GL_CLIP_PLANE1);
glDisable(GL_CLIP_PLANE2);
glDisable(GL_CLIP_PLANE3);
}
// 绘制
-(void) visit
{
// quick return if not visible
if (!visible_)
return;
glPushMatrix();
if ( grid_ && grid_.active) {
[grid_ beforeDraw];
[self transformAncestors];
}
[self transform];
[self beforeDraw];
if(children_) {
ccArray *arrayData = children_->data;
NSUInteger i = 0;
// draw children zOrder < 0
for( ; i < arrayData->num; i++ ) {
CCNode *child = arrayData->arr[i];
if ( [child zOrder] < 0 )
[child visit];
else
break;
}
// self draw
[self draw];
// draw children zOrder >= 0
for( ; i < arrayData->num; i++ ) {
CCNode *child = arrayData->arr[i];
[child visit];
}
} else
[self draw];
[self afterDraw];
if ( grid_ && grid_.active)
[grid_ afterDraw:self];
glPopMatrix();
}
@end
/**************************************************************/
/** 滚动视图
*/
@implementation FMScrollView
// 摩擦阻力
@synthesize resistance;
// 惯性
@synthesize inertia;
// 使用视图大小初始化 默认先选中子项 左右滚动
+(FMScrollView*)viewWithViewSize:(CGSize)viewSize space:(CGFloat)space
{
return [self viewWithViewSize:viewSize space:space touchFunc:fmScrollBeforeTouch
orientation:fmScrollOrientationLeftToRight touchRect:CGRectZero];
}
// 使用视图大小初始化 默认 左右滚动
+(FMScrollView*)viewWithViewSize:(CGSize)viewSize space:(CGFloat)space touchFunc:(fmScrollTouchFunc)func
{
return [[[self alloc]initWithViewSize:viewSize space:space touchFunc:func
orientation:fmScrollOrientationLeftToRight touchRect:CGRectZero] autorelease];
}
// 使用视图大小初始化 touchRect触摸范围
+(FMScrollView*)viewWithViewSize:(CGSize)viewSize space:(CGFloat)space touchFunc:(fmScrollTouchFunc)func
orientation:(fmScrollOrientation)orientation touchRect:(CGRect)rect
{
return [[[self alloc]initWithViewSize:viewSize space:space touchFunc:func
orientation:orientation touchRect:rect] autorelease];
}
// 使用视图大小初始化 touchRect触摸范围
-(id)initWithViewSize:(CGSize)viewSize space:(CGFloat)space touchFunc:(fmScrollTouchFunc)func
orientation:(fmScrollOrientation)orientation touchRect:(CGRect)rect
{
self = [super init];
if (self) {
// 摩擦阻力
resistance = 0.03;
// 惯性
inertia = 20;
size = viewSize;
itemSpace = space;
touchFunc = func;
scrollOrientation = orientation;
touchRect.origin = rect.origin;
touchRect.size.width = (rect.size.width == 0) ? size.width : rect.size.width;
touchRect.size.height = (rect.size.height == 0) ? size.height : rect.size.height;
[self setContentSize:size];
inner = [[CCNode node]retain];
inner.position = touchRect.origin;
// 从右到左
if (scrollOrientation == fmScrollOrientationRightToLeft) {
[inner setLeft:size.width];
}
// 从上到下
if(scrollOrientation == fmScrollOrientationTopToBottom){
[inner setTop:size.height];
}
[self addChild:inner];
items = [[CCArray array]retain];
}
return self;
}
// 添加一个项目节点
-(void)addItemNode:(CCNode*)node
{
if(scrollOrientation == fmScrollOrientationLeftToRight){
// 从左向右排列
[node setLeft:inner.contentSize.width + itemSpace];
}else if(scrollOrientation == fmScrollOrientationRightToLeft){
// 从右向左排列 最后一个插入的应该在最左面 前面的对象都要向右排列
CGFloat left = node.contentSize.width + itemSpace;
for (int i = items.count - 1; i > -1; i--) {
CCNode* item = [items objectAtIndex:i];
[item setLeft:item.position.x + left];
}
[inner setLeft:inner.position.x - left];
}
if (scrollOrientation == fmScrollOrientationBottomToTop) {
// 从下到上排列
[node setTop:inner.contentSize.height + itemSpace];
}else if(scrollOrientation == fmScrollOrientationTopToBottom){
// 从上到下排列 最后一个插入的应该在最下面 前面的对象都要向上排列
CGFloat top = node.contentSize.height + itemSpace;
for (int i = items.count - 1; i > -1; i--) {
CCNode* item = [items objectAtIndex:i];
[item setTop:item.position.y + top];
}
[inner setTop:inner.position.y - top];
}
[items addObject:node];
[inner addChild:node];
[inner setContentSizeWithChilds];
// 设置可视区域
viewPosit.x = inner.contentSize.width - touchRect.size.width + touchRect.origin.x;
viewPosit.y = inner.contentSize.height - touchRect.size.height + touchRect.origin.y;
}
- (void) onEnter
{
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MAX swallowsTouches:YES];
[super onEnter];
}
- (void) onExit
{
[[CCTouchDispatcher sharedDispatcher] removeDelegate: self];
[super onExit];
}
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
if(![self containsTouchLocation:touch touchRect:touchRect])return NO;
[self stopAllActions];
// 摩擦系数
friction.x = 1;
friction.y = 1;
prePoint = startPoint = [touch locationInView:[touch view]];
preTimestamp = touch.timestamp;
// 设置定时器,如果用户在包含的某一项停留超过1秒,将触发该项的触摸行为,进而阻止滚动栏移动
[self saveTouchStartInfo:touch withEvent:event];
return YES;
}
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
if (currentItem != nil) {
// 先选中子项
if (touchFunc == fmScrollBeforeTouch) {
// 触摸发生移动行为,取消子项的触摸
if([currentItem respondsToSelector:@selector(ccTouchCancelled:withEvent:)])
[currentItem ccTouchCancelled:touch withEvent:event];
[self clearTouchStartInfo];
}else if(touchFunc == fmScrollAfterTouch){
// 转发触摸事件给选中项
if([currentItem respondsToSelector:@selector(ccTouchMoved:withEvent:)])
[currentItem ccTouchMoved:touch withEvent:event];
return;
}
}else{
// 取消对子项的选择
[self unschedule:@selector(waitItemTouchBegin)];
}
CGPoint touchPoint = [touch locationInView:[touch view]];
prePoint = startPoint;
preTimestamp = touch.timestamp;
// 当前位置
CGPoint endPoint = inner.position;
// 距离差距
CGPoint dfPoint = ccp(touchPoint.x - startPoint.x, touchPoint.y - startPoint.y);
// 需要移动到的结束坐标
CGPoint targetPoint = ccp(endPoint.x + dfPoint.x, endPoint.y + dfPoint.y);
startPoint = touchPoint;
if(scrollOrientation == fmScrollOrientationLeftToRight){
// 从左向右排列 移动时超出边界增加莫摩擦系数
if (targetPoint.x > touchRect.origin.x || targetPoint.x < -viewPosit.x) {
friction.x = MAX(0, friction.x - resistance);
dfPoint.x = dfPoint.x * friction.x;
}
endPoint.x += dfPoint.x;
}else if(scrollOrientation == fmScrollOrientationRightToLeft){
// 从右向左排列 最后一个插入的应该在最左面 前面的对象都要向右排列
if (targetPoint.x > MAX(touchRect.origin.x, -viewPosit.x) || targetPoint.x < -viewPosit.x) {
friction.x = MAX(0, friction.x - resistance);
dfPoint.x = dfPoint.x * friction.x;
}
endPoint.x += dfPoint.x;
}
if (scrollOrientation == fmScrollOrientationBottomToTop) {
// 从下到上排列 移动时超出边界增加莫摩擦系数
if (targetPoint.y > touchRect.origin.y || targetPoint.y < -viewPosit.y) {
friction.y = MAX(0, friction.y - resistance);
dfPoint.y = dfPoint.y * friction.y;
}
endPoint.y -= dfPoint.y;
}else if(scrollOrientation == fmScrollOrientationTopToBottom){
// 从上到下排列 最后一个插入的应该在最下面 前面的对象都要向上排列
if (targetPoint.y > MAX(touchRect.origin.y, -viewPosit.y) || targetPoint.y < -viewPosit.y) {
friction.y = MAX(0, friction.y - resistance);
dfPoint.y = dfPoint.y * friction.y;
}
endPoint.y -= dfPoint.y;
}
inner.position = endPoint;
}
- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
// 转发触摸事件给选中项
if (currentItem != nil) {
if([currentItem respondsToSelector:@selector(ccTouchEnded:withEvent:)])
[currentItem ccTouchEnded:touch withEvent:event];
[self clearTouchStartInfo];
return;
}else{
// 取消对子项的选择
[self unschedule:@selector(waitItemTouchBegin)];
}
CGPoint touchPoint = [touch locationInView:[touch view]];
NSTimeInterval timedef = touch.timestamp - preTimestamp;
// 当前位置
CGPoint endPoint = inner.position;
// 距离差距
CGPoint dfPoint = ccp(touchPoint.x - prePoint.x, touchPoint.y - prePoint.y);
// 需要移动到的结束坐标
CGPoint targetPoint = ccp(endPoint.x + (dfPoint.x / (timedef * inertia)), endPoint.y - (dfPoint.y / (timedef * inertia)));
if(scrollOrientation == fmScrollOrientationLeftToRight){
// 从左向右排列 移动时超出边界增加莫摩擦系数
endPoint.x = targetPoint.x;
if (targetPoint.x > touchRect.origin.x) {
endPoint.x = touchRect.origin.x;
}else if(endPoint.x < MIN(touchRect.origin.x, -viewPosit.x)){
endPoint.x = MIN(touchRect.origin.x, -viewPosit.x);
}
}else if(scrollOrientation == fmScrollOrientationRightToLeft){
// 从右向左排列 最后一个插入的应该在最左面 前面的对象都要向右排列
endPoint.x = targetPoint.x;
if (targetPoint.x < -viewPosit.x) {
endPoint.x = -viewPosit.x;
}else if (targetPoint.x > MAX(touchRect.origin.x, -viewPosit.x)) {
endPoint.x = MAX(touchRect.origin.x, -viewPosit.x);
}
}
if (scrollOrientation == fmScrollOrientationBottomToTop) {
// 从下到上排列
endPoint.y = targetPoint.y;
if (targetPoint.y > touchRect.origin.y) {
endPoint.y = touchRect.origin.y;
}else if(endPoint.y < MIN(touchRect.origin.y, -viewPosit.y)){
endPoint.y = MIN(touchRect.origin.y, -viewPosit.y);
}
}else if(scrollOrientation == fmScrollOrientationTopToBottom){
// 从上到下排列 最后一个插入的应该在最下面 前面的对象都要向上排列
endPoint.y = targetPoint.y;
if (targetPoint.y < -viewPosit.y) {
endPoint.y = -viewPosit.y;
}else if (targetPoint.y > MAX(touchRect.origin.y, -viewPosit.y)) {
endPoint.y = MAX(touchRect.origin.y, -viewPosit.y);
}
}
if (endPoint.x != inner.position.x || endPoint.y != inner.position.y) {
CCActionEase * action = [CCEaseElasticOut actionWithAction:[CCMoveTo actionWithDuration:1 position:endPoint] period:1];
[inner runAction: action];
}
}
// 保存触摸开始信息
-(void)saveTouchStartInfo:(UITouch *)touch withEvent:(UIEvent *)event
{
// 先选中子项
if (touchFunc == fmScrollBeforeTouch) {
// 查找是否选中某个实现触摸协议的子项
[self containsItemTouchBegin:touch withEvent:event];
}else if(touchFunc == fmScrollAfterTouch){
beginTouch = [touch retain];
beginEvent = [event retain];
[self schedule:@selector(waitItemTouchBegin) interval:1];
}
}
// 清除触摸开始信息
-(void)clearTouchStartInfo
{
[beginTouch release]; beginTouch= nil;
[beginEvent release]; beginEvent = nil;
[currentItem release]; currentItem = nil;
}
// 等待子项触摸开始
-(void)waitItemTouchBegin
{
[self unschedule:@selector(waitItemTouchBegin)];
// 查找是否选中某个实现触摸协议的子项
[self containsItemTouchBegin:beginTouch withEvent:beginEvent];
[beginTouch release]; beginTouch= nil;
[beginEvent release]; beginEvent = nil;
}
// 通知触摸包含项目触摸事件开始
-(BOOL)containsItemTouchBegin:(UITouch *)touch withEvent:(UIEvent *)event
{
for (id item in items) {
if ([item conformsToProtocol:@protocol(CCTargetedTouchDelegate)]
&& [item containsTouchLocation:touch]) {
currentItem = [item retain];
[currentItem ccTouchBegan:touch withEvent:event];
return YES;
}
}
return NO;
}
- (void)dealloc
{
[inner release]; inner = nil;
[items release]; items = nil;
[currentItem release]; currentItem = nil;
[beginTouch release]; beginTouch= nil;
[beginEvent release]; beginEvent = nil;
[super dealloc];
}
@end