SWTableView 经验


最近做的游戏有一块是涉及到表格的,实际开发中大家应该也会用到表格,但cocos2d中没有表格,只有menu,用menu制作表格很不方便,且没有滚动效果,如果使用UIKit里面的UITableView似乎又不太方便,这里我和大家分享一下我的经验。

cocos官网上面有一个CCTableView类,现在已经改名为SWTableView了,链接:http://www.cocos2d-iphone.org/archives/943
用过它的人都估计一部分都吃过苦头,不知道是不是cocos2d原作者提供的,把文件复制到工程里面编译出一堆错误。错误倒是好改,不过是加些头文件,删掉debug等等,这些都不是问题,最关键的有一个坑爹的问题,后面再提

下载的文件共有12个,真正用到的有3个类:SWTableView、SWMultiColumnTableView、SWTableViewSpriteCell,第一个是列表类,第二个是表格类,第三个是表格单元类,单元类可以用在前两个类里面,其中表格类继承于列表类,列表类继承于滑动视图类SWScrollView(不会直接用到),与cocos2d类风格不同,SW类的风格更接近于Cocoa,也就是它是MVC结构的,虽然它们不是抽象类,可以直接创建,但没有代理的协助是无法有效工作的,很明确SW也希望你这样做,既必须实现SWTableViewDelegate和SWTableViewDataSource协议,首先创建一个类,可以直接继承SWTableView或SWMultiColumnTableView,也可以创建一个继承于CCLayer的类,并声明协议SWTableViewDelegate和SWTableViewDataSource,然后实现这四个方法:
-(void)table:(SWTableView *)table cellTouched:(SWTableViewCell *)cell;
-(CGSize)cellSizeForTable:(SWTableView *)table;
-(SWTableViewCell *)table:(SWTableView *)table cellAtIndex:(NSUInteger)idx;
-(NSUInteger)numberOfCellsInTableView:(SWTableView *)table;

第一个方法是触摸单元格的回调
第二个方法提供单元格的尺寸
第三个方法用于创建单元格
第四个方法告诉表格单元格的数目
这四个方法都不是你主动调用的,全是回调,如果仔细看代码或打断点会发现后三个回调的调用顺序(第一个就不说了,触摸时调用),先第二个方法,再第四个方法,最后第三个方法,第二和第四个方法肯定会被调用,第三个则不见得,当第四个方法的返回值为0时,第三个方法不会被调用,也就是你告诉了TableView没有元素时,TableView不会让你创建单元格,相反,如果TableView里面原来有单元格,这时你设置单元格数目为0,TableView还会自动删掉单元格,也就是TableView的行为是实时的,当然这个实时也是人为控制的,你不主动告诉TableView的单元格发生改变,它也是不会更新的,这个主动方法是reloadData。

这四个方法的真正实现取决于你自己,但第三个方法需要说明一下,其实这在官网上面也有代码说明,也就是你总是要先询问TableView里面是否存放了空闲的单元格,如果没有则自己生成一个,象这样:
SWTableViewSpriteCell *cell = (SWTableViewSpriteCell *) [table dequeueCell];
if (!cell) {
    cell = [[[SWTableViewSpriteCell alloc] init] autorelease];
}
方法最后要返回这个cell

最后说明一下那个坑爹的问题,找了我一个晚上。当我测试我的TableView的时候,我发现总是只显示一部分,而且无论怎么改坐标和尺寸,显示区域总是不正常,打LOG看上去都是正常的,我甚至一度怀疑是openGL的问题,把该显示的一部分抹掉了,但是为什么cocos2d的显示就没问题呢。最后我终于找到了根源,SW里面用到了cococs2d里没有用到的一个gl函数:glScissor,这个函数用于设置剪切区域,设置了剪切区域,在该区域里的绘制才会有效,但是,关键就是这个但是,它是基于竖屏的,而我的TableView是在横屏下的,TNND,严重坑爹啊。网上关于SW的信息少的可怜,关于这一点的几乎没有。。。终于,终于发现了一篇文章阐述了这一点的,就是写那本《iphone & iPad cocos2d游戏开发实战》的人说的,在它的官网上。他写了个类,针对需要旋转屏幕时剪切区域需要变化的类,继承于CCNode。当然,他的做法本质上解决了这个问题,但难道要我们把cocos2d里所有继承于CCNode的类都改掉。不现实。由于glScissor只用在了SW里面,我们稍加处理就可以了。

