本篇 macOS NSTableView 教程已經(jīng)更新到 Xcode 8 和 Swift 3蒜绽。

Table view 是 macOS 應(yīng)用程序中最普遍的控件之一司澎,“郵件”的消息列表和 Spotlight 的搜索結(jié)果都是我們很熟悉的例子。它可以讓 Mac 用好看的方式展示表格數(shù)據(jù)癌佩。
NSTableView 按行和列排列數(shù)據(jù)碌嘀。每一行表示給定的數(shù)據(jù)集中的單個(gè)模型對(duì)象,每一列表示這個(gè)模型對(duì)象的某個(gè)屬性傍念。
在這篇 macOS NSTableView 教程中矫夷,我們會(huì)使用 table view 創(chuàng)建一個(gè)實(shí)用的文件瀏覽器葛闷,看起來(lái)會(huì)和 Finder 驚人的相似。學(xué)習(xí)過程中双藕,你會(huì)學(xué)到很多關(guān)于 table view 的知識(shí)淑趾,例如:
- 如何填充 table view
- 如何改變它的視覺風(fēng)格
- 如何對(duì)用戶交互作出反應(yīng),例如選擇或雙擊
準(zhǔn)備好創(chuàng)建第一個(gè) table view 了嗎忧陪?繼續(xù)扣泊!
開始
下載起始項(xiàng)目然后在 Xcode 中打開。
構(gòu)建并運(yùn)行嘶摊,查看起始項(xiàng)目:

這里有一張空白的畫布延蟹,我們會(huì)在這里創(chuàng)建一個(gè)炫酷的文件瀏覽器。起始 app 已經(jīng)具備了一些教程后面會(huì)用到的功能叶堆。
保持應(yīng)用程序開啟阱飘,選擇 File > Open…(或者用快捷鍵 Command+O )。

從彈出的新窗口中虱颗,隨便選一個(gè)文件夾沥匈,然后點(diǎn) Open 按鈕。你會(huì)在 Xcode 的 console 中看到這些東西:
Represented object: file:///Users/tutorials/FileViewer/FileViewer/
這條消息顯示了選中的文件夾的路徑忘渔,初始項(xiàng)目中的代碼將該 URL 傳遞到視圖控制器高帖。
如果你好奇這是如何實(shí)現(xiàn)的,可以看看這些:
- Directory.swift:包含 Directory 結(jié)構(gòu)體的實(shí)現(xiàn)畦粮,它讀取了目錄的內(nèi)容散址。
- WindowController.swift:包含顯示文件夾選擇面板以及將選擇的目錄傳遞給 ViewController 的代碼。
-
ViewController.swift:包含
ViewController
類的實(shí)現(xiàn)锈玉,今天我們會(huì)花時(shí)間處理這個(gè)類爪飘。這是創(chuàng)建 table view 和顯示文件列表的地方。
創(chuàng)建 Table View
打開項(xiàng)目導(dǎo)航器中的 Main.storyboard拉背。選中 View Controller Scene 然后從對(duì)象庫(kù)中拖一個(gè) table view 到視圖中师崎。容器已經(jīng)準(zhǔn)備好了,就是視圖層級(jí)中的 Table Container椅棺。

