36、打造智能小费计算器:Swift 实战指南

打造智能小费计算器: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. 未来扩展方向

小费计算器应用有很多可以扩展的方向,以下是一些建议:

  1. 多语言支持 :添加多语言支持,让不同地区的用户都能使用自己熟悉的语言。可以使用 iOS 的本地化功能,创建不同语言的字符串文件。
  2. 更多小费百分比选项 :除了 15% 和自定义百分比,添加更多预设的小费百分比选项,如 18%、20% 等。
  3. 历史记录功能 :记录用户的计算历史,方便用户查看和比较不同的账单和小费计算结果。可以使用 UserDefaults 或数据库来存储历史记录。
  4. 分享功能 :添加分享功能,让用户可以将计算结果分享到社交媒体或通过短信、邮件等方式发送给他人。
graph LR;
    A[当前应用] --> B[多语言支持];
    A --> C[更多小费百分比选项];
    A --> D[历史记录功能];
    A --> E[分享功能];
9. 总结与展望

通过这个小费计算器应用的开发,我们深入学习了 Swift 的面向对象编程、UI 设计、事件处理、精确计算和本地化等知识。在开发过程中,我们使用了 Interface Builder 进行可视化设计,通过 @IBOutlet 属性和动作方法实现了与 UI 组件的交互。同时,使用 NSDecimalNumber 进行精确的财务计算,利用 NSNumberFormatter 进行本地化处理。

未来,我们可以根据上述的扩展方向,进一步完善这个应用,使其功能更加丰富和实用。同时,通过不断优化代码,提高应用的性能和稳定性。希望这些内容能为你在 Swift 开发的道路上提供一些帮助和启示。

总之,Swift 是一门功能强大的编程语言,通过实践项目可以更好地掌握其特性和应用场景。不断学习和实践,相信你能开发出更多优秀的 iOS 应用。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值