日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[译] NSCollectionView 入门教程

發(fā)布時(shí)間:2025/4/5 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [译] NSCollectionView 入门教程 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文翻譯自 raywenderlich.com 的 NSCollectionView Tutorial,已咨詢對方網(wǎng)站,可至多翻譯 10 篇文章。
希望各位有英語閱讀能力的話,還是 先打賞 然后去閱讀英文原吧,畢竟無論是 Xcode,抑或是官方的文檔,還是各種最前沿的資訊都只有英文版本。
綜上,此翻譯版本僅供參考,謝絕轉(zhuǎn)載

相關(guān)鏈接:
NSCollectionView 進(jìn)階教程:原文 / 譯文(翻譯中)
零基礎(chǔ) macOS 應(yīng)用開發(fā)教程(一):原文 / 譯文

更新信息: 此 NSCollectionView 教程已由 Gabriel Miro 更新至 Xcode 8 和 Swift 3.

Collection View 是顯示一系列相同類型數(shù)據(jù)的最佳方式。Mac 中自帶的 Finder 和 Photos 就是使用了它:通過一個(gè) Collection View 來展示所有的文件和圖片。

NSCollectionView 最早在 OS X 10.5 被推出,它可以非常方便地布局一組具有相同大小的 item,并把它們展示在一個(gè)可以滑動的 Scroll View 中。

在 OS X 10.11 El Capitan 中,參照 iOS 上的 UICollectionView,NSCollectionView 被全面進(jìn)行了升級。

macOS 10.12 Sierra 則給予了它「收起分區(qū)」(就像 Finder 里那樣)和「固定標(biāo)題」兩項(xiàng)新功能,使得它和 iOS 的差距進(jìn)一步減小了。

在這個(gè) NSCollectionView 的入門教程中,你將會創(chuàng)造一個(gè)叫 SlideMagic 的 app,它是一個(gè)只屬于你的網(wǎng)格狀的圖片瀏覽 app。

這個(gè)教程假定你已經(jīng)基本了解過了 macOS app 的開發(fā),如果你還不曾了解過,raywenderlich.com 上提供了很多很棒的 macOS 開發(fā)教程,你可以先去看看那些。

當(dāng)然還有我自己翻的《零基礎(chǔ) macOS 應(yīng)用開發(fā)教程》系列

準(zhǔn)備開始

你將會編寫的 SlidesMagic app 是一個(gè)簡單的圖片瀏覽器,它很酷,但是,可別因?yàn)樗崃硕徊恍⌒脑诎淹娴臅r(shí)候把自己 Mac 上的照片刪了哦?~

這個(gè) app 會從獲取一個(gè)文件夾里的所有圖片,然后用一個(gè)極其優(yōu)雅的 Collection View 來把它們顯示出來。完成了的 app 長這樣:

下載這個(gè)項(xiàng)目的起步代碼,編譯并運(yùn)行:

此時(shí),這個(gè) app 看起來只是一個(gè)空蕩蕩的窗口,但這些起步代碼包含了一些「隱藏功能」,這是后面使它成為一個(gè)圖片瀏覽器的基礎(chǔ)。

SlidesMagic 啟動的時(shí)候,會自動加載系統(tǒng)中 Desktop Pictures 目錄下的所有圖片,在 Xcode 的控制臺輸出中,我們可以看到這些文件的名字。

控制臺中輸出的列表表明,起步代碼中 Model 的加載邏輯代碼已經(jīng)可以正常工作了,你可以在這個(gè) app 的 FileOpen Another Folder… 菜單中打開另一個(gè)目錄。

起步代碼

起步代碼提供了一些與 Collection Views 無直接關(guān)聯(lián)的代碼。

Model

  • ImageFile.swift: 用于描述一個(gè)圖片文件
  • ImageDirectoryLoader.swift: 用來把圖片從硬盤中加載出來的 Helper 類

Controller

這個(gè) app 擁有兩個(gè)主要的 Controller:

  • WindowController.swift

    • windowDidLoad():在左半邊的屏幕上設(shè)置主窗口的大小;
    • openAnotherFolder(_:):提供一個(gè)標(biāo)準(zhǔn)的「打開」對話框來供用戶選擇文件夾;
  • ViewController.swift

    • viewDidLoad() 打開 Desktop Pictures 目錄作為默認(rèn)目錄。