下一步犁罩,需要添加一些約束。點(diǎn)擊自動(dòng)布局工具條上的 Pin 按鈕两疚。在彈出的窗口里床估,如下設(shè)置邊緣約束:
-
Top,Bottom诱渤,Leading 和 Trailing:0丐巫。
確保將 Update Frames 設(shè)置為 Items of New Constraints,然后點(diǎn)擊 Add 4 Constraints。
看看新創(chuàng)建的 table view 的結(jié)構(gòu)递胧。人如其名碑韵,它是典型的表格結(jié)構(gòu):
- 由行和列組成。
- 每行代表數(shù)據(jù)模型集中的單個(gè)項(xiàng)目缎脾。
- 每列表示模型的某個(gè)屬性祝闻。
- 每列可以有標(biāo)題行。
- 標(biāo)題行描述列中的數(shù)據(jù)遗菠。
如果你熟悉 iOS 上的 UITableView
联喘,那你會(huì)對(duì)它也感到熟悉,但在 macOS 上會(huì)深入很多辙纬。實(shí)際上豁遭,你可能會(huì)對(duì)組成 NSTableView
的對(duì)象層次結(jié)構(gòu)中的不同 UI 對(duì)象的數(shù)目感到吃驚。
NSTableView
與 UITableView
相比牲平,是更老堤框、更復(fù)雜的控件域滥,支持不同的用戶使用情境纵柿,具體比如說用戶在用鼠標(biāo)或觸摸板的時(shí)候。
與 UITableView 的主要區(qū)別是启绰,它可能有多個(gè)列昂儒,以及一個(gè)可用于與 table view 交互的標(biāo)題行,比如排序和選擇委可。
一起的還有 NSScrollView
和 NSClipView
渊跋,它們分別表示滾動(dòng)和剪切 NSTableView
的內(nèi)容。
有兩個(gè) NSScroller
對(duì)象——分別用于垂直和水平滾動(dòng)表格着倾。
還有一些列對(duì)象拾酝。NSTableView
有列,這些列有標(biāo)題行卡者。要注意蒿囤,重要的是,用戶可以調(diào)整列的大小和順序崇决,不過你有權(quán)利默認(rèn)禁用這個(gè)功能材诽。
剖析 NSTableView
在 Interface Builder 中,已經(jīng)見識(shí)了 table view 的視圖層級(jí)的復(fù)雜性恒傻。多個(gè)類一起構(gòu)建了表格結(jié)構(gòu)脸侥,最終通常看起來(lái)像這樣:

NSTableView
有幾個(gè)關(guān)鍵部分:
-
頭部視圖:頭部視圖是
NSTableHeaderView
的實(shí)例盈厘。它負(fù)責(zé)繪制表格最上方的頭部睁枕。如果需要顯示自定義的頭部,可以使用自己的頭部子類。 - 行視圖:行視圖顯示 table view 中每行的相關(guān)視覺屬性外遇,例如高亮選中拒逮。表格中顯示出的每一行都有自己的行視圖實(shí)例。一個(gè)重點(diǎn)是行不代表數(shù)據(jù)臀规;數(shù)據(jù)由 cell 負(fù)責(zé)滩援。它只處理視覺屬性,如選中顏色或間隔符塔嬉⊥婊玻可以創(chuàng)建新的行子類,以便為 table view 設(shè)置不同的樣式谨究。
- 單元格(Cell)視圖:?jiǎn)卧窨梢哉f是 table view 中最重要的對(duì)象恩袱。在行和列的交叉處就有一個(gè)單元格。每個(gè)都是 NSView 或 NSTableCellView 的子類胶哲,它負(fù)責(zé)顯示實(shí)際數(shù)據(jù)畔塔。已經(jīng)猜到了?我們可以創(chuàng)建自定義單元格視圖類鸯屿,以顯示任何喜歡的內(nèi)容澈吨。
-
列:列由
NSTableViewColumn
類表示,負(fù)責(zé)管理列的寬度和行為寄摆,例如調(diào)整大小和位置谅辣。這個(gè)類不是視圖,而是控制器類婶恼。使用它指定列的行為桑阶,但是不能控制列的視覺樣式,因?yàn)樗呀?jīng)被包含在頭部蚣录、行和單元格視圖中了萎河。
注意:NSTableView 有兩種模式公壤。第一種是基于單元格的 table view厦幅,叫做 NSCell。它像 NSView 一樣慨飘,但更老确憨、更輕量译荞。它來(lái)自早期計(jì)算機(jī)時(shí)代,當(dāng)時(shí)桌面需要優(yōu)化以縮小繪制控件的開銷休弃。
蘋果建議使用基于視圖的 table view吞歼,但可以在 AppKit 中的許多控件中發(fā)現(xiàn)NSCell
,所以了解它是什么以及它從何而來(lái)是有意義的塔猾「萋猓可以在蘋果的 Control and Cell Programming Topics 中詳細(xì)了解NSCell
好的,以上是關(guān)于 tableview 結(jié)構(gòu)的背后理論非常好的熱身≌傻椋現(xiàn)在你已經(jīng)知道了糯俗,是時(shí)候回到 Xcode 然后處理自己的 table view 了。
使用 Table View 中的列
默認(rèn)情況下睦擂,Interface Builder 會(huì)創(chuàng)建具有兩個(gè)列的 table view得湘,但我們需要三列以顯示名稱、日期和文件尺寸信息顿仇。
回到 Main.storyboard淘正。
選中 View Controller Scene 中的 table view臼闻。確保選中了 table view,而不是包裹它的 scroll view些阅。

