打造智能小费计算器:Swift 实战指南
1. 创建动作方法
在创建好出口(outlets)之后,需要创建能够响应用户界面事件的动作(即事件处理程序)。以文本字段(Text Field)为例,每当用户更改其内容时,就会触发“Editing Changed”事件。如果将一个动作连接到文本字段的这个事件上,那么每次该事件发生时,文本字段都会向视图控制器对象发送一条消息,以执行相应的动作。同样,对于滑块(Slider),当用户移动滑块上的游标时,会反复触发“Value Changed”事件。若将一个动作方法连接到滑块的这个事件上,每次事件发生时,滑块都会向视图控制器发送消息以执行该动作。
要创建一个能够响应上述两种事件的动作方法,可按以下步骤操作:
1. 在场景中,按住“Control”键并从文本字段拖至
ViewController.swift
文件中第 25 行和第 26 行的右花括号(
}
)之间,然后释放鼠标。此时会弹出一个用于配置出口的弹出框,从弹出框的“Connection”列表中选择“Action”,以显示配置动作的选项。
2. 在弹出框中,为动作的“Name”指定
calculateTip
,为“Event”选择“Editing Changed”,然后点击“Connect”。Xcode 会在代码中插入以下空方法定义:
@IBAction func calculateTip(sender: AnyObject) {
}
同时,在该方法左侧的灰色边栏中会显示一个小靶心符号,表示该动作已连接到一个 UI 组件。现在,当用户编辑文本字段时,会向
ViewController
对象发送消息以执行
calculateTip
方法。
要将滑块连接到
calculateTip
方法,以处理滑块的“Value Changed”事件,可在场景中选择滑块,按住“Control”键并从滑块拖至
calculateTip
方法,然后释放鼠标,这样就完成了滑块的“Value Changed”事件与该动作的连接。
2.
ViewController
类详解
ViewController.swift
文件包含了
ViewController
类以及一些全局实用函数,这些函数用于将
NSDecimalNumber
格式化为货币形式,并使用
NSDecimalNumber
对象进行计算。
2.1 导入声明
要使用 iOS 8 框架的功能,必须将其导入到 Swift 代码中。在这个应用中,使用了
UIKit
框架的 UI 组件类。导入声明必须出现在源代码文件中除注释之外的任何其他 Swift 代码之前,示例代码如下:
// ViewController.swift
// Implements the tip calculator’s logic
import UIKit
2.2
ViewController
类定义
ViewController
类定义如下:
class ViewController: UIViewController {
这里的
: UIViewController
表示
ViewController
类继承自
UIViewController
类,
UIViewController
是
UIKit
框架中所有视图控制器的超类。这意味着
ViewController
是
UIViewController
的一种,它具备 iOS 对所有视图控制器所期望的基本功能,例如
viewDidLoad
方法,该方法有助于 iOS 管理视图控制器的生命周期。
2.3
ViewController
的
@IBOutlet
属性
ViewController
类有九个
@IBOutlet
属性声明,这些是在之前创建出口时由 Interface Builder 生成的,示例代码如下:
// properties for programmatically interacting with UI components
@IBOutlet weak var billAmountLabel: UILabel!
@IBOutlet weak var customTipPercentLabel1: UILabel!
@IBOutlet weak var customTipPercentageSlider: UISlider!
@IBOutlet weak var customTipPercentLabel2: UILabel!
@IBOutlet weak var tip15Label: UILabel!
@IBOutlet weak var total15Label: UILabel!
@IBOutlet weak var tipCustomLabel: UILabel!
@IBOutlet weak var totalCustomLabel: UILabel!
@IBOutlet weak var inputTextField: UITextField!
@IBOutlet
表示该属性引用了应用故事板中的一个 UI 组件。当场景加载时,会创建 UI 组件对象、对应的视图控制器类对象,并建立视图控制器的出口属性与 UI 组件之间的连接,连接信息存储在故事板中。这些
@IBOutlet
属性被声明为弱引用,因为视图控制器并不拥有这些 UI 组件,而是由故事板定义的视图拥有。
属性声明中的类型注解使用了感叹号(
!
),表示这是一个隐式解包可选类型,此类变量默认初始化为
nil
。这样做是为了让类能够编译,因为这些
@IBOutlet
属性会在运行时 UI 创建后被分配实际的 UI 组件对象。
2.4 其他
ViewController
属性
在
@IBOutlet
属性下方,定义了其他属性,示例代码如下:
// NSDecimalNumber constants used in the calculateTip method
let decimal100 = NSDecimalNumber(string: "100.0")
let decimal15Percent = NSDecimalNumber(string: "0.15")
decimal100
常量用于计算自定义小费百分比,通过将滑块的值除以 100 得到;同时也用于将用户输入除以 100,以便在应用顶部显示的账单金额中正确放置小数点。
decimal15Percent
常量用于计算 15% 的小费。
2.5 重写的
UIViewController
方法
viewDidLoad
viewDidLoad
方法是从超类
UIViewController
继承而来的,通常会重写该方法以定义只有在视图初始化后才能执行的任务。以下是重写后的
viewDidLoad
方法示例:
// called when the view loads
override func viewDidLoad() {
super.viewDidLoad()
// select inputTextField so keypad displays when the view loads
inputTextField.becomeFirstResponder()
}
在重写超类方法时,需要在
func
关键字前加上
override
关键字,并且方法体的第一条语句通常使用
super
关键字来调用超类的该方法。在这个应用中,希望在应用开始执行时,
inputTextField
成为选中对象,以便立即显示数字键盘。通过调用
inputTextField
的
becomeFirstResponder
方法,可使
inputTextField
成为屏幕上的活动组件,就像用户触摸了它一样。
3.
ViewController
动作方法
calculateTip
calculateTip
方法是一个动作方法(由
@IBAction
指定),用于响应文本字段的“Editing Changed”事件和滑块的“Value Changed”事件。以下是该方法的完整代码:
// called when the user edits the text in the inputTextField
// or moves the customTipPercentageSlider’s thumb
@IBAction func calculateTip(sender: AnyObject) {
let inputString = inputTextField.text // get user input
// convert slider value to an NSDecimalNumber
let sliderValue =
NSDecimalNumber(integer: Int(customTipPercentageSlider.value))
// divide sliderValue by decimal100 (100.0) to get tip %
let customPercent = sliderValue / decimal100
// did customTipPercentageSlider generate the event?
if sender is UISlider {
// thumb moved so update the Labels with new custom percent
customTipPercentLabel1.text =
NSNumberFormatter.localizedStringFromNumber(customPercent,
numberStyle: NSNumberFormatterStyle.PercentStyle)
customTipPercentLabel2.text = customTipPercentLabel1.text
}
// if there is a bill amount, calculate tips and totals
if !inputString.isEmpty {
// convert to NSDecimalNumber and insert decimal point
let billAmount =
NSDecimalNumber(string: inputString) / decimal100
// did inputTextField generate the event?
if sender is UITextField {
// update billAmountLabel with currency-formatted total
billAmountLabel.text = " " + formatAsCurrency(billAmount)
// calculate and display the 15% tip and total
let fifteenTip = billAmount * decimal15Percent
tip15Label.text = formatAsCurrency(fifteenTip)
total15Label.text =
formatAsCurrency(billAmount + fifteenTip)
}
// calculate custom tip and display custom tip and total
let customTip = billAmount * customPercent
tipCustomLabel.text = formatAsCurrency(customTip)
totalCustomLabel.text =
formatAsCurrency(billAmount + customTip)
}
else { // clear all Labels
billAmountLabel.text = ""
tip15Label.text = ""
total15Label.text = ""
tipCustomLabel.text = ""
totalCustomLabel.text = ""
}
}
该方法接收一个参数
sender
,其类型为
AnyObject
,表示任何类型的对象。在运行时需要确定该对象的具体类型,因为不同类型的对象都可能生成事件。在响应多个 UI 组件事件的动作方法中,通常会使用
sender
来确定用户与哪个 UI 组件进行了交互。
以下是该方法的主要步骤:
1. 获取
inputTextField
和
customTipPercentageSlider
的当前值:
- 将
inputTextField
的
text
属性值存储在本地
String
变量
inputString
中。
- 获取
customTipPercentageSlider
的
value
属性值,将其转换为整数后用于初始化
NSDecimalNumber
对象
sliderValue
。
- 使用重载的除法运算符将
sliderValue
除以 100(
decimal100
),得到自定义小费百分比。
2. 当滑块值改变时,更新自定义小费百分比标签:
- 通过判断
sender
是否为
UISlider
对象,确定用户是否与滑块进行了交互。
- 使用
NSNumberFormatter
类的
localizedStringFromNumber
方法将自定义百分比格式化为本地化的百分比字符串,并更新相应的标签。
3. 更新小费和总计标签:
- 检查
inputString
是否为空,若不为空,则进行小费和总计的计算并更新相应的标签;若为空,则清空所有标签。
graph TD;
A[开始] --> B[获取输入和滑块值];
B --> C{滑块值改变?};
C -- 是 --> D[更新自定义百分比标签];
C -- 否 --> E{输入不为空?};
D --> E;
E -- 是 --> F{输入框触发事件?};
F -- 是 --> G[更新账单金额标签和 15% 小费及总计];
F -- 否 --> H[计算并显示自定义小费和总计];
G --> H;
E -- 否 --> I[清空所有标签];
H --> J[结束];
I --> J;
4. 全局实用函数
在
ViewController.swift
文件中,还定义了几个全局实用函数,用于格式化货币和重载运算符:
// convert a numeric value to localized currency string
func formatAsCurrency(number: NSNumber) -> String {
return NSNumberFormatter.localizedStringFromNumber(
number, numberStyle: NSNumberFormatterStyle.CurrencyStyle)
}
// overloaded + operator to add NSDecimalNumbers
func +(left: NSDecimalNumber, right: NSDecimalNumber) -> NSDecimalNumber {
return left.decimalNumberByAdding(right)
}
// overloaded * operator to multiply NSDecimalNumbers
func *(left: NSDecimalNumber, right: NSDecimalNumber) -> NSDecimalNumber {
return left.decimalNumberByMultiplyingBy(right)
}
// overloaded / operator to divide NSDecimalNumbers
func /(left: NSDecimalNumber, right: NSDecimalNumber) -> NSDecimalNumber {
return left.decimalNumberByDividingBy(right)
}
-
formatAsCurrency函数用于将一个数值格式化为本地化的货币字符串。 -
重载的
+、*和/运算符函数分别用于对NSDecimalNumber对象进行加法、乘法和除法运算。
通过这些函数,可以更方便地进行
NSDecimalNumber
对象的计算和格式化。
5. 总结
这个小费计算器应用展示了如何使用 Swift 的面向对象编程能力,包括对象、类、继承、方法和属性。同时,还涉及了各种 Swift 数据类型、运算符、控制语句和关键字的使用。通过 Interface Builder 可以直观地设计应用的 UI,并生成
@IBOutlet
属性和动作方法。使用
NSDecimalNumber
进行精确的财务计算,并利用
NSNumberFormatter
创建本地化的货币和百分比字符串。通过重载运算符,简化了
NSDecimalNumber
的计算过程。希望这些内容能帮助你更好地理解和掌握 Swift 开发。
打造智能小费计算器:Swift 实战指南
6. 应用功能总结与技术要点回顾
这个小费计算器应用具备计算并显示 15% 和自定义百分比小费及餐厅账单总计的功能。以下是应用中使用到的关键技术和功能点总结:
| 技术要点 | 说明 |
|---|---|
| 面向对象编程 |
使用了 Swift 的对象、类、继承、方法和属性等面向对象编程特性。例如,
ViewController
类继承自
UIViewController
类,继承了视图控制器的基本功能。
|
| 数据类型与运算符 |
运用了各种 Swift 数据类型,如
String
、
NSDecimalNumber
等,还使用了重载运算符对
NSDecimalNumber
进行计算。
|
| 控制语句 |
通过
if
语句等控制语句实现条件判断,如判断用户输入是否为空、事件的发送者是文本框还是滑块等。
|
| Interface Builder |
用于可视化设计应用的 UI,生成
@IBOutlet
属性和动作方法,方便与 UI 组件进行交互。
|
| 精确计算 |
使用
NSDecimalNumber
进行精确的财务计算,避免了浮点数计算可能带来的精度问题。
|
| 本地化处理 |
利用
NSNumberFormatter
类创建本地化的货币和百分比字符串,提高了应用的国际化程度。
|
7. 代码优化建议
虽然当前的小费计算器应用已经实现了基本功能,但仍有一些可以优化的地方,以下是一些建议:
7.1 错误处理
在用户输入非数字字符时,当前应用可能会出现异常。可以添加输入验证逻辑,确保用户输入的是有效的数字。
@IBAction func calculateTip(sender: AnyObject) {
let inputString = inputTextField.text
if let billAmount = Double(inputString) {
// 继续进行计算
let billDecimal = NSDecimalNumber(string: inputString) / decimal100
// 后续计算代码...
} else {
// 提示用户输入无效
let alert = UIAlertController(title: "输入无效", message: "请输入有效的数字", preferredStyle: .alert)
let okAction = UIAlertAction(title: "确定", style: .default, handler: nil)
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
}
7.2 代码复用
可以将一些重复的代码提取成独立的函数,提高代码的复用性。例如,将更新标签的代码封装成一个函数:
func updateLabel(label: UILabel, withValue value: NSDecimalNumber) {
label.text = formatAsCurrency(value)
}
@IBAction func calculateTip(sender: AnyObject) {
// ...
if !inputString.isEmpty {
let billAmount = NSDecimalNumber(string: inputString) / decimal100
if sender is UITextField {
billAmountLabel.text = " " + formatAsCurrency(billAmount)
let fifteenTip = billAmount * decimal15Percent
updateLabel(label: tip15Label, withValue: fifteenTip)
updateLabel(label: total15Label, withValue: billAmount + fifteenTip)
}
let customTip = billAmount * customPercent
updateLabel(label: tipCustomLabel, withValue: customTip)
updateLabel(label: totalCustomLabel, withValue: billAmount + customTip)
} else {
// 清空标签
let labels = [billAmountLabel, tip15Label, total15Label, tipCustomLabel, totalCustomLabel]
for label in labels {
label?.text = ""
}
}
}
7.3 性能优化
在进行大量计算时,可以考虑使用更高效的算法和数据结构。例如,对于
NSDecimalNumber
的计算,可以使用更优化的初始化方式和运算方法。
8. 未来扩展方向
小费计算器应用有很多可以扩展的方向,以下是一些建议:
- 多语言支持 :添加多语言支持,让不同地区的用户都能使用自己熟悉的语言。可以使用 iOS 的本地化功能,创建不同语言的字符串文件。
- 更多小费百分比选项 :除了 15% 和自定义百分比,添加更多预设的小费百分比选项,如 18%、20% 等。
-
历史记录功能
:记录用户的计算历史,方便用户查看和比较不同的账单和小费计算结果。可以使用
UserDefaults或数据库来存储历史记录。 - 分享功能 :添加分享功能,让用户可以将计算结果分享到社交媒体或通过短信、邮件等方式发送给他人。
graph LR;
A[当前应用] --> B[多语言支持];
A --> C[更多小费百分比选项];
A --> D[历史记录功能];
A --> E[分享功能];
9. 总结与展望
通过这个小费计算器应用的开发,我们深入学习了 Swift 的面向对象编程、UI 设计、事件处理、精确计算和本地化等知识。在开发过程中,我们使用了 Interface Builder 进行可视化设计,通过
@IBOutlet
属性和动作方法实现了与 UI 组件的交互。同时,使用
NSDecimalNumber
进行精确的财务计算,利用
NSNumberFormatter
进行本地化处理。
未来,我们可以根据上述的扩展方向,进一步完善这个应用,使其功能更加丰富和实用。同时,通过不断优化代码,提高应用的性能和稳定性。希望这些内容能为你在 Swift 开发的道路上提供一些帮助和启示。
总之,Swift 是一门功能强大的编程语言,通过实践项目可以更好地掌握其特性和应用场景。不断学习和实践,相信你能开发出更多优秀的 iOS 应用。
超级会员免费看
9

被折叠的 条评论
为什么被折叠?



