Cocoa 用程式操控視窗 (上)

 

在這系列【Xcode 用 Storyboard 開發程式記錄】已經學會了建立視窗與畫面,開啟視窗,以及為元件寫簡單程式。

底下這一系列就要學習如何用程式來開啟視窗,以及跨視窗操作視窗和元件等各種功能,最後再實作一個總複習,讓視窗與元件的控制可以自在無礙,這也是未來設計 CBReader Mac 版的的前測試。

 

程式開啟視窗 (使用 Segue)

 

如果不由 Button 建立 Segue 自動開啟視窗,而想由程式控制,方法如下:

不要由 Button 連結到第二個視窗,而是由第一個視窗的 View Controller 連結到子視窗。

image

選擇 Show。

image

點選藍色的 Segue,並為其命名 toOption。

image

在 Button 建立程式碼:

 

@IBAction func btToOption(_ sender: Any) {

    performSegue(withIdentifier: "toOption", sender: self)

}

image

如此就可以由程式來開啟 Option 視窗了。

 

程式開啟視窗 (不使用 Segue)

 

設計一個按鈕,直接由 storyboard 取得資料,產生 WindowController,並使之呈現。

@IBAction func btToOption(_ sender: Any) {

// 取得 storyboard

let storyboard = NSStoryboard(name: "Main", bundle: nil)

// 取得 WindowController

let OptionWinController = storyboard.instantiateController(withIdentifier: "OptionWindowController") as! NSWindowController

     

// 取得 window

let OptionWin = OptionWinController.window

 

// 呈現視窗

OptionWin!.orderFront(self)

}

 

取消多重視窗設定

 

在 View Controller 的 Presentation 設定 Single 就不會開啟重複的視窗。

image

 

隱藏 View

 

view.isHidden = true 

這功能可以把視窗清空,但視窗還在,只有內容隱藏。

 

隱藏 Window

 

如果只是要隱藏視窗,可以使用

view.windows!.orderOut(nil)

而呈現則是用

window!.orderFront(self)

不過這應該是要由另一個 View 或功能來呼叫一個已經隱藏的 Window,因為原來的 Widow 已經隱藏起來了。

補充:orderFront 會把視窗顯現出來,但並不會取得焦點,可以改用 makeKeyAndOrderFront 就可以了。

程式關閉視窗

 

建立一個 Button,在 Button 撰寫關閉視窗的程式,網路上有三種方法:

windowController.close()

self.view.window!.windowController!.close()

這是 NSWindowController 的 close(),這個方法不會要求用戶確認而直接關閉視窗。[官方說明]

Window.close()

self.view.window!.close()

這是 NSWindow 的 close(),它會通知發佈 willCloseNotification 到默認通知中心。[官方說明]

window.performClose() 

self.view.window!.performClose(nil) 

或 

self.view.window!.performClose(self) 

這個方法比較靈活。

如果窗口的委託或窗口本身實現 windowShouldClose(_:),則以窗口作為參數發送該消息。(僅發送一條這樣的消息;如果委託和 NSWindow 對像都實現了該方法,則只有委託接收該消息。)如果 windowShouldClose(_:) 該方法返回 false,則不會關閉窗口。如果返回 true,或者未實現,則 performClose(_:) 調用 close() 該方法以關閉窗口。

如果窗口沒有關閉按鈕或無法關閉(例如,如果委託人回復 false 給windowShouldClose(_:) 訊息),則系統會發出警報聲。[官方說明]

 

執行 windoShouldClose

 

視窗關閉時,如果有實作 windowShouldClose(_:),則會執行它,若傳回 true 才會關閉視窗,傳回 false 則不會關閉。

實作的方法有二種:

 1. 在 ViewController 實作

第一種方法是在 ViewController 自身中實作。底下程式中,紅色是後來加上去的。

1. 在 class 要繼承 NSWindowDelegate,因為 windowShouldClose 是 NSWindowDelegate 的成員函式。

2. 撰寫 override viewWillAppear,並在其中加入

self.view.window!.delegate = self

表示 View 的 Window 的代理就是 ViewController 這個 class。

之前是寫在 viewDidLoad 中,所以一直失敗,因為此時 view.window 還沒產生,無法指定代理。

根據 Window 與 View 的生命周期,viewDidLoad 之後才有 windowDidLoad,所以在 viewWillAppear 中指定代理就可以了。

  • viewDidLoad
  • windowDidLoad
  • viewWillAppear
  • ......

3. 之後就可以實作 windowShouldClose 和 windowWillClose 了。

import Cocoa

 

// 繼承 NSWindowDelegate

class ViewController: NSViewController , NSWindowDelegate {

 

    override func viewDidLoad() {

        super.viewDidLoad()

        // Do any additional setup after loading the view.

    }

 

    override var representedObject: Any? {

        didSet {

        // Update the view, if already loaded.

        }

    }

 

    // 在 viewWillAppear 指定 window 的代理

    override func viewWillAppear() {

        view.window!.delegate = self

    }

 

    func windowShouldClose(_ sender: NSWindow) -> Bool {

        print("Window Should Close")

        return true

    }

    

    func windowWillClose(_ notification: Notification) {

        print("Window Will Close")

    }

}

2. 子類化 NSWindowController

第二種方法是子類化 NSWindowController,在其自身實作。

1. 建立新的 Cocoa Class 檔案,選 NSWindowController,命名為 MainWindowController。

image

2. 在 Storyboard 的 Window 點選上方紅框 Window Controller,然後在左邊的 Class 選 MainWindowController,表示指定 Window Controller 是剛剛建立的 Class。

image

3. 在 MainWindowController 中,先繼承 NSWindowDelegate,因為 windowShouldClose 是 NSWindowDelegate 的成員函式。

4. 之後就可以實作 windowShouldClose 和 windowWillClose 了。

import Cocoa

 

// 繼承 NSWindowDelegate

class MainWindowController: NSWindowController, NSWindowDelegate {

 

    override func windowDidLoad() {

        super.windowDidLoad()

        //window!.delegate = self

        // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.

    }

    

    func windowShouldClose(_ sender: NSWindow) -> Bool {

        print("Window Should Close")

        return true

    }

    

    func windowWillClose(_ notification: Notification) {

        print("Window Will Close")

    }

}

 

看起來第二個方法好像稍為麻煩一點。

 

附註:

如果用第二種方法,而視窗又是用程式開啟,也就是上面不使用 Segue 開啟的視窗,則關閉時好像無法執行 windowShouldClose,目前原因不明。日後待查!

重要度:
文章分類:

發表新回應