资讯专栏INFORMATION COLUMN

OS X View Controller 指南

gaara / 1846人阅读

摘要:务必各种指出错误。添加四个约束,将其设置为。打开快捷键确认已经被打开。然后显示正确的值。比如视图从被加载,或者显示在屏幕上这种都属于被的事件范围。所有这些机遇事件的方法被统称为。比如已经无效的神马的。

编译自:https://www.raywenderlich.com...

(本文原文十分值得一读,然而我翻译的略渣,有些直译不出来的,是我根据理解编的。务必各种指出错误。基于此,暂时请勿转载)

苹果公司的开发框架一直围绕着 Modal-View-Controller,提供了多种控制器对象用于管理 UI,以便于我们的代码,易于理解,便于维护。

视图控制器是 OS X 程序中一个极其重要的概念,它是 Modal 层和 View 层之间的桥梁。

本文探讨的内容较多,包括使用视图控制器构建你的应用程序、视图控制器重要的回调事件以及与窗口控制器的比较。

开始之前,你需要装好最新版的 OS X 和 Xcode。你要开发的可不是一个闹着玩的应用程序!推荐你先读读 Garbriel Miro’s 的窗口和窗口视图指南,但这不是必须的。

视图控制器介绍

视图控制器用于管理视图以及视图的子视图。 OS X 中,继承自 NSViewController

OS X 10.5 引入了视图控制器,那时它不是 responder chain 的一部分。这有什么影响呢?举例来说,视图控制器上面有一个按钮,然而它却不能处理按钮的事件,很尴尬吧。OS X 10.10 改进了视图控制器,从那以后,它成为一个构建复杂界面时十分有用的工具。

有了视图控制器,可以很好的规划你的窗口。视图控制器专注与视图有关的交互和事件,像调整窗口大小、关闭窗口这种与窗口有关的事件只放在窗口控制器中处理。于是代码就变的很干爽。

使用视图控制器额外的好处是易于复用。比如你有一个文件浏览器,左边部分的文件浏览视图是通过视图控制器来实现的,这时你恰好需要一个类似的视图,你能很容易的复用它。剩下的时间和精力陪女朋友逛逛街也好啊。

视图控制器和窗口控制器

那么,啥时候使用窗口控制器,啥时候使用视图控制器呢?

如果你期待的视图控制器工作行为是 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.plistProduct.swift拖到项目导航中。确保勾选了Copy items if needed

这时编译运行应用程序。

可以看到空白的主窗口,但不要惊慌,正常运行就是好的开始。

创建用户界面

打开Main.storyboard,选择View Controller Scene,拖拽一个pop-up 按钮到 view 中,之后会用到。

通过 AutoLayout 来设置 它的位置。选择刚刚拖拽的 Pop-up 按钮,点击下方的Pin按钮。在弹出来的窗口中,将其LeadingTrailingTop的约束值都设置为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 方式连接的视图控制器,我们需要自定义,选择它并删除。

Tab View Controllers

现在,我们将添加一个视图控制器,用于显示 Product 信息:我们选择Tab View Controller。它的视图包含几个选项卡,以及视图控制器。每一个选项卡对应一个视图控制器。选项卡切换的时候,对应的视图控制器被切换显示。

选择Tab View Controller,拖拽到Storyboard中。

将刚刚添加的Tab View ControllerContainer View使用embed方式连接起来:

双击左侧的选项卡,将标题改为Overview;双击右侧的选项卡,将标题改为Details

编译并运行应用程序。

可以看到,刚刚我们设计的视图控制器已经能正常显示了,点击选项卡也能正常切换对应的视图控制器。因为我们还没有为其添加内容,所以两个视图控制器现在都还是空白。

Over View Controller

接下来需要创建这个。

FileNewFile,选择OS XSourceCocoa Class,点击Next。类名为OverviewController,继承自NSViewController,不要勾选Also Create XIB for user interface,点击Next创建完成并保存。

回到Main.storyboard,选择Overview Scene。点击视图上蓝色的按钮,选择类对象,在右侧的Identity Inspector的 class 输入框中输入 OverviewController。

拖拽三个 labelOverviewController 的视图的左上方,一个接一个的排列。添加一个image view在视图的右上角。

提示:默认情况下,image view 没有边框,给它设置个图片,这样好找。选择Attributes Inspector,选择gamesImage字段。这个图片是刚刚资源文件里的,应该可以看到效果啦。

选择最最上面的标签。Attributes Inspector里面将字体设置为System Bold,字号设置为 19。

这时候的视图看起来是这样的:

