- Xcode 用 Storyboard 開發程式記錄
- Cocoa 用程式操控視窗 (上)
- Cocoa 用程式操控視窗 (中)
- 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)
}
}
- Xcode 用 Storyboard 開發程式記錄
- Cocoa 用程式操控視窗 (上)
- Cocoa 用程式操控視窗 (中)
- Cocoa 用程式操控視窗 (下)
- 瀏覽次數:8692
發表新回應