打開 Attributes Inspector市埋。把 Columns 的數(shù)量調(diào)整為 3恕刘。就這么簡(jiǎn)單缤谎!你的 table view 選中有三列了。
下一步,勾選 Selection 部分的 Multiple 发侵,因?yàn)槲覀兿M淮慰梢赃x中多個(gè)文件狰住。還要勾選 Highlight 部分的 Alternating Rows。啟用時(shí)省核,它會(huì)告訴 table view 使用交替的背景顏色稿辙,就像 Finder 一樣气忠。

重命名列的標(biāo)題,使文字更具描述性。選中 View Controller Scene 中的第一列淘钟。

打開 Attributes Inspector 然后將列 Title 改為 Name宦赠。

為第二和第三列重復(fù)相同的操作,分別將 Title 改為 Modification Date 和 Size米母。
注意:更改列標(biāo)題還有另一個(gè)方法勾扭。可以在 table view 直接雙擊標(biāo)題铁瞒,它就可以被編輯了妙色。兩種方式有完全相同的效果,隨便選擇一種你喜歡的方式精拟。
最后燎斩,如果還看不到 Size 列虱歪,選中 Modification Date 列,然后調(diào)整大小為 200栅表。也可以用鼠標(biāo)拖動(dòng)調(diào)整笋鄙。:]

構(gòu)建并運(yùn)行」制浚可以看到如下所示:

更改信息的表示方式
在當(dāng)前狀態(tài)下萧落,table view 有三列叠蝇,每列包含一個(gè)在 text field 中顯示文字的單元格狞贱。
但這樣很乏味剑鞍,所以在文件名旁邊增加文件的圖標(biāo)來(lái)調(diào)節(jié)一下慨灭。這次調(diào)整后,我們的表格看起來(lái)會(huì)更清爽烦味。
我們需要把第一列中的單元格視圖替換為包含圖片和 text field 的新單元格類型礁鲁。
我們很幸運(yùn)箍铭,因?yàn)?Interface Builder 內(nèi)置了這種類型的單元格绎晃。
選中 Name 列中的 Table Cell View蜜唾,刪除掉。

打開 Object Library 然后拖一個(gè) Image & Text Table Cell View 到 table view 的第一列庶艾,或者拖到 View Controller Scene 樹中袁余,放到 Name table view 列下面。

馬上就要成形了咱揍!
分配標(biāo)識(shí)符
每個(gè)單元格類型都需要分配一個(gè)標(biāo)識(shí)符颖榜。否則,編碼時(shí)將無(wú)法創(chuàng)建與那列相對(duì)應(yīng)的單元格視圖煤裙。
選中第一列的單元格視圖掩完,在 Identity Inspector 里把 Identifier 改為 NameCellID。

對(duì)第二列和第三列中的單元格視圖重復(fù)相同的過程积暖,將標(biāo)識(shí)符分別命名為 DateCellID 和 SizeCellID藤为。
填充 Table View
注意:有兩種方式可以填充 tableview,本教程中使用 datasource 和 delegate 協(xié)議的方式夺刑,也可以通過 Cocoa bindings 。這兩種技術(shù)不是相互排斥的分别,如果喜歡的話可以一起使用遍愿。
Table view 目前根本不知道你想要顯示什么數(shù)據(jù)、或是如何顯示數(shù)據(jù)耘斩,但這確實(shí)需要規(guī)劃一下沼填!所以,我們會(huì)實(shí)現(xiàn)這兩個(gè)協(xié)議來(lái)提供所需的信息:
-
NSTableViewDataSource
:告訴 table view 它需要顯示多少行括授。 -
NSTableViewDelegate
:提供對(duì)應(yīng)行列顯示的單元格視圖坞笙。

