15

我一直在玩 SwiftUI 和 Combine,感觉可能有一种方法可以在视图中获取现有的 @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)
    }
}

谢谢。

4

2 回答 2

2

目前我不会在测试版@State/@PublishedCombine使用,但这里有一个简单的解决方法,可以解决您想要实现的目标。

我会实现一个视图模型来保存密码、密码确认以及它是否有效

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

}

我不得不将视图放在 a 中NavigationView,因为NavigationButton如果它不在其中一个中,它似乎不会启用。

于 2019-06-10T16:26:14.783 回答
0

您在这里需要的是一个计算属性。顾名思义,每次访问属性时都会重新计算其值。如果你使用@State变量来计算属性,SwiftUI 会在 View 的 body 发生变化时自动重新计算valid

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)
    }
}
于 2021-06-16T19:27:18.930 回答