Skip to Content

Cocoa 用程式操控視窗 (中)

Window 與 View 的生命周期

 

記錄一下自己測試的順序,這也是為什麼在 ViewController 中要設定 Window 的代理不能在第一個 viewDidLoad 之後做,因為此時 window 還沒有 windowDidLoad。要等到 viewWillAppear 時才能設定 window 的代理。

  • viewDidLoad
  • windowDidLoad
  • viewWillAppear
  • viewDidAppear
  • viewWillLayout
  • viewDidLayout
  • viewWillLayout
  • viewDidLayout
  • ------------------
  • windowShouldClose
  • windowWillClose
  • viewWillDisappear
  • viewDidDisappear

 

尋找元件

 

由 Outlet 名稱尋找

 

在 Windows 的 C++ Builder 時代,要處理一個視窗中的元件,例如要找 fmMain 視窗中一個 Label 叫 lbText 的內容,就直接寫

String sStr = fmMain->lbText->Caption 

就可以了,但我在 Mac 程式中,起初一直搞不定。

在 ViewController 中,還可以把元件拉到程式中,設一個 Outlet

@IBOutlet weak var lbText: NSTextField!

在程式中就可以直接使用 lbText。不過若是由A視窗要找B視窗的 Outlet,之前一直試不出來,後來才想通問題在哪裡。

在「程式開啟視窗 (不使用 Segue)」這一段中,曾經實作出視窗並呈現,也就是這一行。

OptionWin!.orderFront(self)

直覺想要處理該視窗的元件,就使用了

OptionWin!.lbText

卻產生了錯誤。

現在才知道應該要如此轉換

 

// 先找出 Window 的 contentViewController , 並且轉成我們的程式類別 OptionView

let vcOption = OptionWin!.contentViewController as! OptionView

 

// 這樣才能操作元件

vcOption.lbText.stringValue = "Hello!"

 

重點在於,寫 MS Window 的程式時,通常就處理視窗 (WinForm) 和元件即可。

但在 Mac 中,則有 NSWindowController、NSWindow、NSViewController、NSView 四種層面要區別,還有 NSWindowDelegate 代理,然後才是處理 NSView 當中的元件。這中間只要沒處理好,就是一堆錯誤來報答你。

由 tag 尋找

NSView 元件有一個 tag 屬性,可以填入一個整數。[官網說明]

若我們先在 lbText 這個 Label 的 tag 填入 100,則程式中可以這樣找到它。

 

// 因為 viewWithTag 是 NSView 的成員函式,所以要用 vcOption.view 來處理

let lbNew = vcOption.view.viewWithTag(100) as! NSTextField

 

lbNew.stringValue = "Hello!"

 

由 tag 來找並不是很方便,過去在 Windows 時代也用過,當時主要是用在臨時產生的一堆元件,例如樹狀目錄中的眾多節點,此時就對於不同的節點給予不同的 tag,方便程式辨識處理。

遍歷元件

view 還有一個 subviews 成員函式,可以找出該 view 的子視圖組。[官方說明]

使用遞迴的方式,就可以把所有的元件找出來了。

 

    @IBAction func btRun(_ sender: Any) {

        SearchItem(item: self.view,level: 1)

    }

    

    func SearchItem (item: NSView, level: Int){

        let subs = item.subviews

        // 遍歷元件

        for subitem in subs {

            // 印出層級與內容

            print ("\(level) : \(subitem)")

            // 遞迴處理

            SearchItem(item: subitem, level: level+1)

        }

    }

 

底下是一個 TableView 和一個 Button,以及它的結構。

底下則是用上面的遍歷方式,把所有的子視窗列出來,排版是後來手動處理的。

上圖與底下列表雙方似乎不太能完全對應起來?

 

1 : <NSButton: 0x101932fa0>

  2 : <NSButtonBezelView: 0x10180dac0>

  2 : <NSButtonTextField: 0x101815220>

1 : <NSScrollView: 0x1020d1800>

  2 : <_NSScrollViewContentBackgroundView: 0x10193a980>

    3 : <NSVisualEffectView: 0x101815e90>

  2 : <NSClipView: 0x101933d80>

    3 : <NSTableView: 0x101934080>

  2 : <NSClipView: 0x101935070>

    3 : <NSBannerView: 0x101935580>

      4 : <NSVisualEffectView: 0x101936cf0>

      4 : <_NSBannerDecorationView: 0x101936fd0>

    3 : <NSTableHeaderView: 0x101934a90>

  2 : <NSScroller: 0x101938fe0>

  2 : <NSScroller: 0x1019385b0>

 

遍歷 + 判斷元件種類

利用遍歷也可以處理指定的元件種類。

底下程式是利用遍歷把全部 Button 的標題換成 "Hello"。

 

    @IBAction func btRun(_ sender: Any) {

        SearchItem(item: self.view)

    }

    

    func SearchItem (item: NSView) {

        let subs = item.subviews

        // 遍歷元件

        for subitem in subs {

            // 判斷是不是能轉成 NSButton 

            if let myButton = subitem as? NSButton {

                myButton.title = "Hello"

            }

            SearchItem(item: subitem)

        }

    }

 

遍歷 + identifier

許多元件都有 identifier 這個識別碼,因此設定識別碼再加上遍歷,就可以找到指定的元件。

例如先把某個 Button 的 identifier 設定為 "mybt"。

底下程式是利用遍歷把該 Button 的標題換成 "Hello"。

 

    @IBAction func btRun(_ sender: Any) {

        SearchItem(item: self.view)

    }

    

    func SearchItem (item: NSView) {

        let subs = item.subviews

        // 遍歷元件

        for subitem in subs {

            // 有 identifier 的元件才處理

            if let id = subitem.identifier {

                // 判斷 identifier 的內容,要用 rawVale 處理

                if id.rawValue == "mybt" {

                    // 將元件轉換成 NSButton

                    let myButton = subitem as! NSButton

                    myButton.title = "Hello"

                }

            }

            SearchItem(item: subitem)

        }

    }

 

回應

發表新回應

這個欄位的內容會保密,不會公開顯示。
  • 自動將網址與電子郵件地址轉變為連結。
  • 自動斷行和分段。
  • 可使用的 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