本节主要讲述和容器相关的控件第二部分,主要包括panel、dialog以及文件打开和保存面板等等。
NSPanel
用来显示辅助性内容的窗口面板,包括文件打开、保存对话框、颜色、字体等都是NSPanel的子类。

基本设置
- title:面板标题
- controls:窗口三个按钮显示:最大,最小,关闭
- behabior:用于控制启动是否显示
- style:皮肤
显示和隐藏面板
这里需要注意只有xib-ui才有panel控件,而storyboard而没有此控件。拖动时需要拖动一个NSPanel到空白处,然后绑定到Appdelegate中。
@IBAction func nsPanelBtnTest(_ sender: NSButton) {
//显示弹出面板
self.window.beginSheet(
self.loginPanel,
completionHandler: { //关闭后的回调函数
[weak self] returnCode in let userName = self?.userNameField.stringValue
print("user \(returnCode)")
}
)
}
//隐藏面板
@IBAction func submitUserInfo(_ sender: NSButton) {
self.window.endSheet(self.loginPanel)
}
NSOpenPanel
文件打开面板,这个面板的打开通常直接绑定文件菜单中的打开上,比如下便中的文件fynd取。

基本设置
- canChooseFiles:是否允许一次选择多个文件;
- canChooseDirectories:是否允许选择目录;
- allowMutipleSelection:是否允许多选;
- allowedFileTypes:允许的文件后缀名;
- URLs:文件路径;
打开文件
打开一个txt文件并设置内容到一个textView中(这里绑定textView时注意其绑定的是最底层视图)
@IBOutlet var textView: NSTextView!
@IBAction func openFiles(_ sender: NSMenuItem) {
let openDlg = NSOpenPanel()
openDlg.canChooseFiles = true
openDlg.canChooseDirectories = false
openDlg.allowsMultipleSelection = false
openDlg.allowedFileTypes = ["txt"]
openDlg.begin(completionHandler: { [weak self] result in
if(result.rawValue == NSFileHandlingPanelOKButton){
let fileURLs = openDlg.urls
for url: URL in fileURLs {
guard let text = try? NSString.init(contentsOf: url, encoding: String.Encoding.utf8.rawValue)
else {
return
}
// as 是一种强制类型转换语法
self?.textView.string = text as String
}
}
})
}
NSSavePanel
文件保存面板,其功能和使用场景同open差不太多。

基本设置
- title:保存面板标题
- allowedFileTypes:可以保存的文件的后缀名称
- nameFieldStringValue:默认要保存的文件名
保存文件
同样的需要绑定save菜单
@IBAction func saveFiles(_ sender: NSMenuItem) {
let text = self.textView.string
let saveFileDlg = NSSavePanel()
saveFileDlg.title = "Save File"
saveFileDlg.message = "Save My File"
saveFileDlg.allowedFileTypes = ["txt"]
saveFileDlg.nameFieldStringValue = "my"
saveFileDlg.begin(completionHandler: { result in
if result.rawValue==NSFileHandlingPanelOKButton {
let url = saveFileDlg.url
_ = try? text.write(to: url!, atomically: true, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue))
}
})
}
测试保存文件时,在保存文件时需要打开相应的权限,如下:

颜色和字体设置
NSColorPanel

