使用 .navigationBarItems

1
2
3
.navigationBarItems(trailing:
// our button here
)

按钮调用一个方法

首先,我们需要一个用于按钮调用的方法,因此添加一个空calculateBedtime()方法,如下所示:

1
2
func calculateBedtime() {
}

在我们的案例中,我们想用“计算”按钮替换该注释。之前我曾解释过按钮有两种形式:

1
2
3
4
5
6
7
8
9
Button("Hello") {
print("Button was tapped")
}

Button(action: {
print("Button was tapped")
}) {
Text("Hello")
}

如果需要,我们可以在这里使用第一个选项:

1
2
3
Button("Calculate") {
self.calculateBedtime()
}

那会很好,但是我希望您重新考虑。该代码创建了一个新的闭包,闭包的唯一工作就是调用一个方法。闭包在大多数情况下只是没有名称的函数–我们将它们直接分配给某个对象,而不是将它们作为单独的实体。

因此,我们正在创建一个仅调用另一个函数的函数。如果我们可以完全跳过中间层,对每个人都更好吗?

好吧,我们可以。该按钮关心的是它的动作是某种函数,该函数不接受任何参数并且不发送任何内容-只要它们都遵循这些规则,就不在乎这是方法还是闭包。

结果,我们实际上可以calculateBedtime直接发送到按钮的动作,如下所示:

1
2
3
Button(action: calculateBedtime) {
Text("Calculate")
}

现在,当人们看到他们经常认为我犯了一个错误时。他们想改成这样:

1
2
3
Button(action: calculateBedtime()) {
Text("Calculate")
}

但是,该代码将不起作用,并且实际上意味着完全不同的东西。如果我们在括号后加上括号,calculateBedtime则表示“调用calculateBedtime(),当点击按钮时它将发送回正确的函数以使用。”因此,Swift将要求calculateBedtime()返回一个闭包以运行。

通过编写calculateBedtime而不是calculateBedtime()告诉我们Swift在单击按钮时运行该方法,仅此而已;它不会返回任何应运行的内容。

Swift确实模糊了函数,方法,闭包甚至运算符(+,-等等)之间的界限,这使我们可以如此互换地使用它们。

因此,整个修饰符应如下所示:

1
2
3
4
5
.navigationBarItems(trailing:
Button(action: calculateBedtime) {
Text("Calculate")
}
)

如何调用 CoreML

当您将.mlmodel文件添加到Xcode时,它将自动创建同名的Swift类。您看不到类,也不需要-类是在生成过程中自动生成的。但是,这的确意味着如果模型文件的名称是奇数的,那么自动生成的类名也将使用奇数的名称。

就我而言,我有一个名为“ BetterRest 1.mlmodel”的文件,这意味着Xcode会生成一个名为的Swift类BetterRest_1。无论您的模型文件使用什么名称,都请将其重命名为“ SleepCalculator.mlmodel”,从而使自动生成的类称为SleepCalculator。

我们如何确定?好吧,只需选择模型文件本身,Xcode将为您显示更多信息。您会看到它知道我们的作者和描述,所创建的Swift类的名称,输入及其类型的列表以及输出以及类型的信息-这些都编码在模型文件中,这就是为什么(相比!)很大。

让我们开始填写calculateBedtime()。首先,我们需要创建SleepCalculator该类的实例,如下所示:

1
let model = SleepCalculator()

那就是读取我们所有数据并输出预测的东西。我们使用包含以下字段的CSV文件训练了模型:

  • “唤醒”:用户想要唤醒时。这表示为从午夜开始的秒数,因此上午8点将是8小时乘以60乘以60,得出28800。
  • “ estimatedSleep”:用户希望拥有的睡眠时间,大致存储为4到12之间的值,每季度增加一次。
  • “咖啡”:用户每天喝几杯咖啡。

因此,为了从我们的模型中获得预测,我们需要填写这些值。

下一步是将我们的价值观输入Core ML,然后看看结果如何。如果Core ML遇到某种问题,这可能会失败,因此我们需要使用do和catch。老实说,我不能认为我的人生曾经失败过,但是安全无害!

因此,我们将创建一个do/catch块,并在其中使用prediction()我们模型的方法。这需要唤醒时间,估计的睡眠时间以及进行预测所需的咖啡量值,所有这些Double值均作为值提供。我们只是计算了hour和minute作为秒,因此我们将它们加在一起后再发送。

请将此代码添加到calculateBedtime()现在:

1
2
3
4
5
6
7
do {
let prediction = try model.prediction(wake: Double(hour + minute), estimatedSleep: sleepAmount, coffee: Double(coffeeAmount))

// more code here
} catch {
// something went wrong!
}

有了这个,prediction现在包含了他们实际需要多少睡眠。几乎可以肯定,这不是我们模型看到的训练数据的一部分,而是由Core ML算法动态计算的。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import SwiftUI

struct ContentView: View {
@State private var sleepAmount = 8.0
@State private var wakeUp = Date()
@State private var coffeeAmount = 1

//alert
@State private var alertTitle = ""
@State private var alertMessage = ""
@State private var showingAlert = false

var body: some View {
NavigationView {
VStack {
Text("When do you want to wake up?")
.font(.headline)
DatePicker(selection: $wakeUp,displayedComponents: .hourAndMinute, label: {
Text("Please enter a date") })
.labelsHidden()

Text("Desired amount of sleep")
.font(.headline)

Stepper(value: $sleepAmount, in: 4...12, step: 0.25) {
Text("\(sleepAmount, specifier: "%g") hours")
}

Text("Daily coffee intake")
.font(.headline)

Stepper(value: $coffeeAmount, in: 1...20) {
if coffeeAmount == 1{
Text("1 cup")
}else{
Text("\(coffeeAmount) cups")
}
}
}
.navigationBarTitle("BetterRest")
.navigationBarItems(trailing:
Button(action: calculateBedtime) {
Text("Caculate")
})
.alert(isPresented: $showingAlert){
Alert(title: Text(alertTitle), message: Text(alertMessage), dismissButton: .default(Text("OK")))
}
}
}
func calculateBedtime() {
let model = SleepCalculator()
let components = Calendar.current.dateComponents([.hour, .minute], from: wakeUp)
let hour = (components.hour ?? 0) * 60 * 60
let minute = (components.minute ?? 0) * 60

do {
let prediction = try model.prediction(wake: Double(hour + minute), estimatedSleep: sleepAmount, coffee: Double(coffeeAmount)) //实用模型计算时间

let sleepTime = wakeUp - prediction.actualSleep

let formatter = DateFormatter()

formatter.timeStyle = .short

alertMessage = formatter.string(from: sleepTime)
alertTitle = "Your ideal bedtime is ..."

} catch {
alertTitle = "Error"
alertMessage = "Sorry there was a problem calculating your bedtime."
}

showingAlert = true

}
}



struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

参考资料

查看下一天的SwiftUI学习笔记

关于100days英文课程