純代碼NSOutlineView

在mac開發(fā)中商源,常常需要用類似列表的結(jié)構(gòu)來展示數(shù)據(jù)唇兑,最好還有層級极谊,類似于樹俯抖,比如族譜兢交、菜單等層級結(jié)構(gòu)數(shù)據(jù)旧找。這個(gè)時(shí)候NSTableView已經(jīng)不能夠很好的滿足我們了浴韭,NSOutlineView則給我們提供了更好的選擇核畴。

NSOutlineView是NSTableView的子類域慷,但是他比NSTableView多了分組的功能严就,使用層級的數(shù)據(jù)結(jié)構(gòu)总寻。

現(xiàn)在NSOutlineView的教程還不是很多,不夠全面梢为,大多都是使用Storyboard/xib開發(fā)渐行,下面以一個(gè)Demo為例介紹純代碼實(shí)現(xiàn)使用NSOutlineView顯示層級數(shù)據(jù)結(jié)構(gòu)的過程。

首先創(chuàng)建一個(gè)OS X項(xiàng)目工程铸董,創(chuàng)建一個(gè)NSViewController祟印,在ViewController中添加NSOutlineView對象(outlineView)并實(shí)現(xiàn)NSOutlineViewDataSource和NSOutlineViewDelegate相應(yīng)的方法。

第一步粟害,我們需要初始化outlineView并設(shè)置相關(guān)屬性蕴忆。

(需要注意的是,outlineView必須放在scrollView上才可以顯示我磁,所以我們在初始化outlineView的時(shí)候孽文,需要將outlineView作為scrollView的documentView展示。)

NSTableColumn *tableColumn = [[NSTableColumn alloc] init];

tableColumn.resizingMask = NSTableColumnAutoresizingMask;

NSOutlineView *outlineView = [[NSOutlineView alloc] init];

outlineView.allowsColumnResizing = YES;

outlineView.headerView = nil;

outlineView.columnAutoresizingStyle = NSTableViewFirstColumnOnlyAutoresizingStyle;

outlineView.usesAlternatingRowBackgroundColors = YES;//背景顏色的交替夺艰,一行白色芋哭,一行灰色。

[outlineView addTableColumn:tableColumn];

NSScrollView *scrollView = [[NSScrollView alloc] init];

scrollView.documentView = outlineView;

scrollView.hasVerticalScroller = YES;

scrollView.autohidesScrollers = YES;

self.view = scrollView;

另外需要注意的一點(diǎn)是郁副,我們需要實(shí)現(xiàn)loadView方法减牺。

第二步,我們需要準(zhǔn)備好用于展示的數(shù)據(jù)存谎。

如果在項(xiàng)目中拔疚,數(shù)據(jù)可以通過model的形式從外部直接傳過來,demo里我們就直接初始化既荚。這里需要注意稚失,outlinView需要的數(shù)據(jù)是一種類型的(父子節(jié)點(diǎn)數(shù)據(jù)是同種類型)。

-(void)initData {

//第一級根節(jié)點(diǎn)

_dataModel? = [[OutlineViewDataModel alloc] init];

_dataModel.name = @"root";

//2級節(jié)點(diǎn)

OutlineViewDataModel *level1Node = [[OutlineViewDataModel alloc]init];

level1Node.name = @"節(jié)點(diǎn)1";

OutlineViewDataModel *level2Node = [[OutlineViewDataModel alloc]init];

level2Node.name = @"節(jié)點(diǎn)2";

OutlineViewDataModel *level3Node = [[OutlineViewDataModel alloc]init];

level3Node.name = @"節(jié)點(diǎn)3";

//1

OutlineViewDataModel *level11Node = [[OutlineViewDataModel alloc]init];

level11Node.name = @"節(jié)點(diǎn)1-1";

OutlineViewDataModel *level12Node = [[OutlineViewDataModel alloc]init];

level12Node.name = @"節(jié)點(diǎn)1-2";

OutlineViewDataModel *level111Node = [[OutlineViewDataModel alloc]init];

level111Node.name = @"節(jié)點(diǎn)1-1-1";

OutlineViewDataModel *level112Node = [[OutlineViewDataModel alloc]init];

level112Node.name = @"節(jié)點(diǎn)1-1-2";

level11Node.childNodes = @[level111Node,level112Node];

level1Node.childNodes = @[level11Node,level12Node];

//2

OutlineViewDataModel *level21Node = [[OutlineViewDataModel alloc]init];

level21Node.name = @"節(jié)點(diǎn)2-1";

OutlineViewDataModel *level22Node = [[OutlineViewDataModel alloc]init];

level22Node.name = @"節(jié)點(diǎn)2-2";

level2Node.childNodes = @[level21Node,level22Node];

//3

OutlineViewDataModel *level31Node = [[OutlineViewDataModel alloc]init];

level31Node.name = @"節(jié)點(diǎn)3-1";

OutlineViewDataModel *level311Node = [[OutlineViewDataModel alloc]init];

level311Node.name = @"節(jié)點(diǎn)3-1-1";

OutlineViewDataModel *level312Node = [[OutlineViewDataModel alloc]init];

level312Node.name = @"節(jié)點(diǎn)3-1-2";

level31Node.childNodes = @[level311Node,level312Node];

level3Node.childNodes = @[level31Node];

_dataModel.childNodes = @[level1Node,level2Node,level3Node];

}

