我一直在使用 SwiftUI 和合并,感觉可能有一种方法可以获取视图中现有的 @State 属性并创建一个新的属性。
例如,我有一个密码创建视图,其中包含用户的密码和密码确认字段。我想获取这两个 @State 属性并派生一个新的 @State,我可以在我的视图中使用它来断言输入是否有效。所以为了简单起见:不空且相等。
Apple 文档说绑定上有一个发布者,尽管我似乎无法掌握它。
这是一些不起作用的伪代码:
import SwiftUI
import Combine
struct CreatePasswordView : View {
@State var password = ""
@State var confirmation = ""
lazy var valid = {
return self.$password.publisher()
.combineLatest(self.$confirmation)
.map { $0 != "" && $0 == $1 }
}
var body: some View {
SecureField($password, placeholder: Text("password"))
SecureField($confirmation, placeholder: Text("confirm password"))
NavigationButton(destination: NextView()) { Text("Done") }
.disabled(!valid)
}
}
任何人发现。解决这个问题的适当方法/如果可能的话?
更新测试版 2:
从 beta 2 发布者开始,该代码的前半部分现在可以工作了。在视图中使用生成的发布者的后半部分我仍然没有弄清楚(
disabled(!valid)
)。
import SwiftUI
import Combine
struct CreatePasswordView : View {
@State var password = ""
@State var confirmation = ""
lazy var valid = {
Publishers.CombineLatest(
password.publisher(),
confirmation.publisher(),
transform: { String($0) != "" && $0 == $1 }
)
}()
var body: some View {
SecureField($password, placeholder: Text("password"))
SecureField($confirmation, placeholder: Text("confirm password"))
NavigationButton(destination: NextView()) { Text("Done") }
.disabled(!valid)
}
}
谢谢。
我不会使用
@State/@Published
,因为 Combine
目前处于测试阶段,但这里有一个简单的解决方法,可以满足您想要实现的目标。
我会实现一个视图模型来保存密码、密码确认以及密码是否有效
class ViewModel: NSObject, BindableObject {
var didChange = PassthroughSubject<Void, Never>()
var password: String = "" {
didSet {
didChange.send(())
}
}
var passwordConfirmation: String = "" {
didSet {
didChange.send(())
}
}
var isPasswordValid: Bool {
return password == passwordConfirmation && password != ""
}
}
通过这种方式,每当密码或确认更改时都会重新计算视图。
然后我会对视图模型创建一个
@ObjectBinding
。
struct CreatePasswordView : View {
@ObjectBinding var viewModel: ViewModel
var body: some View {
NavigationView {
VStack {
SecureField($viewModel.password,
placeholder: Text("password"))
SecureField($viewModel.passwordConfirmation,
placeholder: Text("confirm password"))
NavigationButton(destination: EmptyView()) { Text("Done") }
.disabled(!viewModel.isPasswordValid)
}
}
}
}
我必须将视图放入
NavigationView
中,因为 NavigationButton
如果不在其中之一中,则似乎无法启用自身。
您需要的是一个计算属性。顾名思义,每次访问该属性时都会重新计算其值。如果您使用
@State
变量来计算属性,每当 valid
发生更改时,SwiftUI 都会自动重新计算 View 的主体:
struct CreatePasswordView: View {
@State var password = ""
@State var confirmation = ""
private var valid: Bool {
password != "" && password == confirmation
}
var body: some View {
SecureField($password, placeholder: Text("password"))
SecureField($confirmation, placeholder: Text("confirm password"))
NavigationLink(destination: NextView()) { Text("Done") }
.disabled(!valid)
}
}
Binding 有 初始化器 可能会有所帮助。这有点 hacky,因为它不是实际的绑定,但可以完成工作
@State private var channelSelectionError: Error?
private var isChannelSelectionErrorAlertPresented: Binding<Bool> {
.init(get: {
channelSelectionError != nil
}, set: { newValue in
if newValue {
// Do nothing
} else {
channelSelectionError = nil
}
})
}