编译自: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 应用程序开发中,它是十分有用的组件,涵盖了许多值得学习的内容,加油!本文给开了个好头,立刻去开发你想要的东东吧。
欢迎在下方留言进行讨论。