众所周知,任何一个成功的NSControl(见注释1)背后都有一个NSCell。因为多数功能都可以通过NSControl直接实现,因此NSCell一直是被人忽略的角色。今天就和大家一起走进NSCell的世界,简单谈谈NSCell的常用,实用用法。
大家打开itunes,mail,或者Finder,都会看到左边的树形列秒(NSOutlineView).每一行,即一个Cell中,既有图片,又有文字。并且整个outlineView只有一列,要实现这种效果,就只有靠操作Cell了。
就以NSOutlineView作为例子。利用outlineView的
- (id) outlineView: (NSOutlineView *) aOutlineView objectValueForTableColumn: (NSTableColumn *) tableColumn
byItem: (id) item
这个delegate方法可以根据不同节点(item)显示不同的值,当然这个值不能是图片。因此我们要实现左边的图片就只能依靠cell了。
因为apple没有提供一个text and image的NSCell子类,因此我们需要自己建立这样一个类,建立一个新类,取名NBImageAndTextCell(恩,这个名字很规范,很低调又很华丽),并继承至NSTextFieldCell。怎么把这个类和我们的NSControl(即outlineView)联系起来呢,当然NSControl有个叫setCell的方法可以做,但是既然我们有IB(注释2),于是就在IB里设置即可,方便切安全。打开IB选中你的outlineView,再选中其中的column,然后再选中cell。这过程可以通过连续的 command+ctrl+ 方向键下 完成。在cell的inspector窗口(command+shift+I 打开)的identity中将 Class identity改成“NBImageAndTextCell"回车即可,现在我们的cell类就属于这个outlineView了
在outlineView的delegate类中使用delegate方法
- (void)outlineView:(NSOutlineView *)ov willDisplayCell:(NSCell *)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
NBImageAndTextCell *imageAndTextCell = (NBImageAndTextCell *)cell;
[imageAndTextCell setImage:[item itemImage]];
}
顾名思义,"willDisplayCell"就是在即将要显示cell又还没显示cell的那个时候,我们在这个时候把我们要显示的图片通过 setImage 方法 传给cell。 这个image一般就是node的某个属性,这样就可以根据不同的node显示不同的图片。比如Finder中文件夹node的图片就是蓝色小文件夹图
在 NBImageAndTextCell中声明一个 NSImage *image 和一个- (void)setImage:(NSImage *)anImage;方法。这个setImage方法就是我们上面用到的。
- (void)setImage:(NSImage *)anImage
{
if (anImage != image)
{
[image release];
image = [anImage retain];
}
}
接下来就是如何在cell中画上这个image的方法了。这是核心所在
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
if (image != nil)
{
[image setSize:(NSSize){16,16}];
NSRect imageFrame;
NSSize imageSize = [image size];
NSDivideRect(cellFrame, &imageFrame, &cellFrame,imageSize.width+5, NSMinXEdge);
if ([self drawsBackground]) {
[[self backgroundColor] set];
NSRectFill(imageFrame);
}
imageFrame.origin.x += 3;
imageFrame.size = imageSize;
[image compositeToPoint:imageFrame.origin operation:NSCompositeSourceOver];
}
[super drawInteriorWithFrame:cellFrame inView:controlView];
}
这个draWithFrame继承至NSTextFieldCell,通过这个方法来画上Cell。参数cellFrame就是这个Cell在control(即outlineView)每一行的frame,因此你的outlineView的一行多高多宽,位置在哪儿就是这个cellFrame了。
看第一句 if (image != nil),这个image是在outlineView的delegate方法里通过setImage传进来的,因此如果我们想要某一个节点没有任何图片的话,就传个nil进来,于是就不会执行括号内画图的方法了。
接下来就是核心
NSDivideRect(cellFrame, &imageFrame, &cellFrame,imageSize.width+5, NSMinXEdge);
这个NSDivideRect就是一个切割cell的方法,里面的4个参数当然就是决定你怎么切的。
简单来说,
第一个参数是:在哪个里面切 这里是cellFrame,意思是我们在整个cell内切
第二个:切出来的东西放哪儿 这里是imageFrame一个出参,就是切出来的范围一个Frame 赋给imageFrame
第三个:切剩下的放哪儿 一个出参cellFrame,切后的放入cellFrame,也就说之后的cellFrame就自由我们切剩下的那么大了
第四个:切的边界举例cell边界多远 距离边界imageSize.width+5这么远
第五个:从cell的哪边切 NSMinXEdge是靠cell左边,NSMaxXEdge是右边
于是现在我们的imageFrame就是我们要放图片的地方和大小,cellFrame就是剩下的cell大小和位置了
经过小的调整 imageFrame.origin.x += 3; imageFrame.size = imageSize; 后,就调用[image compositeToPoint:imageFrame.origin operation:NSCompositeSourceOver];
在这个范围内画上我们的image即可
最后千万别忘了[super drawInteriorWithFrame:cellFrame inView:controlView];没有这个,cell都不会被draw上,自然我们切莱切去,画来画去的东西都不能显示了
这个也是我们要继承NSTextFieldCell的原因,就是为了用这个方法。
ok,大功告成,你的outlineView可以图文并茂了,以后还想切一块出来显示点其他什么图啊什么的,就随便你了。
小白注释1:NSControl 是NSView子类,如NSTextField,NSButton,NSTableView 等
小白注释2:IB=Interface Builder