Получение привязки из существующего SwiftUI @States - PullRequest
4 голосов
/ 10 июня 2019

Я играл с SwiftUI и Combine и чувствую, что, возможно, есть способ получить существующие свойства @State в представлении и создать новый.

Например, у меня есть View для создания пароля, который содержит пароль и поле passwordConfirm для пользователя. Я хочу взять эти два свойства @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:

Начиная с бета-версии 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)
    }
}

Спасибо.

1 Ответ

2 голосов
/ 10 июня 2019

Я бы не стал играть с @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, кажется, не включается, если его нет в одном из них.

...