1.7 NSCollectionView
NSCollectionView是Cocoa中用来展示多行多列的自定义Item的控件,其作用和写法类似于UICollectionView,均有数据源和代理协议构成,然而具体实现细节上却有很大出入,下面请看纯代码编写NSCollectionView的介绍,为了方便示例,本节内容并没有严格遵守MVC架构来进行。
1.7.1 基本声明
基本声明包括Item的创建、NSCollectionView的创建、NSCollectionFlowLayout的创建。这三者的创建细节与UICollectionView的细节上有着不同,请看下面讲解。
1.Item的创建
右键单击鼠标,选择New File->Cocoa Class->新建继承于NSCollectionViewItem的子类(取消勾选创建XIB文件),该类即我们的item子类布局文件。我们这里创建item内要有一个Label对象和一个NSImageView对象,其创建代码如下:
import Cocoa
import SnapKit
class CellItem: NSCollectionViewItem {
var mView: NSView!
var mImageView: NSImageView!
var mLabel: NSTextField!
override func loadView() {
self.view = NSView(frame: NSRect(x: 0, y: 0, width: 100, height: 100))
}
override func viewDidLoad() {
super.viewDidLoad()
installMImageView()
installLabel()
}
private func installMImageView(){
mImageView = NSImageView()
self.view.addSubview(mImageView)
mImageView.snp.makeConstraints{ make in
make.size.equalTo(66)
make.centerX.equalToSuperview()
make.top.equalTo(self.view.snp.top).offset(15)
}
}
private func installLabel(){
mLabel = NSTextField()
mLabel.drawsBackground = false
mLabel.isBezeled = false
mLabel.isEditable = false
mLabel.alignment = .center
self.view.addSubview(mLabel)
mLabel.snp.makeConstraints{ make in
make.width.equalTo(60)
make.height.equalTo(25)
make.top.equalTo(self.mImageView.snp.bottom).offset(2)
make.centerX.equalToSuperview()
}
}
public func updateView(){
if self.isSelected == true {
self.view.layer?.backgroundColor = .init(gray: 0.9, alpha: 0.9)
} else if self.isSelected == false {
self.view.layer?.backgroundColor = .white
}
}
}
说明:loadView()
这个方法系纯代码创建要重写的方法,不重写系统则不会绘制此自定义NSView及其子类。
2.NSCollectionView的创建
var mView: NSView!
var scrollView: NSScrollView!
var mCollectionView: NSCollectionView!
override func loadView() {
self.view = NSView(frame: NSRect(x: 0, y: 0, width: 500, height: 350))
}
private func initView(){
mView = NSView(frame: NSRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height))
scrollView = NSScrollView(frame: NSRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height))
self.view.addSubview(scrollView)
mCollectionView = NSCollectionView(frame: NSRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height))
mCollectionView.delegate = self
mCollectionView.dataSource = self
mCollectionView.isSelectable = true
mCollectionView.register(CellItem.self, forItemWithIdentifier:NSUserInterfaceItemIdentifier(rawValue: "NS_COLLECTION_VIEW"))
self.scrollView.contentView.addSubview(mCollectionView)
}
说明:在XIB中,创建的NSCollectionView对象实际上是置于NSScrollView上方,如果这里我们不手动创建一个NSScrollView对象,那么有关数据源代理方法实际上是不会执行的。
3.NSCollectionFlowLayout的创建
在上述的initView()方法内插入如下代码:
// ...
mCollectionView.isSelectable = true
let layout = NSCollectionViewFlowLayout()
layout.itemSize = .init(width: 100, height: 100)
layout.minimumLineSpacing = 10
layout.minimumInteritemSpacing = 10
mCollectionView.collectionViewLayout = layout
mCollectionView.register(CellItem.self, forItemWithIdentifier:NSUserInterfaceItemIdentifier(rawValue: "NS_COLLECTION_VIEW"))
// ...
1.7.2 数据源与代理协议
实现如下协议:
NSCollectionViewDataSource, NSCollectionViewDelegate
有关数据源和代理协议方法如下:
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
} // 返回item数量
func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {
print("------> CLICK AT:\(indexPaths.first)")
let view = collectionView.item(at: indexPaths.first!) as! CellItem
view.updateView()
} // 对应的点击事件
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
let view = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "NS_COLLECTION_VIEW"), for: indexPath) as! CellItem
view.mImageView.image = NSImage(named: "user")
view.mLabel.stringValue = "User"
return view
} // 返回一个NSCollectionViewItem对象
本节代码:
CellItem.swift
import Cocoa
import SnapKit
class CellItem: NSCollectionViewItem {
var mView: NSView!
var mImageView: NSImageView!
var mLabel: NSTextField!
override func loadView() {
self.view = NSView(frame: NSRect(x: 0, y: 0, width: 100, height: 100))
}
override func viewDidLoad() {
super.viewDidLoad()
installMImageView()
installLabel()
}
private func installMImageView(){
mImageView = NSImageView()
self.view.addSubview(mImageView)
mImageView.snp.makeConstraints{ make in
make.size.equalTo(66)
make.centerX.equalToSuperview()
make.top.equalTo(self.view.snp.top).offset(15)
}
}
private func installLabel(){
mLabel = NSTextField()
mLabel.drawsBackground = false
mLabel.isBezeled = false
mLabel.isEditable = false
mLabel.alignment = .center
self.view.addSubview(mLabel)
mLabel.snp.makeConstraints{ make in
make.width.equalTo(60)
make.height.equalTo(25)
make.top.equalTo(self.mImageView.snp.bottom).offset(2)
make.centerX.equalToSuperview()
}
}
public func updateView(){
if self.isSelected == true {
self.view.layer?.backgroundColor = .init(gray: 0.9, alpha: 0.9)
} else if self.isSelected == false {
self.view.layer?.backgroundColor = .white
}
}
}
CollectionViewController.swift
//
// CollectionViewController.swift
// Versatile
//
// Created by Eldest's MacBook on 2021/10/5.
//
import Cocoa
import SnapKit
class CollectionViewController: NSViewController, NSCollectionViewDataSource, NSCollectionViewDelegate {
var mView: NSView!
var scrollView: NSScrollView!
var mCollectionView: NSCollectionView!
override func loadView() {
self.view = NSView(frame: NSRect(x: 0, y: 0, width: 500, height: 350))
}
private func initView(){
mView = NSView(frame: NSRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height))
scrollView = NSScrollView(frame: NSRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height))
self.view.addSubview(scrollView)
mCollectionView = NSCollectionView(frame: NSRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height))
mCollectionView.delegate = self
mCollectionView.dataSource = self
mCollectionView.isSelectable = true
let layout = NSCollectionViewFlowLayout()
layout.itemSize = .init(width: 100, height: 100)
layout.minimumLineSpacing = 10
layout.minimumInteritemSpacing = 10
mCollectionView.collectionViewLayout = layout
mCollectionView.register(CellItem.self, forItemWithIdentifier:NSUserInterfaceItemIdentifier(rawValue: "NS_COLLECTION_VIEW"))
self.scrollView.contentView.addSubview(mCollectionView)
}
private func initConfig(){
self.title = "NSCollectionView"
}
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
initView()
initConfig()
}
// MARK: - Delegate
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {
print("------> CLICK AT:\(indexPaths.first)")
let view = collectionView.item(at: indexPaths.first!) as! CellItem
view.updateView()
}
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
let view = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "NS_COLLECTION_VIEW"), for: indexPath) as! CellItem
view.mImageView.image = NSImage(named: "user")
view.mLabel.stringValue = "User"
return view
}
}
运行截图: