SwiftUI:如何使用@Binding 变量实现自定义初始化 [英] SwiftUI: How to implement a custom init with @Binding variables

查看:66
本文介绍了SwiftUI:如何使用@Binding 变量实现自定义初始化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在处理资金输入屏幕,需要实现自定义 init 以根据初始化金额设置状态变量.

I'm working on a money input screen and need to implement a custom init to set a state variable based on the initialized amount.

我认为这会奏效,但我收到以下编译器错误:

I thought this would work, but I'm getting a compiler error of:

无法分配类型为Binding"的值输入双"

struct AmountView : View {
    @Binding var amount: Double

    @State var includeDecimal = false

    init(amount: Binding<Double>) {
        self.amount = amount
        self.includeDecimal = round(amount)-amount > 0
    }
    ...
}

推荐答案

啊!你是如此接近.这就是你如何做到的.您错过了美元符号 (beta 3) 或下划线 (beta 4),并且在您的 amount 属性前面是 self ,或者是在 amount 参数后面是 .value .所有这些选项都有效:

Argh! You were so close. This is how you do it. You missed a dollar sign (beta 3) or underscore (beta 4), and either self in front of your amount property, or .value after the amount parameter. All these options work:

您会看到我删除了 includeDecimal 中的 @State,请查看最后的说明.

You'll see that I removed the @State in includeDecimal, check the explanation at the end.

这是使用属性(将 self 放在它前面):

This is using the property (put self in front of it):

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,因为您使用的是传递的参数,而不是结构的属性):

or using .value after (but without self, because you are using the passed parameter, not the struct's property):

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
    }
}

请注意,属性不需要 .value,这要归功于属性包装器 (@Binding),它创建了使 .value 变得不必要的访问器.但是,对于参数,没有这样的事情,您必须明确地这样做.如果您想了解有关属性包装器的更多信息,请查看 WWDC session 415 - Modern Swift API设计并跳转到 23:12.

Note that .value is not necessary with the property, thanks to the property wrapper (@Binding), which creates the accessors that makes the .value unnecessary. However, with the parameter, there is not such thing and you have to do it explicitly. If you would like to learn more about property wrappers, check the WWDC session 415 - Modern Swift API Design and jump to 23:12.

如您所见,修改初始化程序中的@State 变量将引发以下错误:线程 1:致命错误:访问 View.body 之外的状态.为了避免它,您应该删除@State.这是有道理的,因为 includeDecimal 不是事实来源.它的价值来源于数量.但是,通过删除 @State,includeDecimal 在数量发生变化时不会更新.为此,最好的选择是将您的 includeDecimal 定义为计算属性,以便其值来自真实来源(金额).这样,每当数量发生变化时,您的 includeDecimal 也会发生变化.如果您的视图依赖于 includeDecimal,则它应该在更改时更新:

As you discovered, modifying the @State variable from the initilizer will throw the following error: Thread 1: Fatal error: Accessing State outside View.body. To avoid it, you should either remove the @State. Which makes sense because includeDecimal is not a source of truth. Its value is derived from amount. By removing @State, however, includeDecimal will not update if amount changes. To achieve that, the best option, is to define your includeDecimal as a computed property, so that its value is derived from the source of truth (amount). This way, whenever the amount changes, your includeDecimal does too. If your view depends on includeDecimal, it should update when it changes:

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(测试版 3)或 _varName (beta4) 初始化状态变量:

As indicated by rob mayoff, you can also use $$varName (beta 3), or _varName (beta4) to initialise a State variable:

// Beta 3:
$$includeDecimal = State(initialValue: (round(amount.value) - amount.value) != 0)

// Beta 4:
_includeDecimal = State(initialValue: (round(amount.value) - amount.value) != 0)

这篇关于SwiftUI:如何使用@Binding 变量实现自定义初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