百日学 Swift(Day 19) – Challenge day(挑战:长度单位转换)
根据教程要求,独立编写了长度单位转换,引入了本地数据以简化代码。将两个长度单位分别用 json 文件保存。利用结构体读取数据,形成数组。
1. 数据模型 Units.swift
import Foundation
struct LengthUnits: Hashable, Codable, Identifiable { // 混装,备用
var id: Int
var category: String
var items: [LengthUnit]
}
struct LengthUnit: Hashable, Codable, Identifiable { // 某个制式的内容
var id: Int // 为 0 的是基准单位
var name: String // 名称
var coefficient: Double // 与基准单位的转换系数(倍数)
}
2. json 数据文件
json 文件保存在 Resources 文件夹,是加载读取的默认路径。
分别将两种制式存入不同文档,这样既方便读取,又便于今后扩展某个制式。
(1)公制单位:MetricUnits.json
[
{
"id" : 0,
"name" : "毫米",
"coefficient" : 1.0
},
{
"id" : 1,
"name" : "厘米",
"coefficient" : 10.0
},
{
"id" : 2,
"name" : "米",
"coefficient" : 1000.0
},
{
"id" : 3,
"name" : "千米",
"coefficient" : 1000000.0
}
]
(2)英制单位:EnglishUnits.json
[
{
"id" : 0,
"name" : "英寸",
"coefficient" : 1.0
},
{
"id" : 1,
"name" : "英尺",
"coefficient" : 12.0
},
{
"id" : 2,
"name" : "码",
"coefficient" : 36.0
},
{
"id" : 3,
"name" : "英里",
"coefficient" : 60120.0
}
]
(3)混装 AllUnits.json
[
{
"id": 0,
"category": "Metric",
"items": [
{
"id" : 0,
"name" : "毫米",
"coefficient" : 1.0
},
{
"id" : 1,
"name" : "厘米",
"coefficient" : 10.0
},
{
"id" : 2,
"name" : "米",
"coefficient" : 1000.0
},
{
"id" : 3,
"name" : "千米",
"coefficient" : 1000000.0
}
]
},
{
"id": 1,
"category": "English",
"items": [
{
"id" : 0,
"name" : "英寸",
"coefficient" : 1.0
},
{
"id" : 1,
"name" : "英尺",
"coefficient" : 12.0
},
{
"id" : 2,
"name" : "码",
"coefficient" : 36.0
},
{
"id" : 3,
"name" : "英里",
"coefficient" : 60120.0
}
]
}
]
3. 加载数据 Data.swift
与数据模型一道保存在 Models 中,便于管理。
应用运行后,会加载全局变量,分别保存了不同制式的数组供视图使用。
import Foundation
import UIKit
import SwiftUI
import CoreLocation
let MetricUnits: [LengthUnit] = load("MetricUnits.json")
let EnglishUnits: [LengthUnit] = load("EnglishUnits.json")
let AllUnits: [LengthUnits] = load("AllUnits.json")
let temp = 0
func load<T: Decodable>(_ filename: String) -> T {
let data: Data
guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
else {
fatalError("Couldn't find \(filename) in main bundle.")
}
do {
data = try Data(contentsOf: file)
} catch {
fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
}
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
}
}
4. 视图文件 ConvertUnit.swift
主要练习了以下几个方面:
- 全局变量加载本地 json 文件
- 表单视图的排列,Section的应用
- 表单控件的使用,包括 TextField、Picker及其修饰器(键盘的选择、Picker的样式)
- 巩固练习了容器组件 VStack、HStack 和 NavigationView。
- 巩固练习了 Button、Text 等视图组件及其修饰器的使用。
- 巩固练习了状态的使用和双向绑定。
import SwiftUI
struct ContentView: View {
@State private var isMetricToEnglish = true
@State private var inputString = ""
@State private var inIndex = 0
@State private var outIndex = 0
var inArray:[LengthUnit] { return isMetricToEnglish ? MetricUnits : EnglishUnits }
var outArray:[LengthUnit] { return !isMetricToEnglish ? MetricUnits : EnglishUnits }
var result: Double {
var outNumber: Double = 0
let inCoe = inArray[inIndex].coefficient
let outCoe = outArray[outIndex].coefficient
let inNumber = Double(inputString) ?? 0
let rate = isMetricToEnglish ? 1/25.4 : 25.4 // 两种制式基准单位的转换
outNumber = inNumber * inCoe * rate / outCoe
return outNumber
}
var body: some View {
NavigationView{
VStack{
Button(action: {self.isMetricToEnglish.toggle()}){
Text(isMetricToEnglish ? "公制 → 英制" : "英制 → 公制")
.font(.headline)
}
.frame(width: 120, height: 40)
.foregroundColor(.white)
.background(Color(
#colorLiteral(red: 0.2392156869, // 颜色字面量
green: 0.6745098233,
blue: 0.9686274529,
alpha: 1)))
.cornerRadius(CGFloat(10.0))
.shadow(radius: CGFloat(15.0))
Form{
Section(header: Text("将 \(isMetricToEnglish ? "公制" : "英制")")
.font(.subheadline).bold()
){
TextField("输入\(isMetricToEnglish ? "公制" : "英制")长度", text: $inputString)
.frame(maxWidth: .infinity)
.keyboardType(.decimalPad)
Picker("", selection: $inIndex){
ForEach(0..<inArray.count) { index in
Text(self.inArray[index].name)
}
}.pickerStyle(SegmentedPickerStyle())
}
Section(header: Text("转换为 \(!isMetricToEnglish ? "公制" : "英制")")
.font(.subheadline).bold()
){
Text("单位")
Picker("", selection: $outIndex){
ForEach(0..<outArray.count) { index in
Text(self.outArray[index].name)
}
}.pickerStyle(SegmentedPickerStyle())
}
Section(header: Text("结果")
.font(.subheadline).bold()
) {
Text("\(result, specifier: "%.2f")")
}
}
}.navigationBarTitle("长度转换").padding()
}
}
}
【项目心得】
- json文件的书写一定要仔细检查,系统对格式的要求超级严格。至少要使用线上工具校验一下。
- 拼写、拼写、拼写,在有自动提示的前提下,基本上关键字不会出错,但是自定义的变量名还是很容易错的。
- 阴影修饰一定要在圆角修饰之后。
- 发现错误不用怕,慢慢找,总能查出原因的。
需要进一步研究:
- keyboard的呼出与隐藏,要再继续多实验几次。
- 修饰器还需要多熟悉