在SWScrollView类的实现里加一个方法:
-(CGRect) clippingRect:(CGRect) r {
    CGRect ret;
    CCDirector *director = [CCDirector sharedDirector];
    CGSize winSize = director.winSize;
    if(director.deviceOrientation == kCCDeviceOrientationPortraitUpsideDown) {
        r.origin.x = winSize.width - r.size.width - r.origin.x;
        r.origin.y = winSize.height - r.size.height - r.origin.y;
    } else if(director.deviceOrientation == kCCDeviceOrientationLandscapeLeft) {
        r.origin = CGPointMake(r.origin.y, winSize.width - r.size.width - r.origin.x);
        r.size = CGSizeMake(r.size.height, r.size.width);
    } else if(director.deviceOrientation == kCCDeviceOrientationLandscapeRight) {
        r.origin = CGPointMake(winSize.height - r.size.height - r.origin.y, r.origin.x);
        r.size = CGSizeMake(r.size.height, r.size.width);
    }
    r = CC_RECT_POINTS_TO_PIXELS(r);
    ret = CGRectMake(r.origin.x * scaleX_, r.origin.y * scaleY_, 
                                r.size.width * scaleX_, r.size.height * scaleY_);
    return ret;
}
别忘了声明

修改-(void)beforeDraw方法:
-(void)beforeDraw {
    if (clipsToBounds_) {
        glEnable(GL_SCISSOR_TEST);
        const CGFloat s = [[CCDirector sharedDirector] contentScaleFactor];
        CGRect rect = CGRectMake(self.position.x*s, self.position.y*s, viewSize_.width*s, viewSize_.height*s);
        rect = [self clippingRect:rect];
        glScissor(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
        //glScissor(self.position.x*s, self.position.y*s, viewSize_.width*s, viewSize_.height*s);
    }
}

OK!大公告成,实际的显示效果还是可以的。

祝大家工作愉快

prompthu2012-06-16 16:36
不好意思,有一点没有说明,上面说到,需要创建你自己的TableView并实现两个协议,但仅实现协议还不够,这两个协议是需要主动设置到SWTableView或SWMutliColumnTableView里面去的,实际上就是设置自己。比如我的代码:

@interface Gallery : SWMultiColumnTableView <SWTableViewDelegate, SWTableViewDataSource> {
    CCArray *elements_;
    CGSize cellSize_;
}
@property (nonatomic, retain) CCArray *elements;
+(Gallery*) galleryWithSize:(CGSize)size colCount:(int)count andRowHeight:(float)h;
-(Gallery*) initWithSize:(CGSize)size colCount:(int)count andRowHeight:(float)h;
@end


@implementation Gallery

@synthesize elements = elements_;

+(Gallery*) galleryWithSize:(CGSize)size colCount:(int)count andRowHeight:(float)h {
    return [[[Gallery alloc] initWithSize:size colCount:count andRowHeight:h] autorelease];
}

-(BodyTypeGallery*) initWithSize:(CGSize)size colCount:(int)count andRowHeight:(float)h {
    if(self = [super initWithViewSize:size]) {
        self.isTouchEnabled = YES;
        self.direction = SWScrollViewDirectionVertical;
        self.dataSource = self;        // 这里
        self.delegate = self;         // 还有这里
        self.verticalFillOrder = SWTableViewFillTopDown;
        self.colCount = count;
        
        cellSize_ = CGSizeMake(size.width / count, h);
    }
    return self;
}

...

@end

最后一点提醒,由于单元格的添加、删除和更新是由用户控制的,所以在我的代码里没有在初始化时设置elements,这保持了原来的风格,当然你也可以不必这样做,在初始化时设置也可以,看你怎么设计了,不过无论如何,记住如果要更新内容,必须要调用reloadData
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值