macOS 控件教程(2)

xiaoxiao2021-02-27  152

原文:macOS Controls Tutorial: Part 2/2 作者:Ernesto García 译者:kmyhy

更新说明:由 Ernesto García 升级至 Xcode 8.2/Swift 3。上一版本由 Michael Briscoe 升级至 Xcode 6.3/Swift 1.2。原文作者是 Ernesto García。

欢迎回到 macOS 控件教程第二部分!

在第一部分,我们创建了一个疯狂填词,用户输入各种单词和短语,创建一个搞笑的句子。

其中,我们学习了几个核心的 UI 控件:Text Field,组合框,弹出式按钮,下压按钮和 Text View。

在这一部分中,我们将完成我们的 app,学习使用这些控件:

SlidersDate PickersRadio ButtonsCheck BoxesImage Views

通过本教程,你将对 macOS 控件有一个深刻的理解。

本教程将从上一部分的工作继续。如果你没有完成前一部分的工作,请从这里下载在上一教程中已经完成的项目。

好了,开工吧!

滑来滑去 — NSSlider

Slider 控件允许用户在某个区间值中进行选择。Slider 有一个最小值和最大值,用户通过拖动滑块在两者间选择一个值。Slider 可能是条状的,也可能是圆形的。你也许会问,这二者有什么不同?

条状的 Slider 要么水平显示,要么垂直显示,你可以沿着滑轨移动滑块。你可以看 macOS 的鼠标偏好设置面板:

https://koenig-media.raywenderlich.com/uploads/2016/12/slider-mouse.png’ width=’300’/>

圆形 Slider 则不同——它就像一个带滑块的小圆环,你可以按旋 360 度。你可以将滑块到任意需要的位置。你可以看看 Adobe Photoshop,会用它来定义渐变色的角度:

https://koenig-media.raywenderlich.com/uploads/2012/12/slider-example-radial.png’ width=’270’/>

这个 macOS 控件就是 NSSlider。

这 3 种 slider(水平、垂直、圆形)实际上是同一个控件 NSSlider。它们只是外观不同而已。IB 的 Object Library 中每种 Slider 都会对应一个 Object:

https://koenig-media.raywenderlich.com/uploads/2014/09/sliders-ib.png’ width=’270’/>

Slider 介绍

使用 Slider 时通常包含两个操作:读取/设置当前值,读取/设置最大值和最小值。这些属性分别是:

// 读/写 int 值 let theInteger = mySlider.integerValue mySlider.integerValue = theInteger // 读/写 float 值 let theFloat = mySlider.floatValue mySlider.floatValue = theFloat // 读/写 double 值 let theDouble = mySlider.doubleValue mySlider.doubleValue = theDouble // 读/写最小值 let theMinimumValue = mySlider.minValue mySlider.minValue = theMinimumValue // 读/写最大值 let theMaximumValue = mySlider.maxValue mySlider.maxValue = theMaximumValue

这些代码没有太多值得注意的地方——如果你注意看,标准的 macOS UI 控件的使用方法都非常简单。接下来,我们将在 App 中添加一个 NSSlider!

选个数字,任何数字

打开 Main.storyboard。在 Object Library 中拖一个 Label 控件到 content 视图。如果窗口空间不够,你可以将窗口高度拉大,将 Go! 按钮往下挪。

双击 Label,编辑它的 text 为 Amount:[10]。拖一个 Horizontal Slider 控件到窗口,将它放在 Label 的右边。

选中这个 slider 控件。打开属性面板,将 Minium 设为 2,Maximum 设为 10,Current value 设为 5。当 app 一启动用户就会看到这个默认值。

确认选中 Continuous。这会允许 slider 值被改变的任意时候发出通知。

https://koenig-media.raywenderlich.com/uploads/2017/01/slider-add-1.png’ width=’700’/>

接下来我们需要创建两个 IBOutlet,一个连接 slider,一个用于连接 label。你可能会问——为什么要连接 label?

label 用于在 slider 值被改变后将当前值显示到 label 的文本中,这也是我们将 Continuous 属性设为 on 的意思。哈,这是不是更好玩?

