07. 容器控件(一) - NSCollectionView 网格、NSTabView 卡片、NSPopover 弹出层

本节主要讲述三个使用比较频繁的容器控件,包括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:要显示的控制器;

事件响应

  1. 添加一个NSViewController,并设置其storyboard id值;
  2. 给需要弹出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示例

  1. 拖动NSCollectionView控件到主面板上;
  2. 添加 add Constraints 这样可以达到自动适应的一个效果;***见Auto Layout应用
  3. 拖动一个NSCollectionViewItem控件到空白处,同时设置其storyboard ID;
  4. 在NSCollectionViewItem控件中添加NSImageView和Label;
  5. 绑定 NSImageView和NSLabel 到 NSCollectionViewItem; ***见bindings 模型

Auto Layout应用

  1. 拖动一个NSCollectionView到面板中;
  2. 然后在导航区选择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
    }

其在拖动时,也可以自由变换位置,如下:
在这里插入图片描述

编码实现

(示例有点显示问题)

  1. 创建一个 NSCollectionViewItem 子类,并附带XIB界面实现,这里需要重新绑定元素,默认的自动绑定不好用
  2. 手动编码
    在这里插入图片描述

创建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)
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

korgs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值