其中model的數(shù)據(jù)結(jié)構(gòu):

@property(nonatomic, strong) NSString *name;

@property(nonatomic, strong) NSArray *childNodes;

第三步恰聘,根據(jù)需要自定義NSTableCellView句各。

除了自定義布局和樣式吸占,還需要在自定義的cellview中實(shí)現(xiàn)數(shù)據(jù)的展示:

//? OutlineItemCellView.m

-(void)setObjectValue:(id)objectValue {

? ? [super setObjectValue:objectValue];

? ? if (self.objectValue) {

? ? ? ? if ([self.objectValue isKindOfClass:[OutlineViewDataModel class]]) {

? ? ? ? ? ? OutlineViewDataModel *model = (OutlineViewDataModel *)self.objectValue;

? ? ? ? ? ? [self updateControlsForDataObject:model.name];

? ? ? ? }

? ? }

}

-(void)updateControlsForDataObject:(NSString *)name {

? ? self.textField.stringValue = name;

? ? self.textField.textColor = [NSColor whiteColor];

}

第四步,根據(jù)需要自定義NSTableHeaderCell凿宾。

類似于NSTableCellView矾屯,自定義樣式和數(shù)據(jù)展示即可。

第五步初厚,在viewController中實(shí)現(xiàn)NSOutlineViewDataSource和NSOutlineViewDelegate的方法件蚕。

下面介紹一下幾個(gè)NSOutlineViewDataSource方法,根據(jù)需要選擇性實(shí)現(xiàn):

1.每一層級節(jié)點(diǎn)包含的下一級節(jié)點(diǎn)的數(shù)量产禾。

