我們新建一個(gè)Mac的工程,配置如下。

我們要選擇macOS的平臺(tái)選擇Cocoa Application的工程模板,點(diǎn)擊Next.

我們給工程命名OnceReplaceKey,(__),名字不是多么專業(yè)。Language選擇Swift,我們選中Use Storyboard.
點(diǎn)擊Next保存在我們Github項(xiàng)目在本地的主目錄。

我們用Xcode打開(kāi)剛才新建的工程,我們修改我們Target中的General里面的Deployment info為10.10。
貌似只有>=10.10的才支持Swift3.0.

我們發(fā)現(xiàn)運(yùn)行起來(lái)并不在中心點(diǎn)的位置,我們?cè)O(shè)置只要運(yùn)行就在中心點(diǎn)。
因?yàn)橹皼](méi)有接觸過(guò)
Mac的開(kāi)發(fā),因此也是不熟悉,我們谷歌一下。
經(jīng)過(guò)我們苦苦的查詢,然而。對(duì)于Mac開(kāi)發(fā)資料很少,我竟然沒(méi)找到。我們就自己找一下吧。

我們?cè)?code>Stroyboard里面的NSWindow設(shè)置那里發(fā)現(xiàn)這個(gè)位置,看顯示屏幕的位置就是我們剛才運(yùn)行的位置。
initial Position看英文的意思是初始化的坐標(biāo),這個(gè)應(yīng)該是的。
我們直接移動(dòng)屏幕四方塊到屏幕中心點(diǎn)的位置。發(fā)現(xiàn)還是不能準(zhǔn)確到屏幕中心位置,我們?cè)O(shè)置下面的選項(xiàng)框。

我們運(yùn)行再次的看一下。
發(fā)現(xiàn)還在那個(gè)位置,我們發(fā)現(xiàn)之前紅色的線變成了虛線,我們點(diǎn)擊試一下,竟然變成了實(shí)線,全點(diǎn)亮再次運(yùn)行試一下。
發(fā)現(xiàn)還不是,我們關(guān)閉軟件再次運(yùn)行,發(fā)現(xiàn)真的居中了。我們?cè)俅吻袚Q虛線,試一下,估計(jì)也是剛才已經(jīng)編譯的結(jié)果。
果然如我們想象的樣子,看來(lái)以后運(yùn)行之前最好清掉運(yùn)行中。

這是我們的原型,我們?cè)囍?code>Storyboard里面試著布局出來(lái)。我們按照500x400大小制作的原型,我們也設(shè)置工程試圖大小為500x400。
我們?cè)诳丶阉骼锩孑斎?code>label關(guān)鍵詞,發(fā)現(xiàn)搜索出來(lái)的還是NSTextFiled只是輸入框禁用了,看來(lái)Mac是沒(méi)有NSLabel的這個(gè)屬性的。
因?yàn)檩斎肟虿季质亲詣?dòng)計(jì)算的,我們防止一個(gè)NSView高度為40,上邊距,左邊距,右邊距分別是0。

我們放置一個(gè)顯示文本的控件放在主視圖上面,設(shè)置和父試圖居中。

我們放置一個(gè)NSTableView的控件?約束如下。

我們放置NSView緊接著剛才表格的下面。
我們運(yùn)行一下,看一下效果。

此時(shí)我們的界面搭建完畢。
我們發(fā)現(xiàn)缺少一個(gè)導(dǎo)入和導(dǎo)出的功能,我們?cè)诓藛?code>File選項(xiàng)新增兩個(gè)功能導(dǎo)出,導(dǎo)入。

我們?cè)?code>AppDelegate去實(shí)現(xiàn)這兩個(gè)功能。
@IBAction func importAction(_ sender: Any) {
}
@IBAction func exportAction(_ sender: Any) {
}
因?yàn)榭紤]到導(dǎo)入導(dǎo)出還有隨時(shí)保存的功能都需要文件管理,我們新建一個(gè)類OFileManger.swift.
import Cocoa
class OFileManger: NSObject {
}
我們?cè)?code>OFileManger類新增class func importAction()方法來(lái)實(shí)現(xiàn)導(dǎo)入的功能。
因?yàn)橐蜷_(kāi)一個(gè)文件,我們百度了一下。需要用到NSOpenPanel這個(gè)類。我們寫一下代碼。
let openPannel = NSOpenPanel()
openPannel.runModal()
我們調(diào)用一下這個(gè)方法看看效果。