打开 Assistant 编辑器,确认打开的是 ViewController.swift 文件。右键(ctrl+左键)从 label 托一条线到 ViewController.swift。

https://koenig-media.raywenderlich.com/uploads/2017/01/slider-drag-1.png’ width=’700’/>

在弹出对话框中,设置 name 为 amountLabel。

https://koenig-media.raywenderlich.com/uploads/2016/12/amountlabel-drag2.png’ width=’330’/>

在 slider 上进行同样的动作,IBOutlet 取名为 amountSlider。

现在我们需要为 slider 添加一个 IBAction,这样当 slider 值被改变时就会调用这个 IBAction。在前一教程中,我们为按钮创建过 Action 了,这样的操作和它是一样的,你可以自己完成这个动作吧?

选中 slider,右键(ctrl+左键)拖一条线到 ViewController.swift 的类定义中:

https://koenig-media.raywenderlich.com/uploads/2017/01/slider-action-drag.png’ width=’700’/>

在弹出窗口中,将 connection 设为 action 而不是 outlet。name 设为 sliderChanged:

https://koenig-media.raywenderlich.com/uploads/2016/12/slider-action2.png’ width=’330’/>

我们需要在 IBAction 中修改 label 文本。

打开 ViewController.swift 在 sliderChanged() 方法中:

let amount = amountSlider.integerValue amountLabel.stringValue = "Amount: [\(amount)]"

上述代码首先读取了 slider 的当前值。然后将 label 的 text 设为 slider 的当前值。注意,虽然用户无法从 UI 上修改 amountLabel 的文本,但我们可以通过代码来修改。

注意:这里,我们用 integerValue 获得的是一个整数值,如果你需要小数值,你可以用 floatValue 或者 doubleValue。

编译运行 app。将 slider 往左往右拖,你会看到 label 中的值会改变:

https://koenig-media.raywenderlich.com/uploads/2017/01/buildrun-slider.png’ width=’400’/>

还有一个问题。注意到没?label 在一开始的时候不会显示当前值。虽然不是什么大问题,但感觉总是不太完美。这是因为 label 只有在 slider 滑块被拖动后才会修改 text!

别急——这很容易搞定。

在 viewDidLoad 方法最后一句添加:

// Update the amount slider sliderChanged(self)

这样 app 一开始就会调用 sliderChanged() 方法,这样 label 上的 text 就会显示了。OK!

编译运行 app。label 现在一来就显示当前值了,这只是一个小问题,但却能让你的 app 更加完善。

还有没有更难搞定的值选择控件?比如日期或日历?答案当然是肯定的:]

今晚的约会——NSDatePicker

macOS 的日期选择器控件用于显示日期和时间,并允许用户编辑日期和时间值。日期选择器能够只显示日期,也能够只显示时间或者两者一起显示。这个控件就是 NSDatePicker。

日期选择器可以显示成两个样式:textual,日期和时间以 text field 方式呈现,以及 graphical,日期以日历方式呈现,而时间以时钟形式呈现。你可以在 macOS 的日期时间偏好设置中看到具体的例子:

https://koenig-media.raywenderlich.com/uploads/2016/12/date-macos.png’ width=’600’/>

我们使用日期选择器的最常见方式是读取/设置时间值,并限定它的最小/最大时间值。这些属性分别是:

// 读取 & 设置日期时间 let myDate = myDatePicker.dateValue myDatePicker.dateValue = myDate // 读取 & 设置最小日期 let theMinimumDate = myDatePicker.minDate myDatePicker.minDate = theMinimumDate // 读取 & 设置最大日期 let theMaximumDate = myDatePicker.maxDate myDatePicker.maxDate = theMaximumDate

这个控件提供了非常简单的 getter/setter 方式来访问这些属性。接下来让我们看看如何使用这个控件。

对不起,我迟到了

按照正常的步骤,将一个 Label 添加到窗口中。

将它的 title 修改为 Date,aligment 修改为 Right。在 Object Library 中,找到 Date Picker 控件,将它拖到窗口中,放到 Label 的右边。调整窗口的高度并将 Go! 按钮的位置往下挪一点:

https://koenig-media.raywenderlich.com/uploads/2017/01/add-datepicker.png’ width= ‘700’/>

