創(chuàng)建Document應(yīng)用絮记,默認(rèn)情況下,啟動(dòng)應(yīng)用就會(huì)彈出一個(gè)窗口省核,此時(shí)一個(gè)NSDocument對(duì)應(yīng)一個(gè)NSWindowController掂僵。如果要new一個(gè)新的文檔航厚,這個(gè)時(shí)候就會(huì)彈出一個(gè)新的窗口。如果我們想要?jiǎng)?chuàng)建一個(gè)應(yīng)用锰蓬,每次new/open一個(gè)文檔的時(shí)候幔睬,該文檔都在一個(gè)窗口上創(chuàng)建或打開(kāi),就是用單窗口容納多個(gè)文檔(譬如瀏覽器用單窗口容納多個(gè)標(biāo)簽頁(yè)面芹扭,word文檔用單窗口容納多個(gè)文檔)麻顶,我們?cè)撛趺磳?shí)現(xiàn)呢。
首先我們先搞清楚NSDocumentController舱卡、NSDocument辅肾、NSWindowController、NSWindow四者的關(guān)系轮锥,通常情況下矫钓,文檔應(yīng)用只有一個(gè)NSDocumentController對(duì)象。新建一個(gè)文檔過(guò)程:首先創(chuàng)建一個(gè)NSDocument對(duì)象,這個(gè)對(duì)象會(huì)被NSDocumentController對(duì)象調(diào)用addDocument方法來(lái)加到自己的documents容器去新娜,然后系統(tǒng)會(huì)調(diào)用文檔的makeWindowControllers方法來(lái)創(chuàng)建自己的NSWindowController對(duì)象赵辕,將NSDocument與NSWindowController一一匹配。makeWindowControllers默認(rèn)實(shí)現(xiàn)就是加載指定的nib文件來(lái)創(chuàng)建自己的windowcontroller概龄。為了實(shí)現(xiàn)單窗口多文檔还惠,這個(gè)時(shí)候我們就需要一個(gè)NSWindowController對(duì)應(yīng)N個(gè)NSDocument,我們需要重載NSWindowController來(lái)實(shí)現(xiàn)我們自己的windowController私杜,且該windowController支持單例模式蚕键,就是只有唯一的一個(gè)NSWindowController對(duì)象,這樣就只有唯一的一個(gè)文檔窗口(NSWindowController對(duì)應(yīng)一個(gè)NSWindow衰粹,也對(duì)應(yīng)一個(gè)包含window的nib/xib文件)锣光。
makeWindowControllers實(shí)現(xiàn)代碼:
- (void)makeWindowControllers{
? ? [self addWindowController:[MyMainWindowController shareMainWindowController]];
}
雖然說(shuō)這樣實(shí)現(xiàn)可以保證我們新建或打開(kāi)文檔都只有唯一個(gè)窗口,但是如何將多文檔在一個(gè)窗口顯示呢寄猩?首先我想到的實(shí)現(xiàn)方式是用兩個(gè)NSCollectionView來(lái)實(shí)現(xiàn)嫉晶,一個(gè)用來(lái)裝標(biāo)簽,另一個(gè)用來(lái)裝文檔田篇,當(dāng)我們新建一個(gè)文檔的時(shí)候,我們就讓兩個(gè)collectionView同時(shí)reloadData來(lái)刷新界面箍铭,有多少個(gè)文檔泊柬,就有多少個(gè)標(biāo)簽,一個(gè)文檔與一個(gè)標(biāo)簽一一對(duì)應(yīng)诈火。實(shí)現(xiàn)邏輯是想到了兽赁,但是問(wèn)題是在哪里調(diào)用reloadData來(lái)刷新界面是關(guān)鍵。
這個(gè)時(shí)候我們就要來(lái)理清楚新建文檔冷守、打開(kāi)文檔刀崖、打開(kāi)最近、本地目錄雙擊文件打開(kāi)拍摇、應(yīng)用異常后下次自動(dòng)打開(kāi)都會(huì)涉及到那些內(nèi)部函數(shù)了亮钦。通過(guò)跟蹤這些步驟發(fā)現(xiàn),只需要重載三個(gè)函數(shù):
1,新建文件
- (__kindof NSDocument *)openUntitledDocumentAndDisplay:(BOOL)displayDocument error:(NSError *__autoreleasing? _Nullable *)outError;
2充活,打開(kāi)文件蜂莉,打開(kāi)最近菜單文件,雙擊文件打開(kāi)應(yīng)用會(huì)調(diào)用這個(gè)函數(shù)
- (void)openDocumentWithContentsOfURL:(NSURL *)url display:(BOOL)displayDocument completionHandler:(void (^)(NSDocument * _Nullable, BOOL, NSError * _Nullable))completionHandler;
3混卵,保存過(guò)的文件再編輯異常后映穗,或者新建文件編輯后沒(méi)保存過(guò)異常會(huì)調(diào)用這個(gè)函數(shù)來(lái)打開(kāi)
- (void)reopenDocumentForURL:(NSURL *)urlOrNil withContentsOfURL:(NSURL *)contentsURL display:(BOOL)displayDocument completionHandler:(void (^)(NSDocument * _Nullable, BOOL, NSError * _Nullable))completionHandler;
我們只需要重載這三個(gè)函數(shù),添加我們的刷新兩個(gè)NSCollectionView的代碼幕随,就能實(shí)現(xiàn)單窗口多標(biāo)簽文檔蚁滋。
到這里似乎好像就已經(jīng)完成了單窗口多標(biāo)簽文檔了,但其實(shí)還沒(méi)有完的。我們知道系統(tǒng)默認(rèn)是一個(gè)NSDocument對(duì)應(yīng)一個(gè)NSWindowController的辕录,此時(shí)我們窗口上有多個(gè)文檔澄阳,而只有一個(gè)NSWindowController,而NSWindowController它有一個(gè)成員document踏拜,就是記錄NSWindowController對(duì)應(yīng)的文檔∷橛現(xiàn)在乍看完了,怎么辦速梗,好像需要一一對(duì)應(yīng)肮塞,感覺(jué)做不到單窗口對(duì)應(yīng)多文檔了。
首先我想到的是姻锁,我們能不能在點(diǎn)擊標(biāo)簽的時(shí)候相應(yīng)的修改document變量到正確的文檔去枕赵,就是相應(yīng)的切換NSDocumentController的currentDocument對(duì)象,通過(guò)修改document變量好像還真的相應(yīng)的改變了currentDocument位隶,但是還是發(fā)現(xiàn)問(wèn)題了拷窜,當(dāng)我編輯了一個(gè)文檔后,切換到另一個(gè)文檔后再切換回來(lái)涧黄,我的編輯狀態(tài)丟失了(就是標(biāo)題欄中-Edited標(biāo)志不見(jiàn)了篮昧,該文檔相當(dāng)于沒(méi)編輯過(guò)了,如果關(guān)閉窗口或關(guān)閉應(yīng)用笋妥,不會(huì)詢問(wèn)是否要保存文檔而只會(huì)默認(rèn)關(guān)閉導(dǎo)致數(shù)據(jù)丟失了)懊昨。換句話說(shuō),簡(jiǎn)單的賦值document來(lái)改變currentDocument是有問(wèn)題的春宣,查看document屬性相關(guān)文檔酵颁,也建議不要修改該變量。這下徹底沒(méi)轍了月帝,還有辦法在點(diǎn)擊標(biāo)簽切換文檔時(shí)能改變NSWindowController中的document屬性和NSDocumentController中的currentDocument嗎躏惋?
答案是有的,我們只需要在點(diǎn)擊標(biāo)簽切換文檔的時(shí)候調(diào)用文檔的makeWindowControllers方法嚷辅,它會(huì)幫我們實(shí)現(xiàn)這一切簿姨,真的是謝天謝地啊。(關(guān)于點(diǎn)擊標(biāo)簽展示相應(yīng)的文檔只需要滾動(dòng)到相應(yīng)的文檔位置潦蝇,而鼠標(biāo)滑動(dòng)文檔切換也只需要變更標(biāo)簽的選中狀態(tài)來(lái)表示標(biāo)簽變化款熬,這些都很好實(shí)現(xiàn)就不一一討論)
到這里我們基本已經(jīng)實(shí)現(xiàn)了單窗口多標(biāo)簽文檔的編輯保存及在打開(kāi),但是還有最后一個(gè)問(wèn)題需要解決攘乒。雖然在退出應(yīng)用的時(shí)候系統(tǒng)會(huì)咨詢我們是否要處理多個(gè)編輯過(guò)的文檔贤牛,這個(gè)時(shí)候我們保存或者不處理都沒(méi)有問(wèn)題。但是则酝,如果編輯過(guò)多個(gè)文檔后點(diǎn)擊窗口的關(guān)閉按鈕就沒(méi)有那么順利了殉簸,因?yàn)榇丝痰膚indow只知道一個(gè)文檔闰集,就是NSWindowController的屬性document記錄的那個(gè)文檔,也是NSDocumentController中的currentDocument般卑。系統(tǒng)只會(huì)詢問(wèn)是否處理該文檔武鲁,而其它文檔會(huì)忽略掉。下次打開(kāi)應(yīng)用的時(shí)候蝠检,因?yàn)橹坝行┪臋n并沒(méi)有從NSDocumentController中移除沐鼠,系統(tǒng)會(huì)當(dāng)是異常了而重新打開(kāi)那些文檔。雖然似乎沒(méi)什么問(wèn)題叹谁,但顯然這不是我們要的結(jié)果饲梭。我們想當(dāng)然是希望我們點(diǎn)擊窗口關(guān)閉按鈕的時(shí)候跟退出應(yīng)用一樣,能詢問(wèn)我們時(shí)候處理所有編輯過(guò)的文檔焰檩,而不是只處理當(dāng)前文檔憔涉。
此時(shí),我們要做的事情就是重新實(shí)現(xiàn)窗口關(guān)閉的功能啦析苫。具體如下:
? ? NSButton* closeButton = [self.window standardWindowButton:NSWindowCloseButton];
? ? [closeButton setTarget:self];
? ? [closeButton setAction:@selector(closeWindow:)];
closeWindow的實(shí)現(xiàn)兜叨,一個(gè)簡(jiǎn)單的方式就是調(diào)用終止應(yīng)用的代碼,這個(gè)時(shí)候點(diǎn)擊窗口關(guān)閉和退出應(yīng)用的過(guò)程如出一轍衩侥,都會(huì)提示是否要處理編輯過(guò)的文檔国旷。但又有問(wèn)題,此時(shí)應(yīng)用會(huì)完全退出啦顿乒,如果我們只想做到關(guān)閉窗口而不退出應(yīng)用议街,我們可以遍歷documents對(duì)象,逐個(gè)檢查是否編輯過(guò)璧榄,編輯過(guò)的文檔調(diào)用- (void)canCloseDocumentWithDelegate:(id)delegate shouldCloseSelector:(SEL)shouldCloseSelector contextInfo:(void *)contextInfo來(lái)彈出窗口來(lái)處理。
到這里基本解決所有遇到的問(wèn)題了吧雹,通常我們可能還想把NSCollectionView中的滾動(dòng)條隱藏掉骨杂。看似隱藏滾動(dòng)條是非常簡(jiǎn)單的事雄卷,但如果大家在添加NSCollectionView的時(shí)候是直接在xib/nib可視文檔上添加的搓蚪,那就會(huì)遇到設(shè)置NSScrollView的NSScroller隱藏?zé)o效,怎么自定義都沒(méi)用丁鹉,關(guān)于如何隱藏滾動(dòng)條請(qǐng)參考我寫的另外一篇文章了妒潭。文章鏈接