到目前为止,最后一部分显示了一个简单的文本视图,其中包含用户输入的任何账单金额,但现在是该项目重要部分的时候了:我们希望该文本视图显示每个人需要为账单支付多少。
有几种方法可以解决这个问题,但最简单的方法恰好也是最干净的方法,我的意思是它给了我们清晰易懂的代码:我们将添加一个计算总数的计算属性。
这需要做少量的数学运算:每人应支付的总金额等于订单价值加小费百分比除以人数。
但在我们做这件事之前,我们首先需要找出有多少人,小费百分比是多少,以及订单的价值。听起来很简单,但有一些小问题:
- 正如您已经看到的,
numberOfPeople
被设置为了2——当它存储值3时,意味着5个人。 -
tipPercentage
整数在tipPercentages
数组中存储索引,而不是实际的tip百分比。 -
checkAmount
属性是用户输入的字符串,可能是20这样的有效数字,也可能是“fish”这样的字符串,甚至可能是空的。
所以,我们将创建一个新的计算属性totalPerPerson
,它将是一个Double
,它将从上面的三个值开始。
首先,在body
属性之前添加计算属性:
var totalPerPerson: Double {
// calculate the total per person here
return 0
}
它将返回0,这样代码就不会崩溃,但是我们将用我们的计算替换//calculate the total per person here
注释。
接下来,我们可以通过读取numberOfPeople
并添加2来计算出有多少人。记住,这个值的范围是2到100,但它是从0开始计算的,所以我们需要添加2。
所以,首先用以下内容替换//calculate the total per person here
:
let peopleCount = Double(numberOfPeople 2)
您会注意到,将结果值转换为Double
,这样我们就可以将所有内容相加并将其除以,而不会失去准确性。
接下来我们需要计算出实际的小费百分比。我们的tipPercentage
属性存储用户选择的值,但实际上这只是tipPercentages
数组中的一个索引。因此,我们需要查看tipPercentages
以确定他们选择了什么,然后再次将其转换为Double
,这样我们就可以保持所有精度——将其添加到前一行下面:
let tipSelection = Double(tipPercentages[tipPercentage])
我们需要计算的最后一个数字是他们账单的金额。现在,如果您还记得这实际上是一个字符串,因为它被用作对文本输入框的双向绑定。尽管我们编写代码只显示十进制键盘,但没有什么可以阻止创造性用户在其中输入无效值,因此我们需要小心处理。
我们想要的另一个Double
是账单金额。实际上,我们有一个字符串可能包含也可能不包含有效的Double
:它可能是22.50,可能是空字符串,也可能是莎士比亚的全部作品。它是一根绳子,几乎可以是任何东西。
幸运的是,Swift有一个将字符串转换为Double
的简单方法,它看起来如下:
let stringValue = "0.5"
let doubleValue = Double(stringValue)
这看起来很简单,但有一个问题:doubleValue
的类型最终是Double?
而不是Double
——是的,这是一个可选的。你看,Swift不能确定字符串是否包含可以安全地转换为Double
的内容,所以它使用可选值:如果转换成功,那么我们的optional将包含结果值,但是如果字符串是无效的(“Fish”,莎士比亚的全集,etc)则可选项将设置为nil
。
这里有几种处理可选性的方法,但最简单的方法是使用空合运算符??
,以确保始终存在有效值。
let orderAmount = Double(checkAmount) ?? 0
它将尝试将checkAmount
转换为Double
,但如果由于某种原因失败,则将使用0。
现在我们有了三个输入值,是时候做我们的数学题了。这还需要三个步骤:
1、我们可以通过将
orderAmount
除以100并乘以tipSelection
来计算tip值。 2、我们可以通过向orderAmount
添加tip值来计算账单的总金额。 3、我们可以用总金额除以人数来计算出每人的金额。
一旦完成,我们可以返回每人的金额,我们就完成了。
将属性中的return 0
替换为:
let tipValue = orderAmount / 100 * tipSelection
let grandTotal = orderAmount tipValue
let amountPerPerson = grandTotal / peopleCount
return amountPerPerson
如果您正确地执行了所有操作,那么您的代码应该如下所示:
代码语言:javascript复制var totalPerPerson: Double {
let peopleCount = Double(numberOfPeople 2)
let tipSelection = Double(tipPercentages[tipPercentage])
let orderAmount = Double(checkAmount) ?? 0
let tipValue = orderAmount / 100 * tipSelection
let grandTotal = orderAmount tipValue
let amountPerPerson = grandTotal / peopleCount
return amountPerPerson
}
既然totalPerPerson
给出了正确的值,我们可以更改表中的最后一部分,以便它显示正确的文本。
将这段代码:
代码语言:javascript复制Section {
Text("$(checkAmount)")
}
替换为:
代码语言:javascript复制Section {
Text("$(totalPerPerson)")
}
现在试着运行应用程序,看看你怎么想。您应该会发现,因为构成总数的所有值都用@State
标记,更改其中任何一个值都会导致总数自动重新计算。
希望您现在可以亲眼看到,SwiftUI的视图是其状态的函数——当状态改变时,视图会自动更新以匹配。
在我们完成之前,我们要解决显示的一个小问题,这就是总价格的显示方式。我们的金额计算使用了双精度,这意味着Swift给我们的精度比我们需要的要高得多——我们预计会看到25.50美元,但实际上是25.500000美元。
我们可以通过使用SwiftUI添加的一个简洁的字符串插值功能来解决这个问题:决定数字应该如何在字符串中格式化的能力。这实际上可以追溯到C编程语言,所以语法一开始有点奇怪:我们编写一个名为specifier
的字符串,给它值“%.2f”。这是C的语法,意思是“两位浮点数”
非常粗略地说,“%f”意味着“任何类型的浮点数”,在我们的例子中,它将是整个数字。另一个选择是“%g”,它也做同样的事情,只是它从末尾去掉了不重要的零——12.50美元将被写成12.5美元。把“.2”放进混合物中,就是要求小数点后有两位数字,不管它们是什么。Swift足够聪明地绕过它们,所以我们仍然可以得到很好的精度。
你可以在Wikipedia上阅读更多关于这些C-style格式说明符的信息:https://en.wikipedia.org/wiki/Printf_format_string——我们不会去其他任何地方,所以如果你想满足你的好奇心,它就在那里!
无论如何,我们希望每人的金额使用新的格式说明符,因此请将总金额文本视图修改为:
代码语言:javascript复制Text("$(totalPerPerson, specifier: "%.2f")")
现在最后一次运行这个项目——我们完成了!
Calculating the total per person
项目打卡
WeSplit
Previous: 使用分段控件选择百分比 | Next: SWeSplit 挑战 |
---|