可視化過程就是 table view岩饼、delegate 和 data source 之間的配合。
- Table view 調(diào)用 data source 方法
numberOfRows(in:)
薛夜,它返回了表格將會(huì)顯示的行數(shù)籍茧。 - Table view 為每個(gè)行列調(diào)用 delegate 方法
tableView(_:viewFor:row:)
。delegate 創(chuàng)建相應(yīng)位置的視圖梯澜,用正確的數(shù)據(jù)填充它寞冯,然后返回給 table view。
這兩個(gè)方法必須按順序?qū)崿F(xiàn)以在 table view 中顯示數(shù)據(jù)晚伙。
在 Assistant editor 中打開 ViewController.swift吮龄,然后按住 Control,把 table view 拖到 ViewController
類實(shí)現(xiàn)中以添加一個(gè) outlet咆疗。

確定 Type 是 NSTableView漓帚,Connection 是 Outlet。將 outlet 命名為 tableView
午磁。

現(xiàn)在可以在代碼中使用這個(gè) outlet 來(lái)指向 table view胰默。
切換到標(biāo)準(zhǔn)編輯器,然后打開 ViewController.swift漓踢。把下面的代碼添加到類的末尾以實(shí)現(xiàn)必須的 data source 方法:
extension ViewController: NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
return directoryItems?.count ?? 0
}
}
這樣就創(chuàng)建了一個(gè)符合 NSTableViewDataSource
協(xié)議的擴(kuò)展牵署,并且實(shí)現(xiàn)了必須的 numberOfRows(in:)
方法以返回目錄中的文件數(shù),即 directoryItems
數(shù)組的大小喧半。
現(xiàn)在我們需要實(shí)現(xiàn) delegate奴迅。添加如下擴(kuò)展到 ViewController.swift 的末端:
extension ViewController: NSTableViewDelegate {
fileprivate enum CellIdentifiers {
static let NameCell = "NameCellID"
static let DateCell = "DateCellID"
static let SizeCell = "SizeCellID"
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
var image: NSImage?
var text: String = ""
var cellIdentifier: String = ""
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .long
// 1
guard let item = directoryItems?[row] else {
return nil
}
// 2
if tableColumn == tableView.tableColumns[0] {
image = item.icon
text = item.name
cellIdentifier = CellIdentifiers.NameCell
} else if tableColumn == tableView.tableColumns[1] {
text = dateFormatter.string(from: item.date)
cellIdentifier = CellIdentifiers.DateCell
} else if tableColumn == tableView.tableColumns[2] {
text = item.isFolder ? "--" : sizeFormatter.string(fromByteCount: item.size)
cellIdentifier = CellIdentifiers.SizeCell
}
// 3
if let cell = tableView.make(withIdentifier: cellIdentifier, owner: nil) as? NSTableCellView {
cell.textField?.stringValue = text
cell.imageView?.image = image ?? nil
return cell
}
return nil
}
}
這段代碼聲明了一個(gè)擴(kuò)展,符合 NSTableViewDelegate
協(xié)議挺据,并且實(shí)現(xiàn)了 tableView(_:viewFor:row)
方法取具。之后 table view 就會(huì)在每個(gè)行列都調(diào)用它以得到正確的單元格。
這個(gè)方法有很多內(nèi)容扁耐,一步步拆開看:
- 如果沒有數(shù)據(jù)要顯示暇检,就不要返回單元格。
- 基于列(Name婉称、Date 或 Size)設(shè)置標(biāo)識(shí)符块仆、文字和圖片。
- 調(diào)用
make(withIdentifier:owner:)
獲得單元格視圖王暗。這個(gè)方法創(chuàng)建或重用了具有該標(biāo)識(shí)符的單元格悔据。然后,它用上一步中提供的信息填充自己俗壹,并將自己返回科汗。
下一步,把下面的代碼添加到 viewDidLoad()
:
tableView.delegate = self
tableView.dataSource = self
在這里告訴 table view绷雏,它的 data source 和 delegate 就是視圖控制器头滔。最后一步是告訴 table view 選擇新目錄后要刷新數(shù)據(jù)怖亭。首先,將這個(gè)方法添加到 ViewController
的實(shí)現(xiàn)中:
func reloadFileList() {
directoryItems = directory?.contentsOrderedBy(sortOrder, ascending: sortAscending)
tableView.reloadData()
}
這個(gè)輔助方法刷新了文件列表坤检。首先兴猩,它調(diào)用目錄方法 contentsOrderedBy(_:ascending)
然后返回帶有目錄文件的有序數(shù)組。然后它調(diào)用了 table view 方法 reloadData()
來(lái)讓它刷新缀蹄。
注意峭跳,只有在選擇了新目錄時(shí),才需要調(diào)用這個(gè)方法缺前。
找到 representedObject
的 observer didSet
蛀醉,將下面這行代碼:
print("Represented object: \(url)")
替換為:
directory = Directory(folderURL: url)
reloadFileList()
我們剛剛創(chuàng)建了 Directory
的一個(gè)實(shí)例,指向了文件夾 URL衅码,然后調(diào)用了 reloadFileList()
方法來(lái)刷新 table view 數(shù)據(jù)拯刁。
構(gòu)建并運(yùn)行。
使用 File > Open… 或快捷鍵 Command+O 打開文件夾逝段,然后看著奇跡發(fā)生垛玻!現(xiàn)在表格全都是來(lái)自剛剛選擇的文件夾的內(nèi)容。調(diào)整列的大小以看到每個(gè)文件或目錄的全部信息奶躯。

