編譯自:https://www.raywenderlich.com...html
(本文原文十分值得一讀,然而我翻譯的略渣,有些直譯不出來的,是我根據理解編的。務必各類指出錯誤。基於此,暫時請勿轉載)swift
蘋果公司的開發框架一直圍繞着 Modal-View-Controller,提供了多種控制器對象用於管理 UI,以便於咱們的代碼,易於理解,便於維護。windows
視圖控制器是 OS X 程序中一個極其重要的概念,它是 Modal 層和 View 層之間的橋樑。數組
本文探討的內容較多,包括使用視圖控制器構建你的應用程序、視圖控制器重要的回調事件以及與窗口控制器的比較。瀏覽器
開始以前,你須要裝好最新版的 OS X 和 Xcode。你要開發的可不是一個鬧着玩的應用程序!推薦你先讀讀 Garbriel Miro’s 的窗口和窗口視圖指南,但這不是必須的。安全
視圖控制器用於管理視圖以及視圖的子視圖。 OS X 中,繼承自 NSViewController
。app
OS X 10.5 引入了視圖控制器,那時它不是 responder chain 的一部分。這有什麼影響呢?舉例來講,視圖控制器上面有一個按鈕,然而它卻不能處理按鈕的事件,很尷尬吧。OS X 10.10 改進了視圖控制器,從那之後,它成爲一個構建複雜界面時十分有用的工具。框架
有了視圖控制器,能夠很好的規劃你的窗口。視圖控制器專一與視圖有關的交互和事件,像調整窗口大小、關閉窗口這種與窗口有關的事件只放在窗口控制器中處理。因而代碼就變的很乾爽。ide
使用視圖控制器額外的好處是易於複用。好比你有一個文件瀏覽器,左邊部分的文件瀏覽視圖是經過視圖控制器來實現的,這時你剛好須要一個相似的視圖,你能很容易的複用它。剩下的時間和精力陪女友逛逛街也好啊。工具
那麼,啥時候使用窗口控制器,啥時候使用視圖控制器呢?
若是你期待的視圖控制器工做行爲是 UIViewController
那種,那麼 OS X 10.10 Yosemite 之前的 NSViewController
會讓你失望。
Apple 基於 MVC 設計了 iOS 的UIViewController
視圖控制器,管理視圖的生命週期、視圖操做、響應控件事件等都包含其中,10.10 以後的NSViewController
加入了這些特性。在視圖控制器裏面完成你的視圖,以及響應和視圖有關的事件,而後設置窗口控制器的主 viewController、大小、標題等成爲了標準流程。
通過這些改進,構建複雜交互的時候,能夠良好的解耦,在多個視圖控制器中完成需求,而後整合到一塊兒。
(譯註:這段實在是翻譯不出來,是根據意思寫出來的,請對照原文使用)
本教程將經過開發一個名爲RWStore
的應用程序,用於選擇查看不一樣raywenderlich.com store 的書籍來實踐。
打開 Xcode 選擇建立新工程,而後從模板中選擇OS X\ Application\ Cocoa Application
,而後點擊Next
將這個項目命名爲RWStore
。使用 Swift 做爲開發語言,同時勾選上 Use Storyboards
。勾選掉單元測試和 UI 測試的選項,你暫時還不須要他們。點擊 Next
保存你的項目。
下載項目須要的資源文件。這個壓縮包包含所需的圖片以及書籍商品所須要的數據,他們保存在Products.plist
文件。此外你還能看到一個名爲Product.swift
的源碼文件。這個文件包含Product
類,它解析了 Product 對象的結構。接下來把他們添加到RWStore項目中。
從項目導航中選擇選擇Assets.xcassets
,將剛剛下載的圖片資源拖進去。
而後將Products.plist
和Product.swift
拖到項目導航中。確保勾選了Copy items if needed
。
這時編譯運行應用程序。
能夠看到空白的主窗口,但不要驚慌,正常運行就是好的開始。
打開Main.storyboard
,選擇View Controller Scene
,拖拽一個pop-up 按鈕
到 view 中,以後會用到。
經過 AutoLayout 來設置 它的位置。選擇剛剛拖拽的 Pop-up 按鈕,點擊下方的Pin
按鈕。在彈出來的窗口中,將其Leading
、Trailing
、Top
的約束值都設置爲Use Standard Value
。
接着完成界面。拖拽一個 container view
放到剛剛添加的 pop-up
按鈕下方。
container view
是一個佔位視圖,其餘視圖或者視圖控制器能夠經過它顯示。
選擇剛剛添加的container view
,點擊下方的Pin
阿牛。添加top、bottom、trailing、leading
四個約束,將其設置爲0。而後點擊Add 4 constrains
按鈕。
選擇你 storyboard 中的視圖控制器,而後點擊Pin
按鈕右側的Resolve Auto Layout Issues
按鈕,選擇All Views in Controller/Update Frames
。這時你的界面看起來是這個樣子的:
如今在代碼中響應你的視圖行爲。打開Assistant Editor
(快捷鍵[alt] + [cmd] + [enter])確認 ViewCotroller.swift 已經被打開。拖拽pop-up
按鈕到ViewController.swift
中,添加行爲鏈接,命名爲valueChanged
,類型是NSPopUpButton
。
剛剛建立的 Container 視圖,自帶了一個以 embed 方式鏈接的視圖控制器,咱們須要自定義,選擇它並刪除。
如今,咱們將添加一個視圖控制器,用於顯示 Product 信息:咱們選擇Tab View Controller
。它的視圖包含幾個選項卡,以及視圖控制器。每個選項卡對應一個視圖控制器。選項卡切換的時候,對應的視圖控制器被切換顯示。
選擇Tab View Controller
,拖拽到Storyboard
中。
將剛剛添加的Tab View Controller
與Container View
使用embed
方式鏈接起來:
雙擊左側的選項卡,將標題改成Overview
;雙擊右側的選項卡,將標題改成Details
。
編譯並運行應用程序。
能夠看到,剛剛咱們設計的視圖控制器已經能正常顯示了,點擊選項卡也能正常切換對應的視圖控制器。由於咱們尚未爲其添加內容,因此兩個視圖控制器如今都仍是空白。
接下來須要建立這個。
File\New\File
,選擇OS X\Source\Cocoa Class
,點擊Next
。類名爲OverviewController
,繼承自NSViewController
,不要勾選Also Create XIB for user interface
,點擊Next
建立完成並保存。
回到Main.storyboard
,選擇Overview Scene
。點擊視圖上藍色的按鈕,選擇類對象,在右側的Identity Inspector
的 class 輸入框中輸入 OverviewController。
拖拽三個 label
到 OverviewController
的視圖的左上方,一個接一個的排列。添加一個image view
在視圖的右上角。
提示
:默認狀況下,image view 沒有邊框,給它設置個圖片,這樣好找。選擇Attributes Inspector
,選擇games
在Image
字段。這個圖片是剛剛資源文件裏的,應該能夠看到效果啦。
選擇最最上面的標籤。Attributes Inspector
裏面將字體設置爲System Bold
,字號設置爲 19。
這時候的視圖看起來是這樣的:
好!讓咱們使用 AutoLayout 來調整一下佈局。
選擇 image view,點擊下面的Pin
按鈕。給其添加約束:top 和 trailing設置爲 standard value
,width
和height
的值設置爲 180。
選擇最上面的標籤,仍是添加約束,將top、bottom、leading 和 trailing
設置爲 standard value
。
選擇挨着的下面的標籤,添加約束:將trailing 和 leading
設置爲standard value
。
選擇最下面的一個標籤,添加約束:將leading、trailing、bottom
設置爲standard value
。點擊top
約束,確認image view
是選擇狀態,而後選擇Use the standard value
。
提示:
若是你不能看到 image view 在選擇菜單,請確保 label 足夠寬,且置於 image view 的下方。
點擊下方區域的Resolve Auto Layout
,選擇All Views in Controller/Update Frames
,你的視圖看起來應該是這樣的:
界面工做到如今能夠告一段落了,編譯運行,如今他長這樣:
點擊標籤按按鈕這時能看到視圖控制器之間的差異了。咱們一行代碼沒寫就獲得了一個不錯的界面。
先來把界面上的控件鏈接到你的代碼中。
打開Assistant Editor
,選擇OverviewViewController.swift
。按着Ctrl
而後拖拽到OverviewController.swift
中,命名爲titleLabel
。類型爲NSTextField
。
重複上面的操做,將剩餘的控件都鏈接到代碼中:
中間的標籤命名爲:priceable
下面的標籤命名爲:descriptionLabel
image view 命名爲:productImageView
和大多數 UI 控件同樣,標籤和 image view 都有子視圖,選擇的時候仔細一下,別選錯了,好比NSImageView
選成了NSImageCell
,NSTextField
選成了NSTextFieldCell
。
點擊OverviewController
,加上下面的代碼:
//1 let numberformatter = NSNumberFormatter() //2 var selectedProduct: Product? { didSet { updateUI() } }
這段代碼:
number formatter
是一個NSNumberFormatter
,用於正確格式化價格。
selectedProduct
對應擋圈選擇的商品。每當值發生變化,didSet
裏面的代碼被執行,而後調用updateUI()
更新界面。
如今給OverviewController
添加updateUI
方法。
private func updateUI() { //1 if viewLoaded { //2 if let product = selectedProduct { productImageView.image = product.image titleLabel.stringValue = product.title priceLabel.stringValue = numberformatter.stringFromNumber(product.price)! descriptionLabel.stringValue = product.descriptionText } } }
經過 viewLoaded 屬性判斷 NSViewController 是否已經加載,若是已經加載完畢,就能夠安全的訪問與視圖有關的屬性了。
解包selectedProduct
肯定是否已經選擇了產品。而後顯示正確的值。
這個方法如今已經會在產品變換的時候調用,還須要在視圖加載完畢的時候調用。
從視圖控制器具有響應視圖事件能力開始,它就爲視圖生命的各個階段提供了各類回調事件。好比視圖從 storyboard 被加載,或者顯示在屏幕上這種都屬於被 Hook 的事件範圍。全部這些機遇事件的方法被統稱爲view controller life cycle
。
視圖控制器生命週期能夠被劃分紅三個主要部分:建立、運轉、終止。每個部分都提供了可重載的方法知足你的須要。
viewDidLoad()
當視圖被首次完整加載的時候調用,一些只執行一次的初始化工做適合在這個時候進行,如建立數值格式化對象,註冊通知,某些只須要調用一次的 API 等。
viewWillAppear()
每當視圖將要被顯示的時候會被調用。好比咱們剛剛選擇 Overview 標籤,每次切換它都會被調用。當數據發生變化,這是個更新到界面的好時候。
viewDidAppear()
每當視圖顯示在屏幕上的時候,這個方法會被調用。這時適合作一些動畫。
視圖控制器被建立以後,一些與用戶交互的事件就該登場了:
updateViewConstraints()
當佈局每次被改變都會被調用,好比窗口大小變化。
viewWillLayout()
是佈局將要發生的時候進行調用。若是你須要調整你的約束,能夠在這時進行。
viewDidLayout()
當佈局完成以後被調用。
當重載這三個方法的時候,在其中你必須調用他們的super
。
終止與建立對應:
viewWillDisappear()
當視圖將要消失的時候調用。在viewDidAppear()
開始的動畫這時能夠結束了。
viewDidDisappear()
視圖消失以後這個方法被調用。一切你不須要的東西均可以在這時被幹掉。好比已經無效的timer神馬的。
有關視圖控制器生命週期重要的事情都已經告訴你了,如今進行一個小測試。
問題:你想把用戶選擇的產品的時候,讓OverviewController
的視圖顯示正確的產品詳情。該在啥時候去執行更新視圖的代碼?
打開OverviewController.swift
,添加下面的代碼:
override func viewWillAppear() { updateUI() }
重載了viewWillAppear
,當用戶看到視圖以前,它會被正確更新。
數值格式化對象當前使用的是默認值,爲了更好的展現,最好把它配置成貨幣格式。viewDidLoad()
是作這事兒的好地方。
在OverviewController
的viewDidLoad()
方法添加下面的代碼:
numberformatter.numberStyle = .CurrencyStyle
用戶在主界面選擇不一樣的商品,當事件發生,咱們須要通知OverviewController
。在ViewController
類中作這件事很合適,由於用戶操做的彈出按鈕就在這上面。打開ViewController.swift
,添加下面的代碼:
private var products = [Product]() var selectedProduct: Product!
products
是用來保存全部商品信息的數組。selectedProduct
指向當前彈出按鈕所選擇的商品。
找到viewDidLoad()
,添加下面的代碼:
if let filePath = NSBundle.mainBundle().pathForResource("Products", ofType: "plist") { products = Product.productsList(filePath) }
加載本教程資源中包含全部商品信息的 plist,賦值給products
屬性。接下來用這個數組初始化彈出按鈕。
打開Main.storyboard
,選擇View Controller Scene
,切換到Assistant Editor
。確保ViewController.swift
被選擇,而後拖拽到ViewController.swift
做爲一個 outlet,命名爲productsButton
。確認類型爲NSopUpButton
。
返回ViewController.swift
,找到viewDidLoad
添加下面的代碼:
//1 productsButton.removeAllItems() //2 for product in products { productsButton.addItemWithTitle(product.title) } //3 selectedProduct = products[0] productsButton.selectItemAtIndex(0)
這段代碼作了一些微小的工做:
刪除彈出按鈕中全部的數據。
遍歷商品數組,將全部商品的標題添加到彈出按鈕。
選擇數組中第一個商品。
最後,咱們還須要在彈出按鈕選擇條目發生變化時作出響應,找到valueChanged(_:)
添加下面的代碼:
if let bookTitle = sender.selectedItem?.title, let index = products.indexOf({$0.title == bookTitle}) { selectedProduct = products[index] }
這段代嘗試根據彈出按鈕的標題在商品列表中查找對應的元素,而後把selectedProduct
指向正確的商品對象。
如今是時候來完成選擇商品發生變化,通知OverViewController
的功能了。先在ViewController
添加一個OverViewController
的引用:
private var overviewViewController: OverviewController!
當 ViewController 以嵌入的形式被加載的時候,prepareForSegue(_:, sender:)
方法會被觸發,咱們能夠在這個時候獲得overViewController
的實例:
override func prepareForSegue(segue: NSStoryboardSegue, sender: AnyObject?) { //1 let tabViewController = segue.destinationController as! NSTabViewController //2 for controller in tabViewController.childViewControllers { //3 if controller is OverviewController { overviewViewController = controller as! OverviewController overviewViewController.selectedProduct = selectedProduct } else { //More later } } }
獲得標籤視圖控制器的引用。
遍歷子視圖控制器。
找到OverviewController
,的實例,而後設置它的selectedProduct
屬性。
找到valueChanged(_:)
方法,在裏面的if let
塊中添加代碼。
overviewViewController.selectedProduct = selectedProduct
編譯運行,當選擇不一樣的商品時候,能夠看到界面已經能正常更新了。
咱們來建立產品詳情的視圖控制器。
選擇File\New\File...
,選擇OS X\Source\Cocoa Class
,點擊Next
。類名爲DetailViewController
,繼承自NSViewController
,不要勾選Also Create XIB for user interface
。點擊Next
保存。
打開Main.storyboard
,選擇Details Scene
。在Identity Inspector
中將class
改成DetailViewController
。
添加一個image view
到詳情視圖。選中它點擊Pin
按鈕建立約束。weight
和height
設置爲180
,top
約束設置爲standard value
。
點擊Align
按鈕,給視圖添加一個居中約束:Horizontally in the Container
。
在剛剛添加的圖像視圖下方添加一個標籤控件,設置字體bold
,字號19。點擊Pin
按鈕,添加約束:top
,leading
和trailing
,值爲standard value
。
在剛剛設定的標籤下方再添加一個標籤。點擊Pin
添加約束:top
,leading
和trailing
。值爲standard value
。
拖拽一個NSBox
在標籤下方。給它添加約束:top
、leading
、trailing
和bottom
,值爲standard value
。
打開Attributes Inspector
,設置字體爲bold
,字號14
。將title
改成Who is this Book For?
。
NSBox 用來組織一組相關聯的 UI 元素很好用。並且有了標題看起來更明確。
拖拽一個標籤控價在NSBox
裏面,選擇這個標籤控件,點擊Pin
按鈕,添加top
,leading
,trailing
和bottom
,所有設置爲standard value
。
而後更新你的界面,看起來是這樣的:
激活Assistant Editor
,打開DetailsViewController.swift
。添加四個IBOutlet
,命名爲:
productImageView for the NSImageView.
titleLabel for the label with the bold font.
descriptionLabel for the label below.
audienceLabel for the label in the NSBox.
在DetailviewController
添加如下代碼:
// 1 var selectedProduct: Product? { didSet { updateUI() } } // 2 override func viewWillAppear() { updateUI() } // 3 private func updateUI() { if viewLoaded { if let product = selectedProduct { productImageView.image = product.image titleLabel.stringValue = product.title descriptionLabel.stringValue = product.descriptionText audienceLabel.stringValue = product.audience } } }
這些代碼和Overview
視圖控制器裏面的代碼很相似,你應該已經很熟悉了:
定義表示當前選中商品的selectedProduct
屬性,當選擇其餘商品的時候更新視圖。
每次視圖被顯示的時候,強制刷新(好比切換選項卡會就會觸發)。
將商品信息顯示在視圖上的圖像視圖和標籤控件中(經過 updateUI)
當被選擇的商品發生變化,你須要從主視圖控制器通知商品詳情視圖控制器。打開ViewController.swift
,給商品詳情視圖控制器添加一個引用。在overviewViewController
屬性下面增長下面的代碼:
private var detailViewController: DetailViewController!
而後找到valueChanged(_:)
添加:
detailViewController.selectedProduct = selectedProduct
如今改變彈出按鈕的選項,詳情頁會被通知到。
最後一點改變是在prepareForSegue(_:, sender:)
。找到註釋//More later
,替換成下面的代碼:
detailViewController = controller as! DetailViewController detailViewController.selectedProduct = selectedProduct
當商品詳情被嵌入的時候,當前選擇的商品信息會正常加載。
你的應用程序已經完成!
你能從這裏下載完整的項目。
在本教程中,你學習瞭如下內容:
什麼是視圖控制器和其與窗口控制器的區別
建立一個自定義的試圖控制
鏈接控件到你的視圖控制器
操做視圖控制器
視圖控制器的生命週期以及回調事件
若是想看看視圖控制器裏面都有啥,請移步官方文檔:https://developer.apple.com/l...
另外仍是推薦看一眼 tutorial on windows and window controllers。
視圖控制器十分強大,並且在 OS X 應用程序開發中,它是十分有用的組件,涵蓋了許多值得學習的內容,加油!本文給開了個好頭,立刻去開發你想要的東東吧。
歡迎在下方留言進行討論。