Collection View 幕后探秘

NSCollectionView 是今天的主角,它將會在幾個(gè)關(guān)鍵的組成部分的幫助下,顯示許多 item。

布局

NSCollectionViewLayout:明確了 Collection View 的布局方式,它是一個(gè)抽象的類,所有用來表示 CollectionView 布局的實(shí)類都繼承自它。

NSCollectionViewFlowLayout:提供了一個(gè)靈活的網(wǎng)格狀的布局。對于絕大多數(shù) app,這種布局方式都適用。

NSCollectionViewGridLayout:為了兼容 OS X 10.11 和以前的版本所保留的布局方式,對于新創(chuàng)建的 app 不推薦使用。

Section 和 IndexPath:前者允許你把 item 分成若干個(gè) section(分區(qū)),每個(gè) section 包含了一組有序的 item。每個(gè) item 都和一個(gè)索引相關(guān)聯(lián),這個(gè)索引是一個(gè)由一對整數(shù)(section,item)構(gòu)成的 IndexPath 實(shí)例。默認(rèn)情況下,當(dāng)你不需要給 item 分區(qū)時(shí),這個(gè) Collection View 仍然會擁有一個(gè) section。

Collection View Item

就像其他許多 Cocoa 框架一樣,Collection View 中的 item 也遵守著 MVC 設(shè)計(jì)模式。

Model 和 View:這個(gè) item 的內(nèi)容來自 Model 的數(shù)據(jù)對象。每個(gè)單獨(dú)的對象都通過在 Collection View 中創(chuàng)建自己的 View 來把自己顯示出來。這些 View 的結(jié)構(gòu)由一個(gè)單獨(dú)的 nib 文件(文件擴(kuò)展名是 .xib)來定義。

Controller:上面提到的 nib 文件是一個(gè)由 NSCollectionViewItem 管理的 NSViewController 的子類。它負(fù)責(zé)與 Model 對象進(jìn)行通信并控制 Collection View 的顯示。通常情況下,你會編寫一個(gè) NSCollectionViewItem 的子類。當(dāng)你需要不同類型的 item 的時(shí)候,你需要為每個(gè)分支定義一個(gè)不同的子類,并創(chuàng)建一個(gè) nib。

額外的 View

要在 Collection View 中顯示不同于普通 item 額外的信息,你需要額外的 item。

最直觀的例子就是分區(qū)的標(biāo)題和腳注。

Collection View 的數(shù)據(jù)源和代理(Data Source and Delegates)

  • NSCollectionViewDataSource:用 item 和額外的 item 來填充 Collection View。
  • NSCollectionViewDelegate:處理拖放相關(guān)的事件,以及選中狀態(tài)和高亮。
  • NSCollectionViewDelegateFlowLayout:允許你自定義你的網(wǎng)格視圖。

注意:填充一個(gè) Collection View 的方法有二:數(shù)據(jù)源和 Cocoa 綁定。這個(gè)教程將會使用數(shù)據(jù)源。

創(chuàng)建 Collection View

打開 Main.storyboard。前往控件庫,向 View Controller Scene 中拖動一個(gè) Collection View

注意:你或許注意到了,Interface Builder 為我們添加了三個(gè) View,而不是一個(gè)。這是因?yàn)?Collection View 是嵌入在一個(gè) Scroll View 中的,而后者又自帶一個(gè) Clip View 子視圖。這仨視圖各不相同,因此當(dāng)本教程需要你選擇 Collection View 的時(shí)候,切記不要錯(cuò)選了 Scroll ViewClip View

調(diào)整 Bordered Scroll View 的大小,使它填滿它的父視圖的所有空間。然后選擇 Xcode 菜單欄上的 EditorResolve Auto Layout IssuesAdd Missing Constraints 來添加 Auto Layout 約束條件。

你需要在 ViewController 中添加一個(gè) Outlet 來訪問界面上的 Collection View。打開 ViewController.swift,在 ViewController 類的定義中添加以下代碼:

