从现有的SwiftUI @States

时间:2019-06-10 15:22:48

标签: swift swiftui combine

我一直在使用SwiftUI和Combine,并且感觉好像有一种方法可以在视图中保留现有的@State属性并创建一个新属性。

例如,我有一个密码创建视图,其中包含用户的密码和passwordConfirm字段。我想采用这两个@State属性,并派生一个新的@State,可以在我的视图中使用它来断言输入是否有效。因此,为简单起见:不能为空且不相等。

Apple文档在绑定上说there is a publisher,尽管我似乎看不到它。

这是一些无效的伪代码:

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

发现任何人。 /如果可能的话,采取适当的方法?

更新Beta 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)
    }
}

谢谢。

2 个答案:

答案 0 :(得分:2)

由于@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不在其中之一中,则似乎无法启用自身。

答案 1 :(得分:0)

这里你需要的是一个计算属性。顾名思义,每次访问该属性时都会重新计算其值。如果您使用 @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)
    }
}