如何为绑定和显示创建不同类型的自定义SwiftUI绑定?



根据本文,创建具有不同类型的自定义Binding用于绑定和显示应该很简单。这是我不工作的例子:

import SwiftUI
struct BindingButton: View {
@Binding var title: String
var onTap: () -> Void
var body: some View {
Button {
onTap()
} label: {
Text(title)
}
}
}
struct CustomBinding: View {
@State var date: Date
@State var int: Int

lazy var descriptiveDate = Binding(
get: { //Escaping closure captures mutating 'self' parameter
return self.date.description
},
set: { _ in }
)

lazy var descriptiveInt = Binding(
get: { //Escaping closure captures mutating 'self' parameter
return String(self.int)
},
set: { _ in }
)

var body: some View {
VStack {
// Cannot find '$descriptiveDate' in scope
BindingButton(title: $descriptiveDate) {
date = Date()
}
// Cannot find '$descriptiveInt' in scope
BindingButton(title: $descriptiveInt) {
int += 2
}
}
}
}

我想绑定日期和Int,但使用字符串显示。有没有可能没有双重绑定?我的意思是我想避免为日期和Int创建第一个绑定,然后.onChange更新字符串和字符串的另一个绑定。我必须这样做吗,或者有更优雅的方法?

Binding根据定义是双向连接。lazy表示代码只运行一次。基于此和空的set,您的descriptiveDatedescriptiveInt不需要是Binding,只是Stringget

struct CustomBinding: View {
@State var date: Date
@State var int: Int

var descriptiveDate: String {
date.description
}

var descriptiveInt : String{
String(self.int)
}

var body: some View {
VStack {
BindingButton(title: descriptiveDate) {
date = Date()
}
BindingButton(title: descriptiveInt) {
int += 2
}
}
}
}
struct BindingButton: View {
let title: String
var onTap: () -> Void
var body: some View {
Button {
onTap()
} label: {
Text(title)
}
}
}

lazy不与SwiftUIView工作,当@State触发重画时,您需要重新计算body和描述性变量。

我完全同意前面的答案,在这种情况下不需要绑定。但是为了解决问题的另一部分:"有没有可能在没有双重绑定的情况下拥有相互依赖的@State@Binding?">

首先,即使在最原始的方式中,您也不需要自定义绑定。相反,你可以使用两个状态变量:

@State var descriptiveDate: String
@State var descriptiveInt: String
并利用didSet:
@State var date: Date {
didSet {
descriptiveDate = date.description
}
}
@State var int: Int {
didSet {
descriptiveInt = String(int)
}
}

@State var descriptiveDate: String
@State var descriptiveInt: String

或者为dateint状态实现onChange:

struct CustomBinding: View {

@State var date: Date
@State var int: Int
@State var descriptiveDate: String
@State var descriptiveInt: String

var body: some View {
VStack {
BindingButton(title: $descriptiveDate) {
date = Date()
}
.onChange(of: date) { newDate in
descriptiveDate = newDate.description
}
BindingButton(title: $descriptiveInt) {
int += 2
}
.onChange(of: int) { newInt in
descriptiveInt = String(int)
}
}

}
}

但是这仍然有点多余,因为您将为每个变量复制@State。因此,为了避免自定义绑定是有帮助的:

BindingButton(title: Binding(
get: { int.description },
set: { int = Int($0)! }
)) {
int += 2
}

也就是说:你不需要为每个状态保留额外的状态,同时仍然有可能从子改变父值。在这个具体的例子中没有多大意义,但在某些情况下是有意义的。

我们是否避免了"double"变量——不完全是。因为你有两条信息,它们可以按照一定的规则变化,但它们是独立的,所以你确实需要两个东西。但是自定义绑定只是一种可能性。