原文地址 翻译:DeveloperLx
更新日志: 已由Ernesto García更新至 Xcode 8.2 / Swift 3 版。 之前 由 Michael Briscoe 更新至Xcode 6.3 / Swift 1.2。 初始版本 作者为 Ernesto García 。
如果你是一个iOS开发者,并且你感兴趣学习Mac开发,你很幸运 - 由于你的iOS的技能,你会发现它非常易学!
很多你了解并喜欢Cocoa的类和设计模式,例如字符型、字典和代理在Mac开发中都是直接提供的。你会感到非常舒服!
然而,一个和macOS开发很大的区别是它们存在不同的控件。消失的是
UIButton
和
UITextField
- 用来取代的是相似(但有轻微不同的)变量。
这篇教程将向你介绍一些用户交互中,更常用的macOS控件 - 它们是构建多数Mac app的基础。你将学习这些控件的方法和property,作为一个开发者,为了建造和运行起来,你需要去理解它们!:]
在这篇教程中,你会创建一个简单的Mac应用,就像流行的游戏 Mad Libs 。Mad Libs是一个单词游戏,你可以在一个文本块中,插入不同的单词,来创建一个故事 - 这常常会造成非常滑稽的结果!
一旦你完成了这篇教程中的两个部分,你就会拥有对下列macOS控件的基本的理解:
- Labels和Text Fields
- Combo Boxes
- Popup Buttons
- Text Views
- Sliders
- Date Pickers
- Buttons
- Radio Buttons
- Check Buttons
- Image Views
学习任何新的编程平台的最好的方式就是钻入并入门 - 所以不要担心得太多,这里就是你学习macOS控件的向导!:]
打开 Xcode ,并选择 File/New/Project 。在 Choose a template 对话框中,选择 macOS/Application/Cocoa ,它是你用来创建带有GUI的macOS app的模板。然后点击 Next 。
在下一屏中,输入 MadLibs 作为产品名称,然后输入唯一的组织名称和标识符。确保 Use Storyboards 被选中,选中 Swift 作为语言。
单击 Next 并选择你想要保存你的新项目的位置。点击 Create 。
打开 Main.storyboard 。 Xcode 已为你创建了一个macOS app的基本的骨架:一个Window controller及一个content View controller。
选择 Window Controller Scene 中的window并打开 Attributes Inspector 。将window的 Title 改为 MadLibs 。
macOS app通常会带有一个可以改变大小的窗口,因此它的内容就不得不适应于窗口的尺寸。对此最有用的工具就是 自动布局 。添加自动布局到这篇教程中全部控件 会造成很大的分神;我们想要你把注意力完全集中到macOS控件上。
因此,我们只会应用默认的autoresizing,它意味着全部的控件都会保持一个规定的位置和尺寸,无论窗口大小如何改变,用户的平台有何不同 —— 包括潜在的一些控件会全部或部分地超出窗口的课件范围。
注意: 如果你想要了解更多关于自动布局,以及如何在你的macOS app中使用,你可以跟随我们的 macOS开发初学者教程,第三部分 。
在这篇教程中,你需要为这个view添加一些macOS控件。默认的高度可能无法盛得下。如果你需要调整尺寸,向下拖拽content view的底部边缘,或在 Size Inspector 中设置view的 Height property。
运行项目。
你已经构建了一个可工作的应用 - 没有一句代码。窗口现在是空的,但马上你就会用一些macOS的控件来填充它,让它看起来更棒!:]
既然基本的框架已搭完,你就可以转移注意力到这篇教程的主题上了 - 添加macOS控件到你的app上。
这篇教程中剩余的每一步骤都会聚焦在一个单独的、不同的控件上。你会了解到每个控件的基础,和如何在MadLibs app中使用它们来造出东西来。
NSControl
是所有其它的macOS控件构建的基础。
NSControl
提供了三个对于用户交互非常重要的特性:绘制到屏幕上,相应用户事件,发送动作消息。
NSControl
是一个抽象的基类,非常有可能从不需要在你的app中直接使用它,除非你想要定制你自己的macOS控件。所有的常用控件都是
NSControl
的子类,因此就继承了定义在NSControl中的property和方法。
一个control最常用的方法是获取和设置它的值,就像打开和禁用这个控件本身一样。下面来看一下这些方法背后的细节:
如果你需要展示信息,通常就要改变控件的值。根据你的需要,这个值可能是一个字符串,一个数字,甚至是一个对象。在大多数环境下,你需要一个能够匹配被展示的信息的类型的值,但
NSControl
允许你超越这个,并设置几个不同类型的值!
获取和设置一个控件的值的方法有:
// getting & setting a string
let myString = myControl.stringValue
myControl.stringValue = myString
// getting & setting an integer
let myInteger = myControl.integerValue
myControl.integerValue = myInteger
// getting & setting a float
let myFloat = myControl.floatValue
myControl.floatValue = myFloat
// getting & setting a double
let myDouble = myControl.doubleValue
myControl.doubleValue = myDouble
// getting & setting an object
let myObject: Any? = myControl.objectValue
myControl.objectValue = myObject
你可以看到不同的setter和getter是如何适应Swift的类型安全特性的。
基于一个app的状态启用或禁用macOS控件,是一个非常常见的UI任务。当一个控件被禁用时,它就不会响应鼠标或键盘的事件,并且通常会更新它的图形表示,来提供一些被禁用的视觉提示,例如用较轻的灰色来绘制本身。
启用和禁用的一个控件的方法有:
// disable a control
myControl.isEnabled = false
// enable a control
myControl.isEnabled = true
// get a control's enabled state
let isEnabled = myControl.isEnabled
OK,看起来相当得容易 - 很棒的事是这些方法对于所有的macOS空间都是通用的。对于任何你在你的UI中使用的控件来说,它们都以相同的方式工作。
现在是时候去看一看更通用的macOS控件了。
在任何UI中,最常用的控件之一,是一个能够用来展示或编辑文本的域。在macOS中负责这个功能的控件就是 NSTextField 了。
NSTextField
是用来展示或编辑文本的。你会注意到它和iOS的差别:在iOS中,
UILabel
是用来展示固定的文本的,
UITextField
是用来编辑文本的。在macOS中它们被合二为一,它的行为会根据
isEditable
这个property的值变化而变化。
如果你想要text field成为一个label,你可以设置它的
isEditable
property为
false
。要让它的行为像一个text field - 是的,你就可以设置
isEditable
为
true
!你可以从代码或从Interface Builder中改变这个property。
为了让你编写代码更容易一些,Interface Builder事实上提供了几个预先配置好的 基于
NSTextField
的macOS控件,来展示和编辑文本。这些可以在
Object Library
中找到的预配置的macOS控件有:
现在你已经了解了有关
NSTextField
的基础,你可以添加它到你爹Mad Libs应用中了!:]
你会添加各种macOS控件到MadLibs app上,让你可以“盲目地”构建一个有趣的句子。一旦你完成后,你就会把各部分连起来,并展示结果,很有可能就是一些喜剧的结果。你越有创造了,它们就会越有趣!
你要添加的第一个控件是一个text field,可以在这里输入一个动词来添加到句子中。以及一个表示这个text field是为什么的label。
打开 Main.storyboard 。定位到 Object Library 中的 Label 控件,并将其拖拽到 View Controller Scene 的view上。双击label来编辑默认的文本,并将其改为 Past Tense Verb: 。
接下来,找到 Text Field 控件并将它拖拽到view上,放置到label的右边,就像这样:
现在,你要在view controller中创建这个text field的outlet。打开 Main.storyboard ,前往 Assistant editor 。确保 ViewController.swift 被选中,并从storyboard的text field中拖拽到 按住Ctrl拖拽到 包含 ViewController.swift 的窗格中,然后在类定义的下面放开鼠标,来创建一个新的property:
在出现的下拉菜单中,将Outlet命名为 pastTenseVerbTextField ,并单击 Connect 。
就是这样!现在你已经在你的view controller中拥有了一个
NSTextField
的property,并且它已经连接到了在主窗口的text field上。
你知道,在app启动时显示一些默认的文本,来提示该往文本框中输入什么是非常棒的。由于每个人都喜欢吃,关于Mad Libs的实物总是最有趣的, ate 这个词在这里就会成为一个很棒的选择。
放置这个的一个很好的地方就在
viewDidLoad()
中。现在设置你先前学过的
stringValue
property。
打开
ViewController.swift
,并添加下列的代码到
viewDidLoad()
方法末尾:
// Sets the default text for the pastTenseVerbTextField property
pastTenseVerbTextField.stringValue = "ate"
运行项目。
OK,它负责了一个默认值的输入。但如果你想要提供一个值得列表来方便选择呢?
Combo Boxes可以解救你!
combo box是非常得有趣且便利的 - 它让用户可以从一个选项的数组中选择一个值,就如同输入它们自己的文本一样。
它看起来就类似于一个用户可以自由输入的text field,但还包含了一个让用户可以展示一个可选项目列表的按钮。你可以在macOS的“日期时间”偏好面板中找到一个示例:
这里,用户可以从一个预定义的列表中进行选择,或输入他们自己的服务器名称,如果他们希望的话。
在macOS中负责这个的是 NSComboBox 。
NSComboBox
有两个不同的成分:你可以进行输入的text field,还有当嵌入的按钮被点击时展示的选项的列表。你可以分别地控制两部分的数据。
要获取或设置text field中的值,只需使用前面提到的
stringValue
property。为保持事情简单一致喝彩吧!:]
为列表提供选项会有一点复杂,但仍是相对直接了当的。你可以直接调用控件上的方法来添加元素,就类似于可变数组一样,也可以使用data source - 任何有iOS编程经验的人都会感到非常舒适!
注意: 如果你并不熟悉Data Sources的相关概念,你可以在苹果的 文档 中进行了解。
NSComboBox
包含了一个内部的项目列表,并暴露了几个允许你操纵这个列表的方法,例如:
// Add an object to the list
myComboBox.addItem(withObjectValue: anObject)
// Add an array of objects to the list
myComboBox.addItems(withObjectValues: [objectOne, objectTwo, objectThree])
// Remove all objects from the list
myComboBox.removeAllItems()
// Remove an object from the list at a specific index
myComboBox.removeItem(at: 2)
// Get the index of the currently selected object
let selectedIndex = myComboBox.indexOfSelectedItem
// Select an object at a specific index
myComboBox.selectItem(at: 1)
这个相对比较直接,但如果你不想在你的app中选择硬编码 - 例如一个储存在app外部的动态列表的话?这时候使用datasource就会显得相当便利!:]
当使用data source时,combo box会查询数据源,来查找需要展示的项目,以及任何必须的元数据,例如将要在列表中展示的数据的项目。为了获取这个信息,你需要在你的一个类中实现 NSComboBoxDataSource 协议,通常是由View Controller来托管控件。从这里,需要两个步骤来配置combo box使用数据源。
首先,设置控件的
usesDataSource
property为
true
。然后设置控件的
dataSource
property,传递一个实现了这个协议的类的实例;当实现data source的类是托管的view controller时,配置这个的很好的一个地方就是
viewDidLoad()
这里了,然后你就可以设置
dataSource
property为
self
,就像下面这样:
class ViewController: NSViewController, NSComboBoxDataSource {
.....
override func viewDidLoad() {
super.viewDidLoad()
myComboBox.usesDataSource = true
myComboBox.dataSource = self
}
.....
}
注意:
上面这些指令的顺序是非常重要的。尝试当
useDataSource
为
false(这是默认的值)
时,设置
dataSource
property会导致失败,你的data source将不会被用到。
为了遵守这个协议,你需要实现来自这个协议中的两个方法:
// Returns the number of items that the data source manages for the combo box
func numberOfItems(in comboBox: NSComboBox) -> Int {
// anArray is an Array variable containing the objects
return anArray.count
}
// Returns the object that corresponds to the item at the specified index in the combo box
func comboBox(_ comboBox: NSComboBox, objectValueForItemAt index: Int) -> Any? {
return anArray[index]
}
最后,无论什么时候你的数据源发生变化,为了更新这个控件,只需调用combo box上的
reloadData()
方法。
如果你的项目列表相对较小,并且你并不需要经常改变它,一次性地添加到内部列表中可能就是最佳的选择了。但如果你的项目列表很大,或者是动态的,使用data source来进行处理就会显得更有效率。在这篇教程中,我们将使用方法1。
既然你已经掌握了combo box的基础,就马上到你的app中实现一个吧!:]
在这个部分,你将添加一个combo box来输入一个名词。你可以选择从列表中输入,或者自己来输入。
首先,添加一个label来描述控件的目的。
打开 Main.storyboard 。找到 Object Library 面板中的 Label 控件,并拖拽它到content view上。改变它的alignment值为 Right ,它的标题为 Singular Noun: 。
注意: 作为替代的捷径,按住 Option 键并拖拽一个存在的label来复制它。这么做是非常方便的,你可以保持和已存在label相同的尺寸和property值。
找到 Combo Box 控件并将它拖拽到content view上,将它放置到label的右边。
现在你需要添加一个
NSComboBox
outlet到view controller上。使用你在text field上相同的技术:选择
Assistant Editor
(确保
ViewController.swift
已选中)并
按住Ctrl拖拽
Ctrl-Drag
combo box到
ViewController
类上,就在
NSTextField
的下方:
在出现的窗口中,命名outlet为 singularNounCombo 。
现在
NSComboBox
property就被连接到了combo box控件上。接下来添加一些数据来填充这个列表。
打开 ViewController.swift 并添加下面的代码到outlets的下方:
fileprivate let singularNouns = ["dog", "muppet", "ninja", "pirate", "dev" ]
现在,添加下列的代码到 viewDidLoad() 方法的尾部:
// Setup the combo box with singular nouns
singularNounCombo.removeAllItems()
singularNounCombo.addItems(withObjectValues: singularNouns)
singularNounCombo.selectItem(at: singularNouns.count-1)
第一行代码移除了所有默认添加的项目。接下来,它使用
addItems()
方法添加来自
singularNouns
的名称到了combo box中。然后,它选择了列表中的最后一个项目。
运行app来查看你的combo box的动作!
Great - 看起来似乎一切正常。如果你点击combo box,你就可以在接下来查看并选择任一其它的项目。
现在,如果你想要展示一个选择的列表,但不允许你自己输入,该怎么办呢?继续阅读,还有一个控件!
pop up button让用户可以从一个选项的列表中进行选择,但不允许用户在控件中输入它们自己的值。在macOS中负责这项工作的控件是 NSPopupButton 。
Pop up button在macOS中非常常用,你可以在几乎每个应用中找到它们 - 包含你正在使用的这个:Xcode!:] 你正在使用pop up button来设置很多你在这篇教程中使用的macOS控件上的property,就像下面的截图中一样:
正如你所期望的,添加项目到
NSPopUpButton
就类似于添加项目到
NSComboBox
上 - 除了
NSPopUpButton
不支持使用data source来为这个控件填充内容。
NSPopUpButton
持有着一个内部的项目的列表,并暴露了几个方法来操纵它:
// Add an item to the list
myPopUpbutton.addItem(withTitle: "Pop up buttons rock")
// Add an array of items to the list
myPopUpbutton.addItems(withTitles: ["Item 1", "Item 2", "Item 3"])
// Remove all items from the list
myPopUpbutton.removeAllItems()
// Get the index of the currently selected item
let selectedIndex = myPopUpbutton.indexOfSelectedItem
// Select an item at a specific index
myPopUpbutton.selectItem(at: 1)
相当的直截了当,不是么?这就是macOS控件的美 - 用来操纵控件的方法会有很多的相似之处。
是时候在你的app中实现一个pop up button了!:]
现在你将添加一个pop up button到你的Mad Libs app上,去选择不同的复数名词来填充你的滑稽的句子。
打开 Main.storyboard 。拖拽一个label到 Singular Noun label的下面。
改变对齐方式为 靠右 ,title为 Plural Noun: 。接下来,找到 Pop Up Button 控件并把它拖到窗口上,把它放到label的右边。
这个content view应当看起来像这样:
现在你需要添加一个outlet到popup button上,现在你应该对此相当熟悉了:打开
Assistant editor
,确认
ViewController.swift
已被选中,然后
按住Ctrl拖拽
pop up button到
ViewController
类上来创建一个新的outlet:
在弹出的窗口中,将outlet命名为 pluralNounPopup :
现在你只需要添加一些数据来填充控件了!
打开 ViewController.swift 并添加下面的property到类的实现中。
fileprivate let pluralNouns = ["tacos", "rainbows", "iPhones", "gold coins"]
现在,添加下面的代码到
viewDidLoad()
的底部:
// Setup the pop up button with plural nouns
pluralNounPopup.removeAllItems()
pluralNounPopup.addItems(withTitles: pluralNouns)
pluralNounPopup.selectItem(at: 0)
第一行代码移除了所有pop up button中已存的项目。第二行代码则使用
addItems()
方法添加了名词的数组。最后,选中列表中的第一个项目。
运行app查看结果:
当app启动时,注意到pop up button会展示初始的项目, tacos ,如果你点击pop up button,你就会看到在列表中的其它项目。
OK,现在你已经有了两个让用户可以从列表中进行选择的macOS控件,以及一个让用户可以输入单行文本的控件。但如果你需要在text field中输入不仅仅几个单词,该怎么办呢?
继续阅读来学习text view!
Text view和text field不同,通常是作为展示富文本的控件。它甚至会允许一些更高级的特性,诸如展示行内的图像。
在macOS中,负责这个的控件是 NSTextView 。
一个全部使用由
NSTextView
提供的特性的应用的例子就是TextEdit了:
NSTextView
的特性非常的丰富,如果要覆盖所有的特性,可能就得单独得出一个对于它的教程。因此这里你只会看到一些能够让你的app运行起来的基本的特性。(你是不是松了一口气?):]
这里有一些你需要在text views上使用的基本方法:
// Get the text from a text view
let text = myTextView.string
// Set the text of a text view
myTextView.string = "Text views rock too!"
// Set the background color of a text view
myTextView.backgroundColor = NSColor.white
// Set the text color of a text view
myTextView.textColor = NSColor.black
相对地简单 - 这里没有太让人震惊的事。
NSTextView
也內建了对
NSAttributedString
的支持。如果你向text view传递了一个attributed string,这个string就会使用所有诸如字体,字体大小,字体颜色等恰当的属性正确地展示出来。
注意:
attributed string是特殊类型的string,你可以将string的子集标记为不同的属性 - 诸如字体,它的颜色,是否加粗,等等。要了解全部的attributed strings,请访问我们的
TextKit教程
。这是一个iOS教程,但有关
NSAttributedString
的信息也可以用到Mac开发上面。
看起来,你已经拥有了所有添加一个text view到你的Mad Libs app所需的知识!这个text view将允许用户输入一个多行的短语,用在最终渲染的Mad Lib上面。
打开 Main.storyboard 并拖拽一个label到 Plural Noun label(或复制一个已存在的label,就像之前提到的一样)的下面。将对齐方式改为 Right ,标题设置为 Phrase: 。
接下来,找到 Text View 控件并把它拖到窗口上,放到你刚创建的label的旁边。
你的窗口现在看起来应该是这样的:
现在,如果你尝试改变view的大小,并让它变得更高,你会注意到一些相当特别的事。text view会独自移动,在你改变窗口大小的时候改变它的位置。
这是因为默认情况下,Xcode会为text view添加Auto resizing的约束,因此它会在父view的大小改变的时候改变它自身的位置。由于你想让text view的位置保证固定,你就需要禁用一些约束。
从Document Outline中选择 Bordered Scroll View – Text View 并前往 Size Inspector 。
在 In the AutoResizing 部分,你会看到一个带有四条连接到父view的红线的矩形。每一个红色的“连接器”都代表了一个Auto resizing的约束。你只需要点击 右边 和 下面 的红色连接器来禁用它们,红色的实现就会变成带有褪色红色的虚线,就像下面这样:
现在,即使你改变了窗口的大小,text view仍会保持它的位置和对齐方式。
下面,添加一个
NSTextView
outlet到view controller上。选择textview,打开
Assistant editor
并确保
ViewController.swift
已选中。
按住Ctrl拖拽
text view到
ViewController
类中已存在的outlet的下面。
重点: Text view内部包含了Text view。在创建outlet前,确保你选中的是text view。要做到这个,只需在text view上单击三次,或在 Document Outline 中选中它。
在弹出的窗口中,确定类型为
NSTextView
,并命名outlet为
phraseTextView
。
现在,添加下列的代码到 viewDidLoad() 的尾部:
// Setup the default text to display in the text view
phraseTextView.string = "Me coding Mac Apps!!!"
运行app,查看结果:
太棒了!Mad Libs app现在开始真正地成型了。
Button是当用户点击它时,向app发送一个消息的macOS控件。
在macOS中,负责这个的控件是 NSButton 。
有很多不同风格的button(你可以在Interface Builder的Object Library中查看它们)。它们都以相同的方式工作,唯一的区别只是外观不同。
你应当使用最匹配你的应用设计风格的button - 引用自 macOS人机交互指南 中,关于你的app的最佳设计实践的建议和指导。
一般地,当使用button的时候,你只需要关联一个动作到button上,并设置它的标题。然而,你可能需要禁用按钮,或改变它的外观。下面的方法让你可以执行这些操作:
// disable a button
myButton.isEnabled = false
// enable a button
myButton.isEnabled = true
// getting & setting a button's title
let theTitle = myButton.title
myButton.title = theTitle
// getting & setting a button's image
let theImage = myButton.image
myButton.image = theImage
看起来相当地简单 - 在下一部分添加一个按钮到你的app上应当是相当容易的。
打开 Main.storyboard 。在Object Library的面板中找到 Push Button ,并将它拖拽到content view上。双击它来改变它的标题为 Go! :
这次你不需要为button创建outlet。然而,你确实需要创建一个动作并将它关联到button上,这样你的app就知道button是何时被点击的了!:]
打开
Assistant Editor
并
按住Ctrl拖拽
button到
ViewController
的实现中。
在出现的窗口中,确保connection被设置为 Action 。将action命名为 goButtonClicked 。
当用户点击按钮时,方法
goButtonClicked()
就会被调用。现在,你只是在这里添加一个debug代码,来确保一切工作正常。
打开
ViewController.swift
并添加下列的代码到
goButtonClicked()
中:
let pastTenseVerb = pastTenseVerbTextField.stringValue
let singularNoun = singularNounCombo.stringValue
let pluralNoun = pluralNouns[pluralNounPopup.indexOfSelectedItem]
let phrase = phraseTextView.string ?? ""
let madLibSentence = "A (singularNoun) (pastTenseVerb) (pluralNoun) and said, (phrase)!"
print("(madLibSentence)")
上面的代码从text field、combo box、popup button,以及text view获取了字符串,并构成了Mad Lib的句子。
注意text view
string
这个property事实上是可选的,因此它可以是
nil
。要避免这种情况,你可以使用nil coalescing操作符
??
因此如果
string
是nil,你就会得到
""
来代替。
这就是到目前为止你所需的全部代码 - 运行你的app。
每次当你单击按钮的时候,你银弹看到一个又短又傻的句子出现在Xcode的控制台中。
A dev ate tacos and said: Me coding Mac Apps!!!!
非常棒,但怎样可以让它变得更加有趣?
让你的电脑来阅读句子怎么样?Steve Jobs让第一台Macintosh在它的发布会上向世界说hello。你也可以让你的电脑说话...让我们来吧!
打开
ViewController.swift
并添加下列的代码到
ViewController
的实现中:
// 1
fileprivate enum VoiceRate: Int {
case slow
case normal
case fast
var speed: Float {
switch self {
case .slow:
return 60
case .normal:
return 175;
case .fast:
return 360;
}
}
}
//2
fileprivate let synth = NSSpeechSynthesizer()
首先,你声明了一个
enum
来代表声速。然后你创建了一个
NSSpeechSynthesizer
的实例,它会将文本朗读出来。
现在,添加这个代码到
ViewController
的实现中:
fileprivate func readSentence(_ sentence: String, rate: VoiceRate ) {
synth.rate = rate.speed
synth.stopSpeaking()
synth.startSpeaking(sentence)
}
该方法启动
synth
对象来以被选中的速度朗读一个字符串。
是时候来调用它了!添加下列代码到
goButtonClicked()
方法的尾部来朗读Mad Libs的句子:
readSentence(madLibSentence, rate: .normal)
运行项目;点击 Go! ,听你的Mac大声朗读出你的句子!
你可以下载 最终的项目 ,它包含了教程到目前为止所提到内容的全部的源码。
在这个教程的 第二部分 ,你会了解到更多关于macOS控件的知识,包括sliders,date pickers, radio buttons,check boxes以及image views - 每个控件都会添加到你的Mad Lib app上来完成它。