【问题标题】:Deriving binding from existing SwiftUI @States从现有的 SwiftUI @States 派生绑定
【发布时间】:2019-10-25 00:27:52
【问题描述】:

我一直在玩 SwiftUI 和 Combine,感觉可能有一种方法可以在视图中获取现有的 @State 属性并创建一个新属性。

例如,我有一个密码创建视图,其中包含用户的密码和密码确认字段。我想获取这两个@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 开始,发布者可用,因此该代码的前半部分现在可以使用。在 View 中使用生成的发布者的后半部分我还没有弄清楚 (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)
    }
}

谢谢。

【问题讨论】:

  • 你试过var valid: Bool { password != "" && password == confirmation }吗?因为passwordconfirmation 都是@State,所以body 在它们发生变化时总是会重新计算,valid 将反映新的状态
  • @RicoCrescenzio 我认为这是正确的想法

标签: swift swiftui combine


【解决方案1】:

你需要的是一个计算属性。顾名思义,每次访问属性时都会重新计算其值。如果你使用@State变量来计算属性,SwiftUI会在valid发生变化时自动重新计算View的body:

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

【讨论】:

    【解决方案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 不在其中之一中,它似乎不会启用自己。

    【讨论】:

    • 你为什么说Combine被窃听了?您在使用 Combine 时遇到过什么重大问题?
    • @SMP 我的意思是beta - Swift 编译器还没有完全准备好像@State/@Published 这样的@propertyWrapper/@propertyDelegate attributes。最后,Foundation 集成无法按照 Xcode 11 beta release notes 工作 - 搜索 51241500。
    • 欣赏这个,谢谢。我实际上已经尝试过这种方法,尽管我实际上想做的不仅仅是简单的验证,还想发出一个带去抖动的网络请求(检查haveibeenpwned.com API 是否有泄露的密码)。 Apple 有 WWDC 视频,介绍了类似的集成 UIKit+Combine 的东西 - 也许 Combine+SwiftUI 还没有,或者他们有其他的想法。
    • @freebie 怀疑它主要是幻灯片,而不是实时编码 - 他们不想冒红点的风险!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-10
    • 2017-02-26
    • 1970-01-01
    相关资源
    最近更新 更多