为日期选择器创建一个 IBOutlet,和之前我们对每个 MacOS 控件所做的一样。在弹出的对话框中,设置 name 为 datePicker。

和之前对其他 macOS 控件所做的一样,当 app 一启动就显示一个默认的日期值。在这里我们设为当前日期。

打开 ViewController.swift ,在 viewDidLoad 方法最后一句添加:

// Set the date picker to display the current date datePicker.dateValue = Date()

运行 app。你会看到新添加的日期选择器显示为当前日期:

https://koenig-media.raywenderlich.com/uploads/2017/01/buildrun-datepicker.png’ width=’400’/>

录影带杀死了广播…按钮注:*《录影带杀死了广播歌星》,上世纪70末的一首流行歌曲

单选按钮是一种特殊的控件,它们总是成批出现,用于显示一个选项列表,每个选项都是一个可选择的按钮。它们的行为很特别,在群组中的任何一颗按钮都可以被选择,但在选择一颗按钮的同时其它按钮会被反选。在一个按钮组中,一次只能选中一颗按钮。

例如 iTunes 备份选项:

https://koenig-media.raywenderlich.com/uploads/2012/12/radio-example.png’ width=’300’/>

一个单选按钮组则由多个单选按钮组成。

当我们点击单选按钮组中的某个单选按钮时,这颗按钮会被选中,而其它按钮自动被反选。我们唯一要做的就是读取/设置正确的值。简单吧?

如何定义一个单选按钮组?这是文档中的介绍:

将多个单选按钮放到同一个 superview 中,并连接到同一个 -action 方法时,就会自动组成一个单选按钮组。

因此,我们需要将所有单选按钮添加到同一个 UIView 中并创建一个 action 方法,将所有按钮的 action 设为这个 action 方法。

我们只要改变某个按钮的状态(On/Off),其它按钮会自动反选。

// 选中单选按钮 radioButton.state = NSOnState // 反选单选按钮 radioButton.state = NSOffState // 判断单选按钮的状态是否为已选中 let selected = (radioButton.state == NSOnState)

这样,一个复杂控件的使用简化到聊聊几个方法。后面,我们将看看如何在我们的 app 中使用单选按钮!

家的港湾注:*澳大利亚电视剧名 – 添加单选按钮

在 app 中添加一个 Label(应该很熟练了吧?!),将 title 修改为 Place:。在 Object Library 中拖 3 个 Radio Button 到 content 视图,放在 Label 右边。双击 3 个单选按钮,分别设置 title 为: RWDevCon, 360iDev 和 WWDC。

https://koenig-media.raywenderlich.com/uploads/2017/01/radioadd.png’ width=’700’/>

然后,为每个按钮创建一个 IBOutlet——这个操作也很熟练了吧?!打开助手编辑器,ctrl+左键(右键)从第一个按钮拖一条线到 ViewController.swift 的其它属性声明之下。设置 name 为 rwDevConRadioButton。在另外 2 个单选按钮上重复上述步骤 ,分别命名为 IBOutlet 为 threeSixtyRadioButton 和 wwdcRadioButton 。

运行 app。

https://koenig-media.raywenderlich.com/uploads/2017/01/buildrun-radio-error.png’ width= ‘300’/>

点击单选按钮,我们立即发现了问题。3 个按钮都可以被选中。它们还没组成单选按钮组。为什么?因为它们不能同时满足这两个条件:必须位于同一个 superview(这个条件),必须拥有同一个 action。第二个条件不满足。

要解决这个问题,我们必须先创建一个 action。

在 IB 中创建 action 你不陌生吧?所以这次我们换一种方式。我们将用代码创建 action,然后在 IB 中将 action 分配给每个单选按钮。

打开 ViewController.swift 在类实现的最后添加:

@IBAction func radioButtonChanged(_ sender: AnyObject) { }

这就是一个典型的 IBAction 方法,方法用 @IBAction 修饰,这样 IB 就能找到并使用这个方法。然后打开 Main.storyBoard,将这个 action 分配给按钮。

