Monthly Archives: February 2011

我想

我想开学
我想去上自习
我想去图书馆
我想去湖边坐着
我想吃大饼鸡排
我想去食堂, 东院西院A座心园宿舍都行
我想去上课
我想学习
我想住宿舍
我想搞竞赛
我想背单词
我想骑单车去上课
我想期末考试
我想复习
我想刚开学就想着赶紧寒暑假的时候
我想一没课我就往家跑的时候

How to Make a Very Fast TableView on Mac with Variant Cell Height

I recently started my project to build my own Sina Weibo (a Chinese Twitter clone) app for the Mac. I started with NSTableView just like how I usually deal with UITableView on iOS programming, but I finally found that NSTableView is very complex and it’s completely different with UITableView, especially for custom cells and variant cell height. I have to use NSCell subclass instead of NSView subclass in NSTableView. Luckily, I then found an article Making Cocoa List Views Really Fast which talks about the performance issue about NSTableView. The author of the article wrote his own implementation of NSTableView, just like a UITableViewPXListView.

The benefit of using PXListView is that I can finally use NSViews as my cell instead of NSCells, so that you can construct your cells very easily just like how you make a custom UITableViewCell on iOS. However, I still have many issues when using this framework. Here is the thing:

My app is a Twitter-like app, so I need to show the Tweet and the Retweet in one cell, and the height of my cell is some paddings + Tweet NSTextView height + Retweet NSTextView height, which is based on the width of my PXListView, that is, when I changed the width of my PXListView, the height of each cell will be changed.

I then found the NS(Attributed)String+Geometrics category by Sheep Systems, which solved some of my problems. But it is way too slow because I have to calculate the height of NSTextView twice – in PXListView delegate listView:heightOfRow and my custom cell’s drawRect method.

After I looked closely through the category by Sheep Systems, I found that it used NSTextContainer, NSTextStorage, NSLayoutManager to calculate the height of the NSTextView with a fixed width. Every time the method was called, I have to alloc, init, and finally release them. It was just a waste of memory and time. I then suddenly found that all the three necessary classes to calculate the text height are just right in an NSTextView instance. So I rewrote the category to this:

@implementation NSTextView (Geometrics)

- (CGFloat)heightForWidth {

    if ([[self textStorage] length] > 0) {

		// Checking for empty string is necessary since Layout Manager will give the nominal
		// height of one line if length is 0.  Our API specifies 0.0 for an empty string.

		// NSLayoutManager is lazy, so we need the following kludge to force layout:
		[self.layoutManager glyphRangeForTextContainer:self.textContainer];

		return [self.layoutManager usedRectForTextContainer:self.textContainer].size.height;
	}

	return 0;
}

@end

It’s very clean now and have a great performance. Another thing is that I still have to call it 2 times when the cell is scrolling. To solve this, I wrote a TextViewHeightCache, which will cache the height of the given text with a fixed width. Next time PXListView asked for the height, the TextViewHeightCache first checked wether or not the height of the provided width is available in the cache. If we found it, just return the corresponding height, otherwise, recalculate it.

#import "NSTextView+Geometrics.h"

@implementation SNTextViewHeightCache

- (id)init {

	if ((self = [super init])) {
		textView = [[NSTextView alloc] init];
		textViewSizes = [[NSMutableDictionary alloc] init];
	}

	return self;
}

- (CGFloat)heightForText:(NSAttributedString *)text withWidth:(CGFloat)width {

	NSValue *sizeValue = [textViewSizes objectForKey:text];
	if (sizeValue) {
		NSSize size = [sizeValue sizeValue];
		if (size.width == width) {
			return size.height;
		}
	}

	[[textView textStorage] setAttributedString:text];
	[textView setFrameSize:CGSizeMake(width, 0)];

	CGFloat height = [textView heightForWidth];

	[textViewSizes setObject:[NSValue valueWithSize:CGSizeMake(width, height)] forKey:text];

	return height;
}

- (void)dealloc {

	[textView release];
	[textViewSizes release];

	[super dealloc];
}

@end

Now we have a very fast table view with variant cell height, completely without any knowledge of NSTableView.

Hey you.

话说, 上个礼拜我回到北京就遇到了倒了血霉的事情, 我开完窗户, 就蹲下看那个牛奶有没有过期, 结果我一站起来, 惨剧就发生了, 头直接撞倒了窗户底下的牙子, 于是鲜血直流, 真是一点也不夸张啊, 我去拿纸的时候都直接流地上了. 结果过了一个礼拜, 现在我还没事头晕乎乎的, 也不知道什么时候才能彻底好. 还有那个眼睛, 医生说是鼻炎的问题, 我了个去的, 鼻炎竟然会眼睛疼.

前天天津下了一整天的雪, 而且有几个小时下的很大很大, 我也是第一次在天津看见下雪, 于是拍了一张照片.

然后昨天是那个情人节神马的, 啥也没干, 想去吃个饭, 结果饭馆各种人满为患, NND, 最后落脚到一家什么桂林餐厅, 吃完我就觉得, 那货真难吃. 还听说最近可能北京在禁止出租隔断间, 我靠, 这事要是真的, 我岂不是在北京混不下去了…

其实在北京的感觉就是, 总想回家.

小学同学聚会

=。=!说了好久要更新博客, 结果就一直给忘了! 我想找回我那个老博客的风格!!

前些日子就一直盼着春节的假期, 结果到了春节吧, 发现我没啥好做的, 结果就晃晃荡荡的到了现在. 我就纳了闷儿了, 我这眼睛怎么就又出毛病了, 我记得我11月份的时候因为感冒的原因眼睛就疼过一阵子, 结果好了后来, 现在又给我开始了. 这不能够啊, 我眼睛必须得赶紧好, 于是我三十儿和初一那两天早上都去了医院, 结果最后第二个医院说第一个医院的诊断是浮云, NND, 我也不知道信谁的, 于是这几天就是各种滴眼药, 眼药水换了三瓶, 可是… 到现在还是有点疼, 所以, 明儿还得去医院.

要说聚会, 也是奇了怪了, 没有初中聚会, 没有高中聚会, 却冒出来个小学聚会, 不过今天过的真不错啊, 看见了各种人, 虽说一开始都有点不老认识, 不过后来熟了一点之后也还觉得不错竟然, 感觉这次聚会还挺成功, 唯一的遗憾是没有个照片什么的, 竟然也没人拍最后, 真是的. 还有我想称赞一下那个Hellen’s餐厅, 很不错, 在天津还能找到这样的地方真不容易.

话说我最近觉得Cocoa编程有的地方比Cocoa touch要简单很多, 可是有的地方却很复杂, 比如那个挨千刀的NSCell, 简直不知道是什么玩意儿, 在Cocoa touch里根本木有NSCell那个东西, 所有的控件东西都是继承自UIView. 简单的东西吧, 就是那个Cocoa Bindings, 简直太方便了, 这俩平台应该互相改进一下, 就完美了. 还有一个问题就是讨论Cocoa的太少了, 一般都是讨论Cocoa touch的, 感觉iOS的用户貌似应该比Mac的用户要高都… 真是奇怪.