Nice帚桩!
Table View 交互
在這個(gè)部分,我們會(huì)做一些交互來(lái)提升 UI嘹黔。
響應(yīng)用戶選擇
當(dāng)用戶選擇一個(gè)或多個(gè)文件時(shí)账嚎,應(yīng)用程序應(yīng)更新底欄中的信息以顯示文件夾中的文件總數(shù)以及選擇了多少個(gè)文件。
為了能夠在 table view 的選擇改變時(shí)收到通知儡蔓,需要實(shí)現(xiàn) delegate 中的 tableViewSelectionDidChange(_:)
郭蕉。這個(gè)方法會(huì)在 table view 檢測(cè)到選擇改變時(shí)調(diào)用。
將下面的代碼添加到 ViewController 實(shí)現(xiàn)中:
func updateStatus() {
let text: String
// 1
let itemsSelected = tableView.selectedRowIndexes.count
// 2
if (directoryItems == nil) {
text = "No Items"
}
else if(itemsSelected == 0) {
text = "\(directoryItems!.count) items"
}
else {
text = "\(itemsSelected) of \(directoryItems!.count) selected"
}
// 3
statusLabel.stringValue = text
}
這個(gè)方法會(huì)根據(jù)用戶選擇更新 status label text喂江。
- Table view 屬性
selectedRowIndexes
包含了選中行的索引召锈。要了解選中了多少項(xiàng)目,只需要獲取數(shù)組 count 即可获询。 - 根據(jù)項(xiàng)目的數(shù)量涨岁,構(gòu)建了信息化文本字符串。
- 設(shè)置 status label 文字筐付。
現(xiàn)在卵惦,只需要在用戶更改 table view 選擇時(shí)調(diào)用這個(gè)方法。在 table view delegate 擴(kuò)展中添加如下代碼:
func tableViewSelectionDidChange(_ notification: Notification) {
updateStatus()
}
選擇改變時(shí)瓦戚,table view 會(huì)調(diào)用這個(gè)方法,然后更新狀態(tài)文本丛塌。
構(gòu)建并運(yùn)行较解。

