从现有 SwiftUI @States 派生绑定

问题描述 投票:0回答:3

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

谢谢。

swift swiftui combine
3个回答
1
投票

我不会使用

@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
投票

您需要的是一个计算属性。顾名思义,每次访问该属性时都会重新计算其值。如果您使用

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

0
投票

Binding 有 初始化器 可能会有所帮助。这有点 hacky,因为它不是实际的绑定,但可以完成工作

@State private var channelSelectionError: Error?

private var isChannelSelectionErrorAlertPresented: Binding<Bool> {
    .init(get: {
        channelSelectionError != nil
    }, set: { newValue in
        if newValue {
            // Do nothing
        } else {
            channelSelectionError = nil
        }
    })
}
© www.soinside.com 2019 - 2024. All rights reserved.