Получение привязки из существующего SwiftUI @States
Я поиграл с SwiftUI и Combine и чувствую, что, возможно, есть способ получить существующие свойства @State в представлении и создать новое.
Например, у меня есть представление создания пароля, которое содержит пароль и поле 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)
}
}
Благодарю.
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
кажется, не включается, если не входит ни в одну из них.
Здесь вам нужно вычисленное свойство . Как следует из названия, его значение пересчитывается при каждом доступе к свойству. Если вы используете
@State
переменных для вычисления свойства, SwiftUI будет автоматически пересчитывать тело представления всякий раз, когда
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)
}
}