動(dòng)手試一下畜疾;在 table view 中選擇一個(gè)或多個(gè)文件,然后觀察信息化文本是如何改變以反映用戶選擇的印衔。
響應(yīng)雙擊
在 macOS 中啡捶,雙擊通常表示用戶觸發(fā)了一個(gè)操作,程序需要去執(zhí)行它奸焙。
例如瞎暑,在處理文件時(shí),通常期望雙擊會(huì)在默認(rèn)程序中打開文件与帆,如果是文件夾了赌,就期望看到它的內(nèi)容。
我們現(xiàn)在要實(shí)現(xiàn)雙擊響應(yīng)玄糟。
雙擊通知不是由 table view delegate 發(fā)送的勿她;相反,它們作為操作被發(fā)送到 table view 目標(biāo)阵翎。但為了在 view controller 中接收這些通知逢并,需要設(shè)置 table view 的 target
和 doubleAction
屬性。
注意:Target-action 是 Cocoa 中大多數(shù)控件通知事件使用的模式郭卫。如果你不熟悉這個(gè)模式砍聊,可以在蘋果的 Cocoa Application Competencies for macOS 文檔里的 Target-Action 部分中進(jìn)行學(xué)習(xí)。
在 ViewController
的 viewDidLoad()
中添加如下代碼:
tableView.target = self
tableView.doubleAction = #selector(tableViewDoubleClick(_:))
這樣就告訴了 table view贰军,視圖控制器會(huì)成為操作的目標(biāo)玻蝌,然后為它設(shè)置雙擊后會(huì)調(diào)用的方法。
添加 tableViewDoubleClick(_:)
方法實(shí)現(xiàn):
func tableViewDoubleClick(_ sender:AnyObject) {
// 1
guard tableView.selectedRow >= 0,
let item = directoryItems?[tableView.selectedRow] else {
return
}
if item.isFolder {
// 2
self.representedObject = item.url as Any
}
else {
// 3
NSWorkspace.shared().open(item.url as URL)
}
}
將下面的代碼一步步拆解如下:
- 如果 table view 選擇為空谓形,什么都不做并且返回灶伊。還要注意在 table view 的空白區(qū)域上雙擊會(huì)導(dǎo)致
tableView.selectedRow
值被設(shè)置為 -1。 - 如果是文件夾寒跳,就將
representedObject
屬性設(shè)置為項(xiàng)目的 URL聘萨。然后刷新 table view 以顯示文件夾的內(nèi)容。 - 如果項(xiàng)目是文件童太,就在默認(rèn)程序中打開它米辐,調(diào)用
NSWorkspace
方法openURL()
即可
構(gòu)建并運(yùn)行,看看我們的作品书释。
在任意文件上雙擊翘贮,觀察它是如何在默認(rèn)程序中打開的。現(xiàn)在選擇文件夾爆惧,觀察 table view 如何刷新以及顯示文件夾內(nèi)容狸页。
哇,我們剛剛是創(chuàng)建了一個(gè) DIY 版本的 Finder 嗎?看起來(lái)一模一樣芍耘!
排序數(shù)據(jù)
每個(gè)人都喜歡良好的順序址遇,下一節(jié)中,我們會(huì)學(xué)習(xí)如何根據(jù)用戶的選擇對(duì) table view 進(jìn)行排序斋竞。
表最棒的特性之一就是單或雙擊對(duì)指定列排序倔约。單擊按升序排序,雙擊則是降序坝初。
實(shí)現(xiàn)這種特定的 UI 很簡(jiǎn)單浸剩,因?yàn)?NSTableView
把大部分功能包裝起來(lái)了,開箱即用鳄袍。
Sort descriptors 用于處理這種情況绢要,它們只是 NSSortDescriptor
類的實(shí)例,指定所需的屬性和排序順序畦木。
設(shè)置 descriptor 后袖扛,會(huì)發(fā)生以下情況:?jiǎn)螕?table view 的列標(biāo)題,將會(huì)通過 delegate 通知該使用哪個(gè)屬性十籍,然后用戶就能夠?qū)?shù)據(jù)進(jìn)行排序了蛆封。
設(shè)置了排序 descriptor 后,table view 會(huì)提供用于處理排序的所有 UI勾栗,例如可點(diǎn)擊的頭部惨篱、箭頭和選擇了哪個(gè) sort descriptor 的通知。然而围俘,我們需要負(fù)責(zé)實(shí)現(xiàn)根據(jù)信息排序數(shù)據(jù)砸讳,然后刷新 table view 以反映新順序。
現(xiàn)在就學(xué)習(xí)怎么實(shí)現(xiàn)界牡。

