SwiftUI:如何使用@Binding变量实现自定义init


102

我正在资金输入屏幕上工作,需要实现一个自定义项,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
    }
    ...
}

Answers:


163

啊!你好亲近 这就是你的做法。您错过了美元符号(测试版3)或下划线(测试版4),并且在您的amount属性前面输入了self,或者在amount参数之后输入了.value。所有这些选项均有效:

您会看到我删除了@StateincludeDecimal中的内容,请检查最后的说明。

这是使用属性(将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
    }
}

或之后使用.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
    }
}

相同,但是我们对参数(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
    }
}
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
    }
}

请注意,由于属性包装器(@Binding)会创建不需要该.value的访问器,因此属性不需要.value。但是,有了参数,就没有这种事情了,您必须显式地执行它。如果您想了解有关属性包装器的更多信息,请查看WWDC会话415-Modern Swift API Design并跳至23:12。

如您所见,从初始化程序修改@State变量将引发以下错误:线程1:致命错误:在View.body之外访问State。为了避免这种情况,您应该删除@State。这是有道理的,因为includeDecimal不是事实的来源。其值是从金额中得出的。但是,通过删除@StateincludeDecimal不会在金额更改时更新。为此,最好的选择是将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 { ... }
}

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)

谢谢!这很有帮助!我得到一个运行时错误self.includeDecimal = round(self.amount)-self.amount > 0Thread 1: Fatal error: Accessing State<Bool> outside View.body
keegan3d

好吧,这很有道理。@State变量应该代表真相的来源。但是在您的情况下,您正在复制该真相,因为includeDecimal的值可以从您的真相实际量即数量得出。您有两种选择:1.将includeDecimal设为私有变量(没有@State),或者甚至更好。2.将其设为从派生其值的计算属性amount。这样,如果金额发生变化,includeDecimal也是如此。您应该这样声明:private var includeDecimal: Bool { return round(amount)-amount > 0 }并删除self.includeDecimal = ...
kontiki

嗯,我需要能够进行更改,includeDecimal因此需要将其作为视图中的@State变量。我真的只想使用一个初始值对其进行初始化
keegan3d

1
@ Let's_Create我只看了一次,但是感谢上帝的转发按钮;-)
kontiki

1
真的很好的解释,谢谢。我认为现在.value已将替换为.wrappedValue,可以很好地更新答案并删除Beta选项。
user1046037 '19

11

您在评论中说:“我需要能够改变includeDecimal”。改变意味着什么includeDecimal?您显然想基于amount(在初始化时)是否为整数来对其进行初始化。好的。如果是的话,会发生什么includeDecimalfalse后来将其更改为和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

(请注意,在某些时候$$includeDecimal语法将被更改为_includeDecimal。)


太棒了,这部分我需要双重$$!
keegan3d

3

既然是2020年中期,让我们回顾一下:

至于 @Binding amount

  1. _amount建议仅在初始化期间使用。self.$amount = xxx在初始化过程中,永远不要这样分配

  2. amount.wrappedValue并且amount.projectedValue不经常使用,但是您会看到类似的情况

@Environment(\.presentationMode) var presentationMode

self.presentationMode.wrappedValue.dismiss()
  1. @binding的常见用例是:
@Binding var showFavorited: Bool

Toggle(isOn: $showFavorited) {
    Text("Change filter")
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.