我正在用钱输入屏幕,需要实现一个自定义项,init
以根据初始化的金额设置状态变量。
我以为这可以用,但是出现编译器错误:
Cannot assign value of type 'Binding<Double>' to type 'Double'
struct AmountView : View {
@Binding var amount: Double
@State var includeDecimal = false
init(amount: Binding<Double>) {
self.amount = amount
self.includeDecimal = round(amount)-amount > 0
}
...
}
Run Code Online (Sandbox Code Playgroud)
Sim*_*ang 38
您应该使用下划线来访问属性包装器本身的合成存储。
\n在你的情况下:
\ninit(amount: Binding<Double>) {\n _amount = amount\n includeDecimal = round(amount)-amount > 0\n}\n
Run Code Online (Sandbox Code Playgroud)\n以下是苹果文档中的引用:
\n\n\n编译器通过在包装属性名称前添加下划线 (_)\xe2\x80\x94 来合成包装类型实例的存储,例如,someProperty 的包装存储为 _someProperty。包装器的综合存储具有私有的访问控制级别。
\n
链接: https: //docs.swift.org/swift-book/ReferenceManual/Attributes.html -> propertyWrapper 部分
\nkon*_*iki 32
啊!你好亲近 这就是你的做法。您错过了美元符号(测试版3)或下划线(测试版4),并且在您的amount属性前面输入了self,或者在amount参数之后输入了.value。所有这些选项均有效:
您会看到我删除了@State
includeDecimal中的内容,最后检查了说明。
这是使用属性(将self放在其前面):
struct AmountView : View {
@Binding var amount: Double
private var includeDecimal = false
init(amount: Binding<Double>) {
// self.$amount = amount // beta 3
self._amount = amount // beta 4
self.includeDecimal = round(self.amount)-self.amount > 0
}
}
Run Code Online (Sandbox Code Playgroud)
或之后使用.value(但不使用self,因为您使用的是传递的参数,而不是struct的属性):
struct AmountView : View {
@Binding var amount: Double
private var includeDecimal = false
init(amount: Binding<Double>) {
// self.$amount = amount // beta 3
self._amount = amount // beta 4
self.includeDecimal = round(amount.value)-amount.value > 0
}
}
Run Code Online (Sandbox Code Playgroud)
相同,但是我们对参数(withAmount)和属性(amount)使用不同的名称,因此您可以清楚地看到使用它们的时间。
struct AmountView : View {
@Binding var amount: Double
private var includeDecimal = false
init(withAmount: Binding<Double>) {
// self.$amount = withAmount // beta 3
self._amount = withAmount // beta 4
self.includeDecimal = round(self.amount)-self.amount > 0
}
}
Run Code Online (Sandbox Code Playgroud)
struct AmountView : View {
@Binding var amount: Double
private var includeDecimal = false
init(withAmount: Binding<Double>) {
// self.$amount = withAmount // beta 3
self._amount = withAmount // beta 4
self.includeDecimal = round(withAmount.value)-withAmount.value > 0
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,由于属性包装器(@Binding)创建了不需要使用.value的访问器,因此属性不需要.value。但是,有了参数,就没有这种事情了,您必须显式地进行操作。如果您想了解有关属性包装器的更多信息,请查看WWDC会话415-Modern Swift API Design并跳至23:12。
如您所见,从初始化程序修改@State变量将引发以下错误:线程1:致命错误:在View.body之外访问State。为了避免这种情况,您应该删除@State。这是有道理的,因为includeDecimal不是事实的来源。其值是从金额中得出的。但是,删除@State includeDecimal
不会在金额更改时更新。为此,最好的选择是将includeDecimal定义为计算属性,以便其值源自真值(量)来源。这样,每当金额更改时,您的includeDecimal也会更改。如果您的视图依赖于includeDecimal,则它应在更改时更新:
struct AmountView : View {
@Binding var amount: Double
private var includeDecimal: Bool {
return round(amount)-amount > 0
}
init(withAmount: Binding<Double>) {
self.$amount = withAmount
}
var body: some View { ... }
}
Run Code Online (Sandbox Code Playgroud)
如rob mayoff所示,您还可以使用$$varName
(beta 3)或_varName
(beta4)初始化State变量:
// Beta 3:
$$includeDecimal = State(initialValue: (round(amount.value) - amount.value) != 0)
// Beta 4:
_includeDecimal = State(initialValue: (round(amount.value) - amount.value) != 0)
Run Code Online (Sandbox Code Playgroud)
rob*_*off 12
你说(在评论中)“我需要能够改变includeDecimal
”。改变是什么意思includeDecimal
?您显然想根据amount
(在初始化时)是否为整数来初始化它。好的。那么如果includeDecimal
是false
然后你把它改成会发生true
什么?你会以某种方式强制amount
然后成为非整数吗?
无论如何,你不能includeDecimal
在init
. 但是你可以在 中初始化它init
,像这样:
struct ContentView : View {
@Binding var amount: Double
init(amount: Binding<Double>) {
$amount = amount
$$includeDecimal = State(initialValue: (round(amount.value) - amount.value) != 0)
}
@State private var includeDecimal: Bool
Run Code Online (Sandbox Code Playgroud)
(请注意,在某些时候,$$includeDecimal
语法将更改为_includeDecimal
。)
由于是 2020 年年中,让我们回顾一下:
至于 @Binding amount
_amount
建议只在初始化期间使用。并且self.$amount = xxx
在初始化期间永远不要像这样分配
amount.wrappedValue
并且amount.projectedValue
不经常使用,但您可以看到类似的情况
@Environment(\.presentationMode) var presentationMode
self.presentationMode.wrappedValue.dismiss()
Run Code Online (Sandbox Code Playgroud)
@Binding var showFavorited: Bool
Toggle(isOn: $showFavorited) {
Text("Change filter")
}
Run Code Online (Sandbox Code Playgroud)
您可以使用静态函数或自定义 init 来实现此目的。
\nimport SwiftUI\nimport PlaygroundSupport\n\nstruct AmountView: View {\n @Binding var amount: Double\n @State var includeDecimal: Bool\n var body: some View {\n Text("The amount is \\(amount). \\n Decimals \\(includeDecimal ? "included" : "excluded")")\n }\n}\n\nextension AmountView {\n static func create(amount: Binding<Double>) -> Self {\n AmountView(amount: amount, includeDecimal: round(amount.wrappedValue) - amount.wrappedValue > 0)\n }\n init(amount: Binding<Double>) {\n _amount = amount\n includeDecimal = round(amount.wrappedValue) - amount.wrappedValue > 0\n }\n}\nstruct ContentView: View {\n @State var amount1 = 5.2\n @State var amount2 = 5.6\n var body: some View {\n AmountView.create(amount: $amount1)\n AmountView(amount: $amount2)\n }\n}\n\nPlaygroundPage.current.setLiveView(ContentView())\n
Run Code Online (Sandbox Code Playgroud)\n实际上,您根本不需要在这里进行自定义初始化,因为.onAppear
除非您需要在外部显式设置初始状态,否则可以轻松移动逻辑。
struct AmountView: View {\n @Binding var amount: Double\n @State private var includeDecimal = true\n \n var body: some View {\n Text("The amount is \\(amount, specifier: includeDecimal ? "%.3f" : "%.0f")")\n Toggle("Include decimal", isOn: $includeDecimal)\n .onAppear {\n includeDecimal = round(amount) - amount > 0\n }\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n这样,您就可以将 @State 保持私有并按照文档的建议在内部进行初始化。
\n\n\n不要在视图层次结构中实例化视图的位置初始化视图的状态属性,因为这可能与 SwiftUI 提供的存储管理发生冲突。为了避免这种情况,\n始终将状态声明为私有,并将其放置在\n需要访问该值的视图层次结构中的最高视图中
\n
。
\n 归档时间: |
|
查看次数: |
3376 次 |
最近记录: |