-(NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {

? ? //當(dāng)item為空時(shí)表示根節(jié)點(diǎn).

? ? if (item != nil && [item isKindOfClass:[OutlineViewDataModel class]]) {

? ? ? ? return ((OutlineViewDataModel *)item).childNodes.count;

? ? }

? ? return _dataModel.childNodes.count;

}

2.每一層級節(jié)點(diǎn)的模型對象為item時(shí),根據(jù)item獲取子節(jié)點(diǎn)模型排作。item為nil空時(shí)表示獲取頂級節(jié)點(diǎn)模型。

-(id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {

? ? if(!item) {

? ? ? ? return _dataModel.childNodes[index];

? ? }else {

? ? ? ? OutlineViewDataModel *model = item;

? ? ? ? return model.childNodes[index];

? ? }

}

3.節(jié)點(diǎn)是否可以打開/關(guān)閉亚情。

-(BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {

? ? //count 大于0表示有子節(jié)點(diǎn),需要允許Expandable

? ? if(!item) {

? ? ? ? return [_dataModel.childNodes count]>0 ;

? ? }else {

? ? ? ? OutlineViewDataModel *model = item;

? ? ? ? return [model.childNodes count]>0;

? ? }

}

4.設(shè)置每個(gè)數(shù)據(jù)載體對應(yīng)的具體數(shù)據(jù)纽绍,根據(jù)節(jié)點(diǎn)模型對象item,更新節(jié)點(diǎn)視圖。

-(id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {

? ? //? ? return ([item isKindOfClass:OutlineViewDataModel.class] ? [item name] : item);

? ? return item;

}

這個(gè)地方用于向自定義的cellView傳遞數(shù)據(jù)势似,如果return item.name拌夏,則那邊接收到的“self.objectValue”就是item.name。

5.展開的節(jié)點(diǎn)是否自動(dòng)保存

-(id)outlineView:(NSOutlineView *)outlineView itemForPersistentObject:(id)object ;

6.自定義row

-(NSTableRowView *)outlineView:(NSOutlineView *)outlineView rowViewForItem:(id)item ;

下面是NSOutlineViewDelegate的幾個(gè)方法:

1.根據(jù)tableColumn定義的cell的唯一標(biāo)示identifier,調(diào)用makeViewWithIdentifier創(chuàng)建result視圖,更新item模型中數(shù)據(jù)到result視圖元素中履因。

-(NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item {

? ? OutlineItemCellView *cellView = [outlineView makeViewWithIdentifier:@"cellView" owner:nil];

? ? if (cellView == nil) {

? ? ? ? cellView = [[OutlineItemCellView alloc] init];

? ? ? ? cellView.identifier = @"cellView";

? ? }

? ? return cellView;

}

這里需要注意cellView一開始要復(fù)用是沒有的障簿,所以要初始化一下。storyboard實(shí)現(xiàn)outlineView不需要這個(gè)操作栅迄,所以純代碼實(shí)現(xiàn)的時(shí)候容易忽略這點(diǎn)站故。

2.是否繪制“組行”樣式

-(BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item {

? ? if (item && [item isKindOfClass:OutlineViewDataModel.class] && ((OutlineViewDataModel *)item).childNodes) {

? ? ? ? return true;

? ? }else {

? ? ? ? return false;

? ? }

}

3.設(shè)置每一行的高度

-(CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item ;

4.獲取到選擇節(jié)點(diǎn)后的通知

-(void)outlineViewSelectionDidChange:(NSNotification *)notification ;

這里可以獲取點(diǎn)擊節(jié)點(diǎn)的相關(guān)信息,根據(jù)需要作出相應(yīng)處理毅舆。比如打印節(jié)點(diǎn)name:

NSOutlineView *treeView = notification.object;

NSInteger row = [treeView selectedRow];

OutlineViewDataModel*model = (OutlineViewDataModel*)[treeView itemAtRow:row];

NSLog(@"name = %@",model.name);

以上五步完成之后西篓,運(yùn)行程序大概是這個(gè)樣子:


圖1

如果我們想要自定義每行的背景色以及點(diǎn)擊高亮色,該怎么做呢憋活?

在OutlineItemCellView中設(shè)置背景色岂津?那效果是這樣的...


圖2

因?yàn)榧^那部分并不屬于cellView,cellView的下面還有一層rowView悦即。

這個(gè)時(shí)候我們需要自定義NSTableRowView吮成,在OutlineRowView中設(shè)置相應(yīng)樣式處理,并在ViewController中實(shí)現(xiàn) (NSTableRowView *)outlineView:(NSOutlineView *)outlineView rowViewForItem:(id)item方法辜梳,效果如下:

圖3
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末粱甫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子作瞄,更是在濱河造成了極大的恐慌茶宵,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宗挥,死亡現(xiàn)場離奇詭異乌庶,居然都是意外死亡叶摄,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進(jìn)店門安拟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人宵喂,你說我怎么就攤上這事糠赦。” “怎么了锅棕?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵拙泽,是天一觀的道長。 經(jīng)常有香客問我裸燎,道長顾瞻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任德绿,我火速辦了婚禮荷荤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘移稳。我一直安慰自己蕴纳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布个粱。 她就那樣靜靜地躺著古毛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪都许。 梳的紋絲不亂的頭發(fā)上稻薇,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天,我揣著相機(jī)與錄音胶征,去河邊找鬼塞椎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛睛低,可吹牛的內(nèi)容都是我干的忱屑。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼暇昂,長吁一口氣:“原來是場噩夢啊……” “哼莺戒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起急波,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤从铲,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后澄暮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體名段,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡阱扬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了伸辟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片麻惶。...
    茶點(diǎn)故事閱讀 40,015評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖信夫,靈堂內(nèi)的尸體忽然破棺而出窃蹋,到底是詐尸還是另有隱情,我是刑警寧澤静稻,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布警没,位于F島的核電站,受9級特大地震影響振湾,放射性物質(zhì)發(fā)生泄漏杀迹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一押搪、第九天 我趴在偏房一處隱蔽的房頂上張望树酪。 院中可真熱鬧,春花似錦大州、人聲如沸嗅回。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绵载。三九已至,卻和暖如春苛白,著一層夾襖步出監(jiān)牢的瞬間娃豹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工购裙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留懂版,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓躏率,卻偏偏與公主長得像躯畴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子薇芝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評論 2 355

推薦閱讀更多精彩內(nèi)容