本节主要讲述三个使用比较频繁的容器控件,包括tab页、表格以及集合面板三种。
NSTabView
Tab卡片视图,这个很简单就是如下样式,在使用过程中还可以动态增加和删除。

基本设置
- tabs:指定tab的个数;
- initial tab:指定默认被选中的tab;
- style:指定tab的位置;
- identifier:tab的id值(设置tabItem的);
事件响应
需要实现NSTabViewDelegate代理协议,当tab加载时调用(默认加载或点击),本示例中因为是委托给了ViewController,所以需要outlets到View Controller。

extension ViewController: NSTabViewDelegate{
func tabView(_ tabView: NSTabView, didSelect tabViewItem: NSTabViewItem?) {
//点击和加载的默认事件
print("title:\(tabViewItem?.label) id:\(tabViewItem?.identifier)")
}
}
动态添加TabItem
编程实现tabitem的动态添加:
@IBOutlet weak var tabView: NSTabView!
@IBAction func addTabItemAction(_ sender: NSButton) {
let tabViewItem = NSTabViewItem(identifier: "Untitled")
tabViewItem.label = "dyItemTitle"
let view = NSView(frame: NSZeroRect) //系统常量,类似的还有NSZeroPoint等
tabViewItem.view = view
self.tabView.addTabViewItem(tabViewItem)
}
动态选择TabItem
比如添加一个 NSSegmentedControl ,然后根据索引设置tab显示,即不使用NSTabView自带的切换按钮,而是从外部控件其视图的显示。

示例代码如下:
@IBOutlet weak var tabView: NSTabView!
@IBAction func segmentAction(_ sender: NSSegmentedControl) {
let index = sender.selectedSegment
tabView.selectTabViewItem(at: index)
}
NSPopover
弹出容器,可以指定其位置等属性,其内容来自于别一个NSViewController。

基本设置
- behavior:设置关闭行为,transient=只要点击Popover区域外就会关闭, semitransient=只要点击Popover区域外就会关闭,但点击到当前App外则不关闭
- animates:是否有动画效果;
- contentViewController:要显示的控制器;
事件响应
- 添加一个NSViewController,并设置其storyboard id值;
- 给需要弹出popover面板的控件加点击事件;
//延迟加载技术,主要为了提升性能,在被调用时才初始化
lazy var sharePopover:NSPopover = {
let popover = NSPopover()
let controller = self.storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("Share")) as! NSViewController
popover.contentViewController = controller
popover.behavior = .transient //关闭行为
return popover
}()
@IBAction func showPopupAction(_ sender: NSButton) {
//relativeTo:显示时参考的矩形, of:参考点,preferredEdge:显示位置,上面
sharePopover.show(relativeTo: sender.bounds, of: sender, preferredEdge: .minY)
//sharePopover.close()
}
NSCollectionView
一个带有自动分布功能的Grid面板控件,类似九宫格,在使用此控件之前需要先了解两个核心对象:
- NSCollectionView:相当于一个容器,内容会被封装到一个名为
content的属性中,content是一个NSCollectionViewItem数组; - NSCollectionViewItem:是NSCollectionView中的内容,它也是一个容器,它继承了NSViewController,所以使用时需要binding 数据;

Demo示例
- 拖动NSCollectionView控件到主面板上;
- 添加 add Constraints 这样可以达到自动适应的一个效果;***见Auto Layout应用
- 拖动一个NSCollectionViewItem控件到空白处,同时设置其storyboard ID;
- 在NSCollectionViewItem控件中添加NSImageView和Label;
- 绑定 NSImageView和NSLabel 到 NSCollectionViewItem; ***见bindings 模型
Auto Layout应用
- 拖动一个NSCollectionView到面板中;
- 然后在导航区选择Bordered Scroll View,点击右下角第二个按钮 add Constraints;这样NSCollectionView就可以随父窗口变化而变化了,这个Container的作用是固定元素到父元素的边距大小,相当于一个内容区域自适应功能。

上面设置的等价代码实现如下:
//下面代码采用swift 4
func autoLayoutConfig() {
// tableScrollView 为自定义的view
let topAnchor = self.tableScrollView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0)
let bottomAnchor = self.tableScrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0)
let leftAnchor = self.tableScrollView.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 0)
let rightAnchor = self.tableScrollView.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: 0)
NSLayoutConstraint.activate([topAnchor, bottomAnchor, leftAnchor, rightAnchor])
}
Bindings 数据模型
这里要做的就是把NSCollectionViewItem中的相应属性和相空的控件绑定在一起,比如 NSCollectionViewItem面板中添加了一个image如下:

则点击image然后切换到bindings 面板:

这里的 Bind to 表示要绑定的控件类,Model key path 表示代码中的数据属性名称(representedObject是一个固定值),通过以上设置,就把元素和控件的类属性绑定在一起了。
组装NSCollectionView和NSCollectionViewItem
@IBOutlet weak var collectionView: NSCollectionView!
override func viewDidLoad() {
//collectionViewItem 为 NSCollectionViewItem的storyboardid,在identity面板中设置
let itemPrototype = self.storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "collectionViewItem"))
as! NSCollectionViewItem
//设置NSCollectionView内容为NSCollectionViewItem
self.collectionView.itemPrototype = itemPrototype
//更新数据
self.updateContent()
}
添加内容,这样就实现了动态添加内容了
var content = [NSDictionary]()
func updateContent(){
let item1: NSDictionary = ["title" : "computer","image" : NSImage(named: NSImage.Name.computer)!]
let item2: NSDictionary = ["title" : "folder","image" : NSImage(named: NSImage.Name.folder)!]
let item3: NSDictionary = ["title" : "home","image" : NSImage(named: NSImage.Name.homeTemplate)!]
let item4: NSDictionary = ["title" : "list","image" : NSImage(named: NSImage.Name.listViewTemplate)!]
let item5: NSDictionary = ["title" : "network","image" : NSImage(named: NSImage.Name.network)!]
let item6: NSDictionary = ["title" : "share","image" : NSImage(named: NSImage.Name.shareTemplate)!]
content.append(item1)
content.append(item2)
content.append(item3)
content.append(item4)
content.append(item5)
content.append(item6)
self.collectionView.content = content
}
其在拖动时,也可以自由变换位置,如下:

编码实现
(示例有点显示问题)
- 创建一个 NSCollectionViewItem 子类,并附带XIB界面实现,这里需要重新绑定元素,默认的自动绑定不好用
- 手动编码

创建NSCollectionViewItem
选择新建Cocos class 类,然后按下图设置

此时会生成一个CollectionViewItem.xib文件和一个名为CollectionViewItem.swift的源文件重新绑定元素
//CollectionViewItem.swift
import Cocoa
class CollectionViewItem: NSCollectionViewItem {
//这两个变量要重新绑定,否则不生效,报nil找不到错误
@IBOutlet weak var image: NSImageView!
@IBOutlet weak var titleField: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override var representedObject: Any? {
didSet {
let data = self.representedObject as! NSDictionary
if let image = data["image"] as? NSImage {
self.image.image = image
}
if let title = (data["title"] as? String) {
self.titleField.stringValue = title
}
}
}
}
创建 NSCollectionView
在ViewController.swift中一步步手工编码即可
import Cocoa
//1、 得到一个名为CollectionViewItem的界面元素,声明为全局变量
extension NSUserInterfaceItemIdentifier {
static let collectionViewItem = NSUserInterfaceItemIdentifier("CollectionViewItem")
}
class ViewController: NSViewController {
//声明 NSScrollView 对象
lazy var scrollView: NSScrollView = {
let scrollView = NSScrollView()
scrollView.focusRingType = .none
scrollView.autohidesScrollers = true
scrollView.borderType = .noBorder
scrollView.documentView = self.collectionView
return scrollView
}()
// 声明 NSCollectionView 对象--内容面板
lazy var collectionView: NSCollectionView = {
let view = NSCollectionView()
view.isSelectable = true
view.delegate = self
view.dataSource = self
view.collectionViewLayout = self.flowLayout
view.register(CollectionViewItem.self, forItemWithIdentifier: .collectionViewItem) //注册
view.backgroundColors[0] = NSColor.red
return view
}()
var sectionInset: NSEdgeInsets = NSEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
lazy var flowLayout: NSCollectionViewFlowLayout = {
let layout = NSCollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.itemSize = NSSize(width: 40, height: 40)
layout.sectionInset = self.sectionInset
layout.minimumInteritemSpacing = 10
layout.minimumLineSpacing = 10
return layout
}()
//添加到window中
override func viewDidLoad() {
super.viewDidLoad()
scrollView.frame = self.view.bounds
self.view.addSubview(scrollView)
self.updateContent()
let dd = NSCollectionViewGridLayout()
}
//更新元素
var content = [NSDictionary]()
func updateContent(){
let item1: NSDictionary = ["title" : "computer","image" : NSImage(named: NSImage.Name.computer)!]
let item2: NSDictionary = ["title" : "folder","image" : NSImage(named: NSImage.Name.folder)!]
let item3: NSDictionary = ["title" : "home","image" : NSImage(named: NSImage.Name.homeTemplate)!]
let item4: NSDictionary = ["title" : "list","image" : NSImage(named: NSImage.Name.listViewTemplate)!]
let item5: NSDictionary = ["title" : "network","image" : NSImage(named: NSImage.Name.network)!]
let item6: NSDictionary = ["title" : "share","image" : NSImage(named: NSImage.Name.shareTemplate)!]
content.append(item1)
content.append(item2)
content.append(item3)
content.append(item4)
content.append(item5)
content.append(item6)
collectionView.reloadData()
}
}
//以下全是扩展协议
extension ViewController: NSCollectionViewDataSource {
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
return content.count
}
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
//let item = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "CollectionViewItem"), for: indexPath)
let item = collectionView.makeItem(withIdentifier: .collectionViewItem, for: indexPath)
let itemIndex = (indexPath as NSIndexPath).item
item.representedObject = content[itemIndex]
return item
}
func numberOfSections(in collectionView: NSCollectionView) -> Int {
return 1
}
}
extension ViewController: NSCollectionViewDelegate {
func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {
print(indexPaths)
}
}
extension ViewController: NSCollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize {
return NSSize(width: 100, height: 100)
}
}
1922

被折叠的 条评论
为什么被折叠?