在 Document Outline 视图中,右键(ctrl+左键)点击 View Controller。当对话框弹出时,从 Received Actions 下面将 radioButtonChanged: 旁的小圆圈拖到 RWDevCon 按钮。通过这种简化的方式,我们将这个单选按钮的 action 设置为指定的 action。

https://koenig-media.raywenderlich.com/uploads/2017/01/radio-addaction.png’ width=’500’/>

重复这个过程,将另两个按钮的 action 设置为同一个 action。最终 Received action 列表会变成这个样子:

https://koenig-media.raywenderlich.com/uploads/2016/12/radio-actions-all.png’ width=’300’/>

运行 app:

https://koenig-media.raywenderlich.com/uploads/2017/01/buildrun-radio.png’ width=’400’/>

现在,单选按钮终于有点单选按钮组的意思了。

当 app 已启动,我们要让 RWDevCon 按钮默认选中。我们只需要将这个按钮的 state 设置为 On 即可,同时其它按钮会自动反选。

打开 ViewController.swift 在 viewDidLoad() 最后一句添加:

// 设置按钮组的默认选项 rwDevConRadioButton.state = NSOnState

运行 app。你会看到 app 已启动就选中了 RWDevCon 选项。

https://koenig-media.raywenderlich.com/uploads/2016/12/radio-rwdev.png’ width=’300’/>

单选按钮是一种在多个值中切换的方法,但我们还有另一个 MacOS 控件拥有接近的功能——多选框!

Ticking all the Boxes注:*歌曲名 — 勾选框

勾选框常用语显示一些类似布尔值的状态。这些状态会用于设置 app 的某些功能开启或禁用。

只要在用户用来开启/关闭某个功能的地方,你都可以看到勾选框。在设置程序的每个窗口中你都可以找到它。例如,在节能器窗口,你会用它们来打开/关闭不同的节能选项。

https://koenig-media.raywenderlich.com/uploads/2017/01/check-example.png’ width=’400’/>

勾选框的使用非常简单;大部分时候你只需要用到(读取/设置)控件的 state 属性。勾选框的 state 可以是三者之一: NSOnState (开启), NSOffState (禁用) 以及 NSMixedState (部分开启)。 这是用法:

// 设为 On myCheckBox.state = NSOnState // 设为 Off myCheckBox.state = NSOffState // 读取 state 状态 let state = myCheckBox.state

超级简单!接下来在我们的 app 中使用它吧。

勾选勾选再勾选 – 使用勾选框

打开 Main.storyboard,在 Object Library 找到 Check Box Button ,将它拖到 content 视图。双击它将 title 设置为 Yell!!

https://koenig-media.raywenderlich.com/uploads/2017/01/check-add.png’ width= ‘600’/>

为它创建一个 IBOutlet 并命名为 yellCheck。关于如何创建 IBOutlet 就不需要再说了吧?

当 app 一启动我们就让勾选框默认设置为 off 状态。在 viewDidLoad() 最后一句添加:

// set check button state yellCheck.state = NSOffState

运行 app!你会看到勾选框,它的状态是未勾选状态。点击它试试:

https://koenig-media.raywenderlich.com/uploads/2017/01/buildrun-check.png’ width=’400’/>

选择,选择 – NSSegmentedControl

一个 Segment 控件,用 NSSegmentedControl 类表示,当我们需要从多个选项中进行选择时可以用 Segment 控件代替单选按钮。你可以参考 Xcode 的属性面板:

https://koenig-media.raywenderlich.com/uploads/2016/12/segmented-alignment.png’ width=’300’/>

它的使用也非常简单。只需要 get/set 它的 selectedSegment 属性(这个属性代表了用户的选择)即可:

// Select the first segment segmentedControl.selectedSegment = 0 // Get the selected segment let selected = segmentedControl.selectedSegment

调整语速——添加 Segment 控件

你还记得吗,readSentence() 方法有一个参数用于控制语速(正常、快、慢)。我们可以用 segment 控件来修改语速。

打开 Main.storyboard ,在 content 视图中添加一个 Label。修改它的 title 为Voice Speed:。在 Object Library 中拖一个 Segmented Control 到 content 视图。我们可以双击控件的每一段来修改它们的 title。分别修改为 Slow、Normal 和 Fast。