@IBOutlet weak var collectionView: NSCollectionView!

打開 Main.storyboard,并選擇 View Controller Scene 中的 View Controller

打開連接檢查器,在 Outlets 部分中找到 collectionView拖動它旁邊的小圓圈到畫布中的 Collection View 上。

調(diào)整 Collection View 的布局

你現(xiàn)在有兩種選擇:在 Interface Builder 中設(shè)置好主要的布局屬性,或者在代碼中手動編寫。

在 SlidesMagic 這個(gè)項(xiàng)目中,我們選擇手動編寫代碼。

打開 ViewController.swift,把這些方法添加到 ViewController 中:

fileprivate func configureCollectionView() {// 1let flowLayout = NSCollectionViewFlowLayout()flowLayout.itemSize = NSSize(width: 160.0, height: 140.0)flowLayout.sectionInset = EdgeInsets(top: 10.0, left: 20.0, bottom: 10.0, right: 20.0)flowLayout.minimumInteritemSpacing = 20.0flowLayout.minimumLineSpacing = 20.0collectionView.collectionViewLayout = flowLayout// 2view.wantsLayer = true// 3collectionView.layer?.backgroundColor = NSColor.black.cgColor}

這些代碼的作用是:

  • 創(chuàng)建一個(gè) NSCollectionViewFlowLayout,配置它的基本屬性,并設(shè)置 NSCollectionView 的 collectionViewLayout;
  • 一般情況下,NSCollectionView 是基于層的,所以你需要把它的父視圖的 wantsLayer 設(shè)置為 true;
  • 把 Collection View 的背景顏色設(shè)置為黑色。
  • 你需要在試圖加載完成時(shí)調(diào)用這個(gè)方法,所以在 viewDidLoad() 方法的最后插入:

    configureCollectionView()

    編譯并運(yùn)行:

    此時(shí),你的 Collection View 已經(jīng)擁有了一個(gè)黑色的背景,并配置好了布局。

    創(chuàng)建一個(gè) Collection View Item

    先在你需要創(chuàng)建一個(gè) NSCollectionViewItem 的子類并把 Model 里的數(shù)據(jù)們顯示出來。

    點(diǎn)擊 Xcode 菜單欄上的 FileNewFile…,選擇 macOSSourceCocoa Class 并點(diǎn)擊 Next

    Class 填寫 CollectionViewItemSubclass of 填寫 NSCollectionViewItem,并勾選 Also create XIB for user interface

    點(diǎn)擊 Next,然后在對話框中的 Group 中選擇 Controllers,并點(diǎn)擊 Create

    打開 CollectionViewItem.swift,把里邊的內(nèi)容全部替換為:

    import Cocoa class CollectionViewItem: NSCollectionViewItem {// 1var imageFile: ImageFile? {didSet {guard isViewLoaded else { return }if let imageFile = imageFile {imageView?.image = imageFile.thumbnailtextField?.stringValue = imageFile.fileName} else {imageView?.image = niltextField?.stringValue = ""}}}// 2override func viewDidLoad() {super.viewDidLoad()view.wantsLayer = trueview.layer?.backgroundColor = NSColor.lightGray.cgColor} }

    這些代碼的功能是:

  • 定義了 imageFile 屬性,用來訪問需要展示的 Model 對象。當(dāng)你為 imageFile 屬性賦值時(shí),它的 didSet 屬性觀察器會設(shè)置這個(gè) item 的 Image 和 Label;
  • 改變此 item 的 View 的背景顏色。
  • 向 View 中添加 Control

    你在 CollectionViewItem.swift 時(shí)勾選了「Also create a XIB(同時(shí)創(chuàng)建一個(gè) XIB)」,為了更清楚地整理文件,把 CollectionViewItem.xib 拖動到 Main.storyboard 下方的 Resources 分組中。

    Nib 文件中的 View 就是每個(gè) item 所顯示出來的根視圖,你需要添加一個(gè) Image View 來顯示圖片,以及一個(gè) Label 來顯示文件名。

    打開 CollectionViewItem.xib,添加一個(gè) NSImageView:

  • 控件庫中拖動一個(gè) Image View 到畫布上的 View 中;
  • Auto Layout 工具欄中點(diǎn)擊 Pin 按鈕來設(shè)置它的約束條件;
  • 設(shè)置它的 topleadingtrailing 約束為 0,bottom 為 30。點(diǎn)擊 Update Frames: Items of New Constraints 然后點(diǎn)擊 Add 4 Constraints
  • 再來添加一個(gè) Label:

  • 控件庫中拖動一個(gè) Label 到畫布上的 Image View 的下方;
  • Auto Layout 工具欄中點(diǎn)擊 Pin 按鈕,設(shè)置它的 topbottomleadingtrailing 約束都為 0。點(diǎn)擊 Update Frames: Items of New Constraints 然后點(diǎn)擊 Add 4 Constraints
  • 選中 Label,在屬性檢查器中設(shè)置如下屬性:

  • 設(shè)置 Alignmentcenter
  • 設(shè)置 Text Colorwhite
  • 設(shè)置 Line BreakTruncate Tail
  • 向 Nib 中添加 CollectionViewItem 并連接 Outlets

    盡管 Nib 文件的 File’s Owner 現(xiàn)在是 CollectionViewItem,它還只是一個(gè)占位符。當(dāng) Nib 文件被實(shí)例化時(shí),它還會需要一個(gè)「真正的」NSCollectionViewItem 的實(shí)例。

    控件庫中拖動一個(gè) Collection View Item文檔大綱中,選中它,在 身份檢查器中把它的 Class 設(shè)置為 CollectionViewItem。

    在 xib 中,你需要把 View 的層次關(guān)系連接到 CollectionViewItem 的 Outlet 中,在 CollectionViewItem.xib 中:

  • 選中 Collection View Item 并前往 Connections Inspector
  • 把 view 的 outlet 拖動到文檔大綱中的 View 上;
  • 用同樣的方法,把 imageView 和 textField 的 outlet 連接到文檔大綱中的 Image ViewLabel 中。
  • 填充 Collection View

    你需要實(shí)現(xiàn) Collection View 的數(shù)據(jù)源協(xié)議,說人話就是:

  • Collection 中有幾個(gè)分區(qū)?
  • 每個(gè)分區(qū)分別有多少個(gè) item?
  • 某個(gè)索引路徑(Index Path)對應(yīng)的是哪個(gè) Item?
  • 打開 ViewController.swift 并在文件的末尾添加這些擴(kuò)展代碼:

    extension ViewController : NSCollectionViewDataSource {// 1func numberOfSections(in collectionView: NSCollectionView) -> Int {return imageDirectoryLoader.numberOfSections}// 2func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {return imageDirectoryLoader.numberOfItemsInSection(section)}// 3func collectionView(_ itemForRepresentedObjectAtcollectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {// 4let item = collectionView.makeItem(withIdentifier: "CollectionViewItem", for: indexPath)guard let collectionViewItem = item as? CollectionViewItem else {return item}// 5let imageFile = imageDirectoryLoader.imageFileForIndexPath(indexPath)collectionViewItem.imageFile = imageFilereturn item}}
  • 如果你的 app 不需要用到分區(qū),那么你可以刪除這個(gè)方法,因?yàn)橐粋€(gè)分區(qū)就夠了;
  • 這是兩個(gè) NSCollectionViewDataSource 協(xié)議必須實(shí)現(xiàn)的方法之一,在這個(gè)方法中你需要返回某個(gè)分區(qū)容納的 item 的數(shù)量;
  • 另一個(gè)必須實(shí)現(xiàn)的方法,這個(gè)方法會針對某個(gè) indexPath 返回一個(gè) item;
  • 這個(gè)方法會從 nib 中實(shí)例化一個(gè) item,這個(gè) item 的名字是 identifier 參數(shù),它會根據(jù)所需要的 item 的類型來試圖重復(fù)使用一個(gè) item,如果沒有 item 可供重復(fù)使用,它會新建一個(gè) item;
  • 根據(jù) IndexPath 獲取 Model 對象,設(shè)置 Image 和 Label 的內(nèi)容。
  • 注意: Collection View 具有一項(xiàng)能力:循環(huán)使用已經(jīng)生成了的 Item,以此來減輕數(shù)據(jù)源過大時(shí)的內(nèi)存壓力。從界面上移出去的 item 就是被重復(fù)使用的 item。

    設(shè)置數(shù)據(jù)源

    接下來我發(fā)需要定義數(shù)據(jù)源:

    打開 Main.storyboard,選中 Collection View。

    打開連接檢查器,在 Outlets 部分中找到 dataSource拖動它旁邊的小圓圈到文檔大綱里的 View Controller 上。

    編譯并運(yùn)行,你的 Collection View 現(xiàn)在應(yīng)該能顯示 Desktop Pictures 目錄中的圖片了:

    哈哈哈,折騰了半天都是值得的??~

    故障排除

    如果你還看不見任何圖片,你可能遺漏了一些小細(xì)節(jié):

  • 你在連接檢查器中正確地設(shè)置了所有的連接嗎?
  • 你設(shè)置 dataSource 的 Outlet 了嗎?
  • 你在身份檢查器中應(yīng)用正確的自定義類了嗎?
  • 你添加頂層的 NSCollectionViewItem,并把它的類設(shè)置為 CollectionViewItem 了嗎?
  • makeItemWithIdentifier 中的 identifier 參數(shù)的值和 nib 的名字一樣嗎?
  • Model 發(fā)生改變時(shí)重新加載 Item 們

    要顯示另一個(gè)目錄中的圖片,你可以在 app 的菜單欄上點(diǎn)擊 FileOpen Another Folder…,然后選擇一個(gè)存有 JPGPNG 格式的圖片的目錄。

    但時(shí)此時(shí)窗口中的東西似乎什么變化都沒有,還是顯示著 Desktop Pictures 目錄中的圖片。盡管 Xcode 里的控制臺中已經(jīng)打印出了新目錄里的文件名稱。

    你需要調(diào)用 Collection View 的 reloadData() 方法來刷新它的 item。

    打開 ViewController.swift 并把這些代碼添加到 loadDataForNewFolderWithUrl(_:) 方法中:

    collectionView.reloadData()

    編譯并運(yùn)行,現(xiàn)在你應(yīng)該能看到窗口中已經(jīng)能顯示正確的照片了。

    添加分區(qū)

    SlidesMagic app 現(xiàn)在已經(jīng)可以做一些很神奇的事兒了,但我們要更進(jìn)一步 —— 為 Collection View 加入分區(qū)。

    首先,你需要在主視圖的最底部加入一個(gè)復(fù)選框,來允許你切換是否啟用分組。

    打開 Main.storyboard,然后在文檔大綱中選中 Scroll View 的約束條件,在尺寸檢查器中吧它的 Constant 修改為 30.

    這會把 Collection View 抬高一些些,騰出地方來放置復(fù)選框。

    現(xiàn)在,從控件庫中拖動一個(gè) Check Box Button到畫布中 Collection View 下方的空間里,選中它,在屬性檢查器中把它的 Title 設(shè)置為 Show Sections,然后把 State 設(shè)置為 Off

    接下來,點(diǎn)擊 Pin 按鈕更新它的 Auto Layout 約束條件:Top 設(shè)置為 8,Leading 設(shè)置為 20。然后點(diǎn)擊 Update Frames: Items of New ConstraintsAdd 2 Constraints

    編譯并運(yùn)行,現(xiàn)在 app 的底部看起來應(yīng)該是這樣的:

    當(dāng)你點(diǎn)擊這個(gè)復(fù)選框的時(shí)候,你的 app 需要改變 Collection View 的外觀。

    打開 ViewController.swift 在 ViewController 類的最后添加這些代碼:

    @IBAction func showHideSections(sender: NSButton) {let show = sender.state// 1imageDirectoryLoader.singleSectionMode = (show == NSOffState)// 2imageDirectoryLoader.setupDataForUrls(nil)// 3collectionView.reloadData()}

    這些代碼會:

  • 根據(jù)復(fù)選框的狀態(tài)切換單一分組/多個(gè)分組;
  • 根據(jù)當(dāng)前選擇的模式來調(diào)整 Model,此時(shí)傳遞的 nil 參數(shù)表示跳過圖像加載 —— 畢竟圖片還是那些圖片,只是布局發(fā)生了改變;
  • Model 發(fā)生了改變,所以需要刷新數(shù)據(jù)。
  • 如果你好奇圖片是按照是按照什么規(guī)則進(jìn)行分組的,在 ImageDirectoryLoader 中找到 sectionLengthArray,這個(gè)數(shù)組里的數(shù)字設(shè)置了各個(gè)分組的里最多可以容納多少個(gè) item。這個(gè)數(shù)組是隨機(jī)生成的,只是用來用作演示。

    現(xiàn)在,打開 Main.storyboard。在文檔大綱按住 Control? 鍵的同時(shí)Show Sections 拖動到 View Controller 上。在彈出的黑色窗口中點(diǎn)擊 showHideSections:。你可以在連接檢查器里查看你是否連接成功了。

    編譯并運(yùn)行,勾選 Show Sections 來查看布局的變化。

    為了更好地區(qū)分各個(gè)分區(qū),打開 ViewController.swift,編輯 configureCollectionView() 方法里的 sectionInset 屬性。

    把這一行:

    flowLayout.sectionInset = EdgeInsets(top: 10.0, left: 20.0, bottom: 10.0, right: 20.0)

    替換成這個(gè):

    flowLayout.sectionInset = EdgeInsets(top: 30.0, left: 20.0, bottom: 30.0, right: 20.0)

    編譯并運(yùn)行,勾選 Show Sections,可以看到各個(gè)分區(qū)之間已經(jīng)有了分隔。

    添加分區(qū)標(biāo)題

    另一種區(qū)分各個(gè)分區(qū)邊界的方法是為每個(gè)分區(qū)添加一個(gè)標(biāo)題或腳注。

    你需要一個(gè)自定義的 NSView 類,并實(shí)現(xiàn)相應(yīng)的數(shù)據(jù)源方法來為 Collection View 添加一個(gè)標(biāo)題

    要創(chuàng)建一個(gè)標(biāo)題,在 Xcode 的菜單欄點(diǎn)擊 FileNewFile…。選擇 macOSUser InterfaceView,并點(diǎn)擊 Next

    文件名輸入 HeaderView.xibGroup 選擇 Resources

    點(diǎn)擊 Create

    打開 HeaderView.xib 并選中 Custom View。在尺寸檢查器中把 Width 設(shè)置為 500,Height 設(shè)置為 40。

    Object Library 拖動一個(gè) Label 到 Custom View 的左半邊。打開屬性檢查器,設(shè)置它的 TitleSection Number,設(shè)置 Font Size16

    再拖動一個(gè) Label 到 Custom View 的右半邊。設(shè)置它的 TitleImage Count,設(shè)置 AlignmentRight

    選中 Section Number Label,點(diǎn)擊 Pin 按鈕,設(shè)置它的 Top 約束為 12,Leading 約束為 20。點(diǎn)擊 Update Frames: Items of New ConstraintsAdd 2 Constraints

    接下來,設(shè)置 Image Count Label 的 Top 約束為 11,Trailing 約束為 20,別忘了點(diǎn)擊 Update Frames: Items of New ConstraintsAdd 2 Constraints

    現(xiàn)在我們的標(biāo)題應(yīng)該看起來像這樣:

    現(xiàn)在我們的標(biāo)題 UI 已經(jīng)準(zhǔn)備好了,我們還需要為它創(chuàng)建一個(gè)子類。

    在 Xcode 的菜單欄點(diǎn)擊 FileNewFile…。選擇 macOSSourceCocoa Class,并點(diǎn)擊 Next。把它的類名設(shè)置為 HeaderView,并讓它繼承自 NSView,點(diǎn)擊 Next,并在 Group 中選擇 Views。點(diǎn)擊 Create

    打開 HeaderView.swift 然后把里邊的所有內(nèi)容替換為:

    // 1 @IBOutlet weak var sectionTitle: NSTextField! @IBOutlet weak var imageCount: NSTextField!// 2 override func draw(_ dirtyRect: NSRect) {super.draw(dirtyRect)NSColor(calibratedWhite: 0.8 , alpha: 0.8).set()NSRectFillUsingOperation(dirtyRect, NSCompositingOperation.sourceOver) }

    這里的代碼做了這些事兒:

  • 設(shè)置你需要用來連接 nib 元素的 outlet;
  • 繪制一個(gè)灰色的背景。
  • 要把 outlet 連接至 Label,打開 HeaderView.xib 并選中 Custom View。在 Identity Inspector 中把 Class 設(shè)置為 HeaderView

    文檔大綱視圖中,按住 Control? 鍵的同時(shí)點(diǎn)擊 Header View。在彈出的黑色窗口中,拖動 imageCountImages Count 上來連接 outlet。

    對第二個(gè) Label 進(jìn)行同樣的操作,拖動 sectionTitle 到畫布中的 Section Number Label 上。

    實(shí)現(xiàn)數(shù)據(jù)源和代理方法

    你的標(biāo)題已經(jīng)完全準(zhǔn)備好上戰(zhàn)場了,你需要實(shí)現(xiàn) collectionView(_:viewForSupplementaryElementOfKind:at:),把這個(gè)標(biāo)題視圖傳遞給 Collection View:

    打開 ViewController.swift 并把這些方法添加到 NSCollectionViewDataSource extension 中:

    func collectionView(_ collectionView: NSCollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> NSView {// 1let view = collectionView.makeSupplementaryView(ofKind: NSCollectionElementKindSectionHeader, withIdentifier: "HeaderView", for: indexPath) as! HeaderView// 2view.sectionTitle.stringValue = "Section \(indexPath.section)"let numberOfItemsInSection = imageDirectoryLoader.numberOfItemsInSection(indexPath.section)view.imageCount.stringValue = "\(numberOfItemsInSection) image files"return view }

    Collection View 會在它需要數(shù)據(jù)源的時(shí)候調(diào)用這個(gè)方法,并為每個(gè)分區(qū)設(shè)置標(biāo)題。這個(gè)方法做了這些:

  • 調(diào)用 makeSupplementaryViewOfKind(_:withIdentifier:for:) 來從 nib 文件實(shí)例化一個(gè)名字是 withIdentifier 的 HeaderView 對象;
  • 設(shè)置各個(gè) Label 的值。
  • ViewController.swift 的最后,添加這個(gè) NSCollectionViewDelegateFlowLayout 擴(kuò)展:

    extension ViewController : NSCollectionViewDelegateFlowLayout {func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> NSSize {return imageDirectoryLoader.singleSectionMode ? NSZeroSize : NSSize(width: 1000, height: 40)} }

    上面這個(gè)方法其實(shí)是不是必須的,但當(dāng)你需要設(shè)置標(biāo)題的時(shí)候就必須寫上了,因?yàn)?Flow Layout(流式布局)的代理需要你提供各個(gè)分區(qū)的標(biāo)題的大小。

    如果沒有實(shí)現(xiàn)這個(gè)方法,你將看不到標(biāo)題,因?yàn)樗鼈兊某叽缍际?0。此外,它還會忽略你指定的寬度,而是把標(biāo)題的寬度設(shè)置為 Collection View 的寬度。

    在這個(gè)例子中,當(dāng) Collection View 只有一個(gè)分區(qū)的時(shí)候,這個(gè)方法返回的標(biāo)題尺寸是 0,否則他會返回 40.

    對于使用了 NSCollectionViewDelegateFlowLayout 的 Collection View,你需要把 ViewController 連接到 NSCollectionView 的 delegate。

    打開 Main.storyboard 并選中 Collection View。打開連接檢查器,在 Outlets 部分中找到 delegate拖動他旁邊的小圓點(diǎn)到文檔大綱中的 View Controller 上。

    編譯并運(yùn)行,勾選 Show Sections,可以看到一個(gè)個(gè)標(biāo)題把分區(qū)區(qū)分開來:

    固定標(biāo)題

    macOS 10.12 中的 NSCollectionViewFlowLayout 新加入了兩個(gè)屬性:sectionHeadersPinToVisibleBounds 和 sectionFootersPinToVisibleBounds。

    當(dāng) sectionHeadersPinToVisibleBounds 設(shè)置為 true,最上端的分區(qū)的標(biāo)題將會固定在頂端,而不會移出界面以外。當(dāng)你繼續(xù)向下滾動時(shí),下一個(gè)標(biāo)題會把它頂走。這種效果一般被稱為「sticky headers(固定標(biāo)題)」或「floating headers(浮動標(biāo)題)」。

    把 sectionFootersPinToVisibleBounds 設(shè)置為 true 則會把腳注固定在底部。

    打開 ViewController.swift,在 configureCollectionView() 方法的底部加入這個(gè)方法:

    flowLayout.sectionHeadersPinToVisibleBounds = true

    編譯并運(yùn)行,勾選 Show Sections 并向下滾動一些,你可以看到第一個(gè)區(qū)域已經(jīng)有一些圖片被移出屏幕了,但標(biāo)題還是固定在頂部:

    注意:如果你的 app 需要支持 OS X 10.11 或更老的版本,你需要通過重寫 layoutAttributesForElements(in:) 方法來「手動」實(shí)現(xiàn)固定標(biāo)題。你可以查看[Advanced Collection Views in OS X Tutorial]這篇教程(我正在翻譯~)。

    Collection View 的選擇功能

    為了顯示一個(gè) item 的被選中狀態(tài),你需要設(shè)置一個(gè)白色的邊框,沒有被選中的項(xiàng)目將不會顯示這個(gè)邊框。

    首先,你需要讓我們的 Collection View 支持選中。打開 Main.storyboard,選中 Collection View 并在屬性檢查器里,勾選 Selectable

    勾選 Selectable 開啟了選擇功能,意味著你可以通過點(diǎn)擊一個(gè) item 來選中它。如果你點(diǎn)擊另一個(gè) item,將會取消選擇之前的那個(gè) item 并選中新的 item。

    當(dāng)你選中一個(gè) item:

  • 它的 IndexPath 會被添加到 NSCollectionView 的 selectionIndexPaths 屬性;
  • 它的 isSelected 屬性會被設(shè)置為 true。
  • 打開 CollectionViewItem.swift。在 viewDidLoad() 方法的最后追加:

    // 1 view.layer?.borderColor = NSColor.white.cgColor // 2 view.layer?.borderWidth = 0.0

    這段代碼:

  • 設(shè)置了一個(gè)白色的邊框;
  • 把 borderWidth 設(shè)置為 0.0 來確保邊框不可見?—— 也就是沒被選中。
  • 要在每次 isSelected 被設(shè)置時(shí)改變 borderWidth,我們需要把這些代碼添加到 CollectionViewItem 類中:

    override var isSelected: Bool {didSet {view.layer?.borderWidth = isSelected ? 5.0 : 0.0}}

    每次 isSelected 發(fā)生了改變,didSet 將會根據(jù)新的值來設(shè)置邊框的寬度。

    編譯并運(yùn)行。點(diǎn)擊一個(gè)項(xiàng)目來選中它,你將會看見它周圍出現(xiàn)了邊框。哈哈哈,神奇?!

    下一步該做些啥?

    點(diǎn)擊這里下載最終完成了的 SlideMagic

    在這個(gè) NSCollectionView 入門教程中,你了解了如何創(chuàng)建你的第一個(gè) Collection View,了解了錯(cuò)綜復(fù)雜的數(shù)據(jù)源 API 和如何處理分區(qū)。至此你已經(jīng)學(xué)到了很多,但其實(shí)這僅僅是個(gè)開始,Collection View 還有很多功能等待你去發(fā)掘。這里有很多值得去探索的東西:

    • 通過 Cocoa 的數(shù)據(jù)綁定構(gòu)建「免數(shù)據(jù)源」的 Collection View
    • 不同類型的 Item
    • 追加和移除 Item
    • 自定義布局
    • 拖放手勢(Drag and drop)
    • 動畫
    • 修改 NSCollectionViewFlowLayout
    • 收起某個(gè)分區(qū)(macOS 10.12 Sierra 的新功能)

    你可以在我們的《NSCollectionView 進(jìn)階教程》(原文|譯文)中了解更多。

    《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

    總結(jié)

    以上是生活随笔為你收集整理的[译] NSCollectionView 入门教程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。