web-dev-qa-db-ja.com

既存のSwiftUI @Statesからバインディングを派生させる

私はSwiftUIとCombineをいじってみましたが、ビューの既存の@Stateプロパティを取得して新しいものを作成する方法があると思います。

たとえば、ユーザーのパスワードとpasswordConfirmフィールドを保持するパスワード作成ビューがあります。これらの2つの@Stateプロパティを取得し、ビューで使用できる新しい@Stateを派生させて、入力が有効かどうかをアサートしたいと考えています。簡単にするために、空ではなく等しくないようにします。

Apple docsは 発行者がいます と言いますが、私はそれを理解しているようには見えませんが。

これは機能しない疑似コードです:

_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の更新:

ベータ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)
    }
}
_

ありがとう。

9
freebie

Combineは現在ベータ版であるため、私は@State/@Publishedで遊んでいませんが、達成しようとしていることの簡単な回避策を次に示します。

パスワード、パスワードの確認、およびそれが有効かどうかを保持するビューモデルを実装します

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つにない場合、それ自体を有効にできないようです。

3
Matteo Pacini