好!让我们使用 AutoLayout 来调整一下布局。

选择 image view,点击下面的Pin按钮。给其添加约束:top 和 trailing设置为 standard valuewidthheight的值设置为 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选成了NSImageCellNSTextField选成了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()是做这事儿的好地方。

OverviewControllerviewDidLoad()方法添加下面的代码:

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

编译运行,当选择不同的商品时候,可以看到界面已经能正常更新了。

产品详情视图控制器

我们来创建产品详情的视图控制器。

选择FileNewFile...,选择OS XSourceCocoa Class,点击Next。类名为DetailViewController,继承自NSViewController,不要勾选Also Create XIB for user interface。点击Next保存。

打开Main.storyboard,选择Details Scene。在Identity Inspector中将class改为DetailViewController

添加一个image view到详情视图。选中它点击Pin按钮创建约束。weightheight设置为180top约束设置为standard value

点击Align按钮,给视图添加一个居中约束:Horizontally in the Container

在刚刚添加的图像视图下方添加一个标签控件,设置字体bold,字号19。点击Pin按钮,添加约束:topleadingtrailing,值为standard value

在刚刚设定的标签下方再添加一个标签。点击Pin添加约束:topleadingtrailing。值为standard value

拖拽一个NSBox在标签下方。给它添加约束:topleadingtrailingbottom,值为standard value

打开Attributes Inspector,设置字体为bold,字号14。将title改为Who is this Book For?

NSBox 用来组织一组相关联的 UI 元素很好用。而且有了标题看起来更明确。

拖拽一个标签控价在NSBox里面,选择这个标签控件,点击Pin按钮,添加topleadingtrailingbottom,全部设置为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 应用程序开发中,它是十分有用的组件,涵盖了许多值得学习的内容,加油!本文给开了个好头,马上去开发你想要的东东吧。

欢迎在下方留言进行讨论。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/16368.html

相关文章

  • phalcon简易指南

    摘要:帮助你开始使用的简易指南。第一种方式参考第二种方式参考使用参考简单粗暴的理解是把下的对应成数据库的表,类属性对应表字段。 帮助你开始使用 phalcon 的简易指南。 简介 Phalcon 2将于2015年4月17日发布,这个版本大约85%的代码是基于 Zephir 语言重写的。Zephir是开源的,使用类似PHP语法的语言,生成C语言代码,并编译成PHP扩展。这提高了PHP扩展的开发...

    whataa 评论0 收藏0
  • ViewController编程指南

    摘要:是内部结构的基础。由于在中扮演的重要作用,的角色处于中心位置。用户交互属于对象,能够处理传递下来的事件。尽管如此,极少直接处理。通常由来处理自己的事件,并将结果报告给代理的一个方法或对象通常是这个。资源管理主要是内存资源适配自动调整尺寸 View Controller是App内部结构的基础。每个App至少含有一个View Controller,大多数会含有多个。每个View Contr...

    hizengzeng 评论0 收藏0
  • 当 Python 邂逅 POV-Ray

    摘要:本文介绍了使用为编写代码生成器的基本思路。所实现的代码生成器重视的建模功能,而忽视其光线追踪渲染功能。至于点的尺寸,可将其视为包围盒外接球空间的最小长度单位,并使之与包围盒外接球半径成固定比例。 showImg(https://segmentfault.com/img/bVX1x8?w=600&h=450); 引言 POV-Ray 是一种专业的三维场景描述语言,它描述的三维场景可交由 ...

    summerpxy 评论0 收藏0
  • 2011斯坦福 iOS 应用开发第一课

    摘要:顶层为,我们开发应用时大部分都是在这层上进行。它们之间通过进行通信。还有一个重要的事是,并不是它显示的数据的所有者,即不拥有数据。注意数据源的永远是或设置的第三方,但不可能是。的工作是把的数据传递给,响应所有的。 这节课主要讲了两个东西: 1. iOS 概述 2. MVC 讲解 iOS 概述 showImg(http://segmentfault.com/img/bVbEkN); ...

    megatron 评论0 收藏0
  • RubyMotion 指南:API 驱动开发示例

    摘要:当请求发出后,我们临时禁用。我们要自定义的构造函数。这个的视图有两部分一个,用来显示颜色标记,一个显示具体颜色和添加新的标记。不嘴炮了,看看代码当重载一个构造函数的时候,你需要做两件事调用它的父构造函数在函数结尾的时候返回初始化过的它自己。 翻译:@shiweifu 本文链接:http://segmentfault.com/blog/shiweifu 原文链接:http://rubym...

    CoderBear 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<