如果需要,更新接收器中即将到来的值
Update upcoming values in sink if needed
我目前正在尝试从使用 Binding<Double>
的文本字段修改即将到来的值,但尚未找到任何可行的解决方案。它只是无限循环(如下例所示)和其他最终不起作用的解决方案。因此,例如,如果用户输入的金额太低,我想将即将到来的值更改为最小值,如果该值高于最大值,则反之亦然。
我还想为用户显示 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 [=10=] < self.minimum {
// Set minimum value
self.amount = self.minimum
} else if [=10=] > self.maximum {
// Set maximum value
self.amount = self.maximum
}
// If `Else` is implemented it will just be an infinite loop...
else {
self.amount = [=10=]
}
}
.store(in: &subscriptions)
}
func prepareStuff() {
// Start preparing
let chosenAmount = amount
}
}
一种方法是使用 属性 包装器来限定值。
这是一个非常基本的问题示例,我们有一个 amount
,我们可以将其更改为任何值。 Stepper
只是让 input/testing:
变得容易
struct ContentView: View {
@State private var amount = 0
var body: some View {
Form {
Stepper("Amount", value: $amount)
Text(String(amount))
}
}
}
这个例子的问题在于 amount
不限于一个范围。要解决此问题,请创建一个 Clamping
属性 包装器(部分来自 here):
@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
以获得 clamped 值。像我在解决方案中所做的那样使用 $amount.clampedValue
来获取 Binding<Double>
绑定。
如果有时在编译链式 属性 包装器时遇到麻烦(可能是错误),这里是我使用 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))
}
}
我目前正在尝试从使用 Binding<Double>
的文本字段修改即将到来的值,但尚未找到任何可行的解决方案。它只是无限循环(如下例所示)和其他最终不起作用的解决方案。因此,例如,如果用户输入的金额太低,我想将即将到来的值更改为最小值,如果该值高于最大值,则反之亦然。
我还想为用户显示 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 [=10=] < self.minimum {
// Set minimum value
self.amount = self.minimum
} else if [=10=] > self.maximum {
// Set maximum value
self.amount = self.maximum
}
// If `Else` is implemented it will just be an infinite loop...
else {
self.amount = [=10=]
}
}
.store(in: &subscriptions)
}
func prepareStuff() {
// Start preparing
let chosenAmount = amount
}
}
一种方法是使用 属性 包装器来限定值。
这是一个非常基本的问题示例,我们有一个 amount
,我们可以将其更改为任何值。 Stepper
只是让 input/testing:
struct ContentView: View {
@State private var amount = 0
var body: some View {
Form {
Stepper("Amount", value: $amount)
Text(String(amount))
}
}
}
这个例子的问题在于 amount
不限于一个范围。要解决此问题,请创建一个 Clamping
属性 包装器(部分来自 here):
@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
以获得 clamped 值。像我在解决方案中所做的那样使用 $amount.clampedValue
来获取 Binding<Double>
绑定。
如果有时在编译链式 属性 包装器时遇到麻烦(可能是错误),这里是我使用 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))
}
}