在 viewDidLoad()
中添加如下代碼以創(chuàng)建排序 descriptor:
// 1
let descriptorName = NSSortDescriptor(key: Directory.FileOrder.Name.rawValue, ascending: true)
let descriptorDate = NSSortDescriptor(key: Directory.FileOrder.Date.rawValue, ascending: true)
let descriptorSize = NSSortDescriptor(key: Directory.FileOrder.Size.rawValue, ascending: true)
// 2
tableView.tableColumns[0].sortDescriptorPrototype = descriptorName
tableView.tableColumns[1].sortDescriptorPrototype = descriptorDate
tableView.tableColumns[2].sortDescriptorPrototype = descriptorSize
這段代碼做了這些事情:
- 為每列都創(chuàng)建了一個(gè) sort descriptor簿寂,帶有一個(gè)鍵(Name,Date 或 Size)宿亡,該鍵指明可以對(duì)文件列表進(jìn)行排序的屬性常遂。
- 設(shè)置
sortDescriptorPrototype
屬性以為每列添加 sort descriptor。
如果用戶點(diǎn)擊任意列標(biāo)題挽荠,table view 會(huì)調(diào)用 data source 方法 tableView(_:sortDescriptorsDidChange:)
克胳,app 應(yīng)根據(jù)提供的 descriptor 對(duì)數(shù)據(jù)進(jìn)行排序。
將如下代碼添加到 data source 擴(kuò)展中:
func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) {
// 1
guard let sortDescriptor = tableView.sortDescriptors.first else {
return
}
if let order = Directory.FileOrder(rawValue: sortDescriptor.key!) {
// 2
sortOrder = order
sortAscending = sortDescriptor.ascending
reloadFileList()
}
}
這段代碼做了下面的事情:
- 檢索用戶點(diǎn)擊的列標(biāo)題相應(yīng)的 sort descriptor圈匆。
- 分配視圖控制器的
sortOrder
和sortAscending
屬性漠另,然后調(diào)用reloadFileList()
。剛剛設(shè)置它們以獲取排序的文件數(shù)組跃赚,然后告訴 table view 要重載數(shù)據(jù)笆搓。
構(gòu)建并運(yùn)行。

隨便單擊一個(gè)標(biāo)題,看看 table view 排序數(shù)據(jù)砚作。在同一個(gè)標(biāo)題再點(diǎn)一次窘奏,在升序和降序之間切換嘹锁。
我們已經(jīng)使用 table view 構(gòu)建了一個(gè)好看的文件瀏覽器葫录。好棒棒!
下一步领猾?
可以在 這里 下載完整的項(xiàng)目米同。
這篇 macOS NSTableView 教程涉及了相當(dāng)多內(nèi)容,現(xiàn)在你應(yīng)該對(duì)使用 table view 組織數(shù)據(jù)更有信心了摔竿。此外面粮,我們還學(xué)習(xí)了:
- table view 構(gòu)建的基礎(chǔ)知識(shí),包括頭继低、行熬苍、列和單元格。
- 如何添加列以顯示更多數(shù)據(jù)袁翁。
- 如何識(shí)別不同的組件以供引用柴底。
- 如何在表格中加載數(shù)據(jù)。
- 如何響應(yīng)不同的用戶交互粱胜。
還可以用 table view 做很多事情來(lái)為 app 構(gòu)建優(yōu)雅的 UI柄驻。如果你想學(xué)習(xí),可以參考下面的資源:
- 蘋果的 Table View Programming Guide for Mac焙压。
- WWDC 2016 - Session 239 Video - Crafting Modern Cocoa Apps 鸿脓, 快速了解如何構(gòu)建先進(jìn)的 table view。
- WWDC 2011 - Session 120 Video View Based NSTableView Basic to Advanced涯曲。
- TableViewPlayGround 有 Objective-C 的示例代碼野哭,展示如何創(chuàng)建不同類型的自定義 table view。
如果對(duì)于本教程有任何問題或評(píng)論幻件,隨意在下方發(fā)表拨黔!