这篇文章介绍了在SwiftUI中使用NavigationView添加按钮和调用CoreML模型的方法。通过创建一个空的calculateBedtime()方法,并将其作为按钮的动作,我们可以调用另一个方法来计算睡眠时间。同时,我们还需要创建一个SleepCalculator类的实例来使用CoreML模型进行预测。
此内容根据文章生成,并经过人工审核,仅用于文章内容的解释与总结
投诉NavigationView 中导航栏添加按钮
使用 .navigationBarItems
1 2 3
| .navigationBarItems(trailing: )
|
按钮调用一个方法
首先,我们需要一个用于按钮调用的方法,因此添加一个空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))
} catch { }
|
有了这个,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 86
| import SwiftUI
struct ContentView: View { @State private var sleepAmount = 8.0 @State private var wakeUp = Date() @State private var coffeeAmount = 1 @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英文课程