共享系统toolbar面板功能
@IBAction func chooseColor(_ sender: NSButton) {
let colorpanel = NSColorPanel.shared
//事件
colorpanel.setAction(#selector(changeTextColor(_:)))
colorpanel.setTarget(self)
colorpanel.orderFront(nil)
}
@objc func changeTextColor(_ sender: NSColorPanel){
let color = sender.color;
self.textView.textColor = color
}
NSFontManager
共享系统toolbar面板功能
@IBAction func chooseFont(_ sender: NSButton) {
let fontManager = NSFontManager.shared
fontManager.target = self
fontManager.action = #selector(changeTextFont(_:))
fontManager.orderFrontFontPanel(self)
}
lazy var font = NSFont()
@objc func changeTextFont(_ sender: NSFontManager){
self.font = sender.convert(self.font)
self.textView.font = self.font
}
NSAlert
显示提示信息,有时也可以用popup来代替,可以用于验证提示等场景。

基本设置
- messageText:提示信息
- informativeText:提示详细信息
- icon:图标
- alertStyle:样式,分为基本、警告和错误三类
编程示例
同样的也会存在一个名为 NSAlertDelegate的代理。
@IBOutlet weak var userNameForAlert: NSTextField!
@IBAction func userNameForAlert(_ sender: NSButton) {
let password = self.userNameForAlert.stringValue
if password.characters.count < 6 {
let alert = NSAlert()
//增加一个按钮
alert.addButton(withTitle: "Ok")
alert.addButton(withTitle: "Canel")
//提示的标题
alert.messageText = "Alert"
//提示的详细内容
alert.informativeText = "userName length must be more than 6 "
//设置告警风格
alert.alertStyle = .informational
alert .beginSheetModal(for: self.window!, completionHandler: { returnCode in
//当有多个按钮是 可以通过returnCode区分判断,三个按钮分别为1000, 1001, 1002
print("returnCode :\(returnCode)")
}
)
}
}
使用XIB创建面板
UI 绑定
- 新建一个xib文件,选择Application.xib, 也可选择Empty;
- 新建一个新NSPanel的子类,比如Application.Swift;
- 在xib-window的identity面板中绑定custom类为Application.Swift;


功能代码

定义全局变量
建议定义在一个单独的文件中
import Cocoa
let kCancelCode = 0
let kOkCode = 1
自定义面板实现
import Cocoa
class Application: NSPanel {
var datas = [NSDictionary]()
@IBOutlet weak var tableView: NSTableView!
override func awakeFromNib() {
super.awakeFromNib()
self.updateData()
}
func updateData() {
self.datas = [
["name":"john","address":"USA"],
["name":"mary","address":"China"],
["name":"park","address":"Japan"],
["name":"Daba","address":"Russia"],
]
}
@IBAction func okAction(_ sender: AnyObject?) {
self.parent?.endSheet(self, returnCode: NSApplication.ModalResponse(rawValue: kOkCode))
}
@IBAction func cancelAction(_ sender: AnyObject?) {
self.parent?.endSheet(self, returnCode: NSApplication.ModalResponse(rawValue: kCancelCode))
}
}
extension Application: NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
return self.datas.count
}
}
extension Application: NSTableViewDelegate {
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let data = self.datas[row]
//表格列的标识
let key = (tableColumn?.identifier)!
//单元格数据
let value = data[key]
//根据表格列的标识,创建单元视图
let view = tableView.makeView(withIdentifier: key, owner: self)
let subviews = view?.subviews
if (subviews?.count)!<=0 {
return nil
}
if key.rawValue == "name" || key.rawValue == "address" {
let textField = subviews?[0] as! NSTextField
if value != nil {
textField.stringValue = value as! String
}
}
return view
}
}
调用自定义面板
var topLevelArray: NSArray?
lazy var myPanel: Application? = {
var panel: Application?
// 加载xib文件
let nib = NSNib.init(nibNamed: NSNib.Name(rawValue: "Application"), bundle: Bundle.main)
// 实例化xib资源文件中的类,并存放在对象数组中
//topLevelObjects::是一个指向数组的指针,方法会将从 Nib 文件中实例化的顶级对象放入这个数组中。这些对象通常是视图控制器、视图或其他界面元素。
if let success = nib?.instantiate(withOwner: self, topLevelObjects: &topLevelArray) {
if success {
for obj in self.topLevelArray! {
if obj is Application {
//一种类型转换操作,具体来说是尝试将 obj 转换为 Application 类型的一个可选值(Application?)
panel = obj as? Application
break
}
}
}
}
return panel
}()
//按钮调用
@IBAction func showWindowAction(_ sender: NSButton) {
self.myPanel?.parent = self.window
self.window?.beginSheet(self.myPanel!, completionHandler: { returnCode in
if returnCode.rawValue == kOkCode {
print("returnCode \(returnCode)")
}
})
}

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