https://koenig-media.raywenderlich.com/uploads/2017/01/segmented-add-1.png’ width=’700’/>

在 ViewController 中为这个 Segment 控件创建 IBOutlet,取名为 voiceSegmentedControl。当 app 一启动,我们要让 Normal 变成选中,也就是第一个选项(选项引从 0 开始)。打开 ViewController.swift 在 viewDidLoad() 最后加上:

// 设置 segment 控件默认选项 voiceSegmentedControl.selectedSegment = 1

没法再简单了。只需要将 selectedSegment 属性设为 1 就行。运行 app,你会看到 Normal 选项自动选中。

https://koenig-media.raywenderlich.com/uploads/2017/01/buildrun-selected.png’ width=’400’/>

好了!我们已经把创建填词金句时所需要用到的控件都添加完了。我们所缺的不过是把每个控件的值取出来拼成句子的!

拼出句子

我们需要另外两个控件来显示结果:一个 Label——用于显示句子,一个 Image View 用于显示图片,图片会让 UI 看起来更精致!

打开 Main.storyboard。在 Object Libray 中拖一个 label 到窗口中,放在 Go! 按钮下面。在属性面板中修改 label 的 Border 属性为边框(即四个按钮中的第一个),这样控件看起来会更显目。

然后双击 label,选中它的 text 然后删除。

https://koenig-media.raywenderlich.com/uploads/2017/01/resulttext-add.png’ width=’700’/>

然后我们要为新的 Label 创建 IBOutlet,以便用它来显示我们的短笑话!和之前一样,右键(ctrl+左键)从 label 拖一条线到 ViewController.swift 中,取名为resultTextField。

暂时把这个控件放到以便,接下来我们要编写代码了。

让视图饱满 — NSImageView

一个 Image View 是一个简单易用的控件——不奇怪——它只是用来显示图片而已。你想到了吗?

在运行时,你只需要关心这几个属性:

// 读取 image view 的图片 let myImage = myImageView.image // 设置 image view 的图片 myImageView.image = myImage

设计时,需要配置它的外观:边框、缩放比例和对齐方式。当然,这些属性也可以通过代码设置,当使用 IB 会更简单:

https://koenig-media.raywenderlich.com/uploads/2016/12/imageview-props.png’ width=’300’/>

Just a Pretty Face注:*专辑名 – 显示 Image view

让我们来在 app 中添加一张图片!在 Object Library 中拖一个 Image view 到视图中,放到 wrapping label 的左边。如果有必要,请修改窗口大小。

https://koenig-media.raywenderlich.com/uploads/2017/01/imagewell-add.png’ width=’700’/>

使用和前面相同的方法,为 image view 创建新的 IBOutlet:右键(ctrl+左键)从 image view 拖一条线到 ViewController.swift,取名为 imageView。

运行 app。效果如下:

https://koenig-media.raywenderlich.com/uploads/2017/01/buildrun-uifinished-386x500.png’ width=’400’/>

嘘!我们的 UI 部分终于完成了——接下来该编写拼接句子的代码并显示图片了!

是时候来搞定它了!

我们需要根据输入拼接出句子。

当用户点击 Go! 按钮,我们需要从各种控件中读取数据并将它们拼接出完整的句子,然后在 wrapping label 中显示出来!

除了文字之外,我们还要显示一张图片。

请下载本项目要用到的资源文件(默认情况下会下到你的“下载”文件夹中)。如果 zip 文件没有解压缩,请解压缩,我们会拿到 face.png 文件。在项目导航器中选择 Assets.xcassets,将图片文件拖到 assets 列表中。

https://koenig-media.raywenderlich.com/uploads/2016/12/assets.png’ width=’700’/>

最后,添加代码——用于拼接出 Mad Lib 金句。

打开 ViewController.swift 在类实现的最后添加:

fileprivate var selectedPlace: String { var place = "home" if rwDevConRadioButton.state == NSOnState { place = "RWDevCon" } else if threeSixtyRadioButton.state == NSOnState { place = "360iDev" } else if wwdcRadioButton.state == NSOnState { place = "WWDC" } return place }

