Skip to Content

Cocoa 用程式操控視窗 (上)

 

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

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

 

程式開啟視窗 (使用 Segue)

 

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

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

選擇 Show。

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

在 Button 建立程式碼:

 

@IBAction func btToOption(_ sender: Any) {

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

}

如此就可以由程式來開啟 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 就不會開啟重複的視窗。

 

隱藏 View

 

view.isHidden = true 

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

 

隱藏 Window

 

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

view.windows!.orderOut(nil)

而呈現則是用

window!.orderFront(self)

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

 

程式關閉視窗

 

建立一個 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() {

        self.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。

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

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,目前原因不明。日後待查!

回應

發表新回應

這個欄位的內容會保密,不會公開顯示。
  • 自動將網址與電子郵件地址轉變為連結。
  • 自動斷行和分段。
  • 可使用的 HTML 標籤:<a> <address> <b> <blockquote> <br> <caption> <center> <cite> <code> <dd> <del> <div> <dl> <dt> <em> <h1> <h2> <h3> <h4> <h5> <h6> <hr> <i> <img> <ins> <li> <ol> <p> <pre> <span> <strike> <strong> <sub> <sup> <table> <tbody> <td> <th> <tr> <u> <ul>
    Allowed Style properties: background, background-attachment, background-color, background-image, background-position, background-repeat, border, border-bottom, border-bottom-color, border-bottom-style, border-bottom-width, border-collapse, border-color, border-left, border-left-color, border-left-style, border-left-width, border-right, border-right-color, border-right-style, border-right-width, border-spacing, border-style, border-top, border-top-color, border-top-style, border-top-width, border-width, bottom, caption-side, clip, color, direction, empty-cells, font, font-family, font-size, font-size-adjust, font-stretch, font-style, font-variant, font-weight, height, left, letter-spacing, line-height, list-style, list-style-image, list-style-position, list-style-type, margin, margin-bottom, margin-left, margin-right, margin-top, max-height, max-width, min-height, min-width, overflow, padding, padding-bottom, padding-left, padding-right, padding-top, right, table-layout, text-align, text-decoration, text-indent, text-transform, top, unicode-bidi, vertical-align, white-space, width, word-spacing, z-index

更多關於格式選項的資訊

CAPTCHA
驗證碼只有阿拉伯數字, 這是躲廣告用的, 麻煩你輸入了.
Image CAPTCHA
Enter the characters shown in the image.


story | about seo