貌似任何文件都可以選擇,我們只允許加載我們自己的文件類型,我們?cè)O(shè)置我們導(dǎo)出的文件類型為.ork取工程名稱的前一個(gè)字母。
我們?cè)谧宇?code>NSSavePannel找到了下面的屬性
open var allowedFileTypes: [String]?
我們趕緊設(shè)置一下,看一看是否達(dá)到我們的需求。
openPannel.allowedFileTypes = ["ork"];
之前可以選擇的文件已經(jīng)不能選擇,看來(lái)我們已經(jīng)設(shè)置正確。我們?cè)谧烂嫘陆ㄒ粋€(gè)demo.ork文件,測(cè)試一下。
/* NSSavePanel/NSOpenPanel: Presents the panel as an application modal window. It returns only after the user has closed the panel. The return value is NSFileHandlingPanelOKButton==1 or NSFileHandlingPanelCancelButton==0.
*/
open func runModal() -> Int
這個(gè)方法注釋說(shuō)明返回值代表我們點(diǎn)擊什么類型的按鈕,我們只需要點(diǎn)擊確定按鈕,獲取剛才選中的文件即可。
let buttonIndex = openPannel.runModal()
guard buttonIndex == NSFileHandlingPanelOKButton else {
return
}
下面的屬性是Get屬性,一個(gè)數(shù)組,是我們剛才選中的一組文件或者單個(gè)文件。
open var urls: [URL] { get }
我們不可能讓用戶可以選擇多個(gè)配置文件,我們?cè)O(shè)置一下只能選擇單個(gè)文件。
openPannel.allowsMultipleSelection = false
我們獲取選中文件。
guard openPannel.urls.count > 0 else {
return
}
let fileName = openPannel.urls.first
獲取這個(gè)文件的內(nèi)容。
do {
let dataString = try String(contentsOfFile: fileName)
} catch _ {
let alert = NSAlert()
alert.messageText = "打不開(kāi)次配置文件!"
alert.runModal()
}
如果把獲取的字符串轉(zhuǎn)成Json對(duì)象。修改上面的代碼改成下面的
guard let fileName = openPannel.urls.first else {
return
}
guard let jsonData = try? Data(contentsOf: fileName) else {
return
}
guard let jsonObj = try? JSONSerialization.jsonObject(with: jsonData, options: .allowFragments) else {
return
}
轉(zhuǎn)換成數(shù)組。因?yàn)槲覀兙褪窍氆@取配置文件的數(shù)組對(duì)象。
class func importAction() -> [Any]? {
let openPannel = NSOpenPanel()
openPannel.allowedFileTypes = ["ork"];
openPannel.allowsMultipleSelection = false
let buttonIndex = openPannel.runModal()
guard buttonIndex == NSFileHandlingPanelOKButton else {
return nil
}
guard openPannel.urls.count > 0 else {
return nil
}
guard let fileName = openPannel.urls.first else {
return nil
}
guard let jsonData = try? Data(contentsOf: fileName) else {
return nil
}
guard let jsonObj = try? JSONSerialization.jsonObject(with: jsonData, options: .allowFragments) else {
return nil
}
guard let configList:[Any] = jsonObj as? [Any] else {
return nil
}
return configList
}
我們讓給方法增加異常,新增錯(cuò)誤類型。
enum OFileMagerImportError:Error {
case cannel //點(diǎn)擊了取消的按鈕
case urlListEmpty // 獲取文件路徑為空
case readFileError // 讀取文件的內(nèi)容失敗
case notJsonObject // 不是一個(gè)JSON對(duì)象
case notArrayOnject // 不是一個(gè)數(shù)組對(duì)象
}
修改我們的方法成下面。
/*
* 導(dǎo)入配置文件
* return 返回一個(gè)數(shù)組對(duì)象 可能返回為空
*/
class func importAction() throws -> [Any]? {
let openPannel = NSOpenPanel()
openPannel.allowedFileTypes = ["ork"]; // 只允許讀取.ork的文件類型
openPannel.allowsMultipleSelection = false // 設(shè)置不允許多選
let buttonIndex = openPannel.runModal()
guard buttonIndex == NSFileHandlingPanelOKButton else {
throw OFileMagerImportError.cannel
}
guard openPannel.urls.count > 0 else {
throw OFileMagerImportError.urlListEmpty
}
guard let fileName = openPannel.urls.first else {
throw OFileMagerImportError.urlListEmpty
}
guard let jsonData = try? Data(contentsOf: fileName) else {
throw OFileMagerImportError.readFileError
}
guard let jsonObj = try? JSONSerialization.jsonObject(with: jsonData, options: .allowFragments) else {
throw OFileMagerImportError.notJsonObject
}
guard let configList:[Any] = jsonObj as? [Any] else {
throw OFileMagerImportError.notArrayOnject
}
return configList
}
我們修改我們導(dǎo)入的方法
@IBAction func importAction(_ sender: Any) {
guard let configList:[Any] = try! OFileManger.importAction() else {
return
}
}
今天的教程到此就結(jié)束了,下一篇教程繼續(xù)。