这里添加了一个计算属性,用于根据单选按钮组中的选择返回一个地名。

然后将 goButtonClicked() 中代码替换为:

// 1 let pastTenseVerb = pastTenseVerbTextField.stringValue // 2 let singularNoun = singularNounCombo.stringValue // 3 let amount = amountSlider.integerValue // 4 let pluralNoun = pluralNouns[pluralNounPopup.indexOfSelectedItem] // 5 let phrase = phraseTextView.string ?? "" // 6 let dateFormatter = DateFormatter() dateFormatter.dateStyle = .long let date = dateFormatter.string(from: datePicker.dateValue) // 7 var voice = "said" if yellCheck.state == NSOnState { voice = "yelled" } // 8 let sentence = "On \(date), at \(selectedPlace) a \(singularNoun) \(pastTenseVerb) \(amount) \(pluralNoun) and \(voice), \(phrase)" // 9 resultTextField.stringValue = sentence imageView.image = NSImage(named: "face") // 10 let selectedSegment = voiceSegmentedControl.selectedSegment let voiceRate = VoiceRate(rawValue: selectedSegment) ?? .normal readSentence(sentence, rate: voiceRate)

看起来有点多,当分开后其实也很简单:

读取 pastTenseVerbTextField 的 text。这里,通过 stringValue 属性读取组合框的字符串值。你可能会问为什么不找出选中行,并读取这一行的字符串?很简单,因为用户可以输入自己的字符串啊!所以我们用 stringValue 去获取组合框的值,这样无论是用户输入的还是选中的都可以拿到。用 integerValue 方法读取 slider 的当前值。记住,如果需要更精确的值,请使用 floatValue 和 doubleValue。这里读取弹出按钮中用户选择的复数名词。怎么做呢?从 pluarlNouns 数组中通过下表和 indexOfSelectedItem 属性来读取正确的复数名词。接下来是用户输入的短语。只需要访问 text view 的 string 属性就可以了。这里用了一个合并空操作,因为 string 属性是可空的,有可能是空值。要获得日期,用到了日期选择器的 dateValue 方法。然后,用一个 NSDateFormatter 将日期转换成人类可读的字符。是念出来还是吼出来?我们需要判断勾选框的状态:如果是 NSOnState,将 yelled 放到字符串变量,否则保持默认的 said。将搜集来的所有信息构造出我们的填词造句!这是奇迹发生的地方。返回结果中使用了字符串插入,各个值由控件中用户输入的值填充。将我们计算的结果显示出来!首先用 stringValue 属性在结果标签中显示句子。然后显示一张图片美化我们的 app,简单地加载一张图片,设置给 image view 的 image 属性。最后,大声念出句子!语速使用 segment 控件进行选择,然后调用文本转语音方法。

大功告成!运行 app,尝试创造自己的幽默断句吧!

https://koenig-media.raywenderlich.com/uploads/2017/01/buildrun-final.png’ width=’330’/>

恭喜你——你已经完成了你的填词造句 app, 在这个过程中你学习了一系列常见 macOS 控件!

请尝试修改这些控件的值,设置不同的值,输入好玩的名词和动词,看看你点击 Go! 之后是什么结果!看看你写出的短笑话好不好笑!:]

结束

在这里下载最终完成的项目。

为了进一步学习 macOS 控件,我建议你阅读下面列出的苹果文档,它们包含了许多关于控件使用的有用的资料。

我强烈建议阅读其中的macOS 人机界面指南。在这篇指南中,介绍了 macOS UI 概念和理论,苹果建议开发者如何使用这些控件构建UI并提供一致和愉悦的体验。每个 mac 平台的开发者都应该好好读一读它,尤其是那些需要在 Mac 商店中发布引用的开发者。

这里列出一些链接,巩固和加深你对本教程的理解:

macOS Human Interface GuidelinesButton Programming TopicsAttributed String Programming GuideCombo Box Programming TopicsSegmented control Programming TopicsApplication Menu and PopUp Programming TopicsImage Kit Programming Guide

希望你喜欢本教程,有任何建议和问题,请在下面留言。

转载请注明原文地址: https://www.6miu.com/read-13481.html

最新回复(0)