如果需要,更新sink中即将到来的值



我目前正试图修改即将到来的值从一个使用Binding<Double>的textField,但还没有找到任何工作的解决方案。它只是无限循环(就像下面的例子)和其他最终不起作用的解决方案。因此,例如,如果用户输入的金额过低,我希望将即将到来的值更改为最小值,反之亦然,如果该值高于最大值。

我还想为用户显示modified值(如果需要的话),所以我不能只是将它存储在另一个变量中。

对于如何解决这个问题有什么想法吗?

例子
class ViewModel: ObservableObject {
@Published var amount: Double
private var subscriptions: Set<AnyCancellable> = []
private let minimum: Double = 10_000
private let maximum: Double = 100_000
init() {
$amount
.sink {
if $0 < self.minimum {
// Set minimum value
self.amount = self.minimum
} else if $0 > self.maximum {
// Set maximum value
self.amount = self.maximum
}
// If `Else` is implemented it will just be an infinite loop...
else {
self.amount = $0
}
}
.store(in: &subscriptions)
}
func prepareStuff() {
// Start preparing
let chosenAmount = amount
}
}

一种方法是使用属性包装器来夹紧这些值。

这是一个非常基本的问题的例子,我们有一个amount,我们可以改变为任何值。Stepper使输入/测试变得简单:

struct ContentView: View {
@State private var amount = 0
var body: some View {
Form {
Stepper("Amount", value: $amount)
Text(String(amount))
}
}
}

这个例子的问题是amount不局限于一个范围。为了解决这个问题,创建一个Clamping属性包装器(部分来自这里):

@propertyWrapper
struct Clamping<Value: Comparable> {
private var value: Value
let range: ClosedRange<Value>
var wrappedValue: Value {
get { value }
set { value = min(max(range.lowerBound, newValue), range.upperBound) }
}
var clampedValue: Value {
get { wrappedValue }
set { wrappedValue = newValue }
}
init(wrappedValue value: Value, _ range: ClosedRange<Value>) {
precondition(range.contains(value))
self.value = value
self.range = range
}
}

然后我们可以链接属性包装器,并得到一个amount是有限的工作示例:

struct ContentView: View {
@State @Clamping(-5 ... 5) private var amount = 0
var body: some View {
Form {
Stepper("Amount", value: $amount.clampedValue)
Text(String(amount))
}
}
}

我知道,这不是限制Stepper范围的正确方法。您应该使用Stepper(_:value:in:)。然而,这是为了演示夹紧值——而不是如何夹紧Stepper.

这意味着你需要做什么?

好,首先改变你的@Published属性为:

@Published @Clamping(10_000 ... 100_000) var amount: Double

现在您可以像正常一样访问amount以获得夹住的值。使用$amount.clampedValue,就像我在我的解决方案中得到你的Binding<Double>绑定。


如果在编译链式属性包装器时遇到麻烦(可能是bug),这里是我使用Model对象和@Published重新创建的示例:

struct ContentView: View {
@StateObject private var model = Model(amount: 0)
var body: some View {
Form {
Stepper("Amount", value: $model.amount.clampedValue)
Text(String(model.amount.clampedValue))
}
}
}
class Model: ObservableObject {
@Published var amount: Clamping<Int>
init(amount: Int) {
_amount = Published(wrappedValue: Clamping(wrappedValue: amount, -5 ... 5))
}
}

相关内容

最新更新