在Simulator / Preview上运行的SwiftUI:切换无法正常工作,控制台日志:“无效模式'kCFRunLoopCommonModes'”

时间:2019-08-19 14:26:50

标签: swift xcode swiftui

更新:这是一条红鲱鱼

所以我最初的问题问如何禁用导航链接,并且仅在影响两个Toggle属性的两个@State var isXYZToggleOn Bool都为 true 时才启用。这一直都是有效的,我第一次尝试使用.disabled(!(hasAgreedToTermsAndConditions && hasAgreedToPrivacyPolicy))是正确的方法(也由@superpuccio提出,但是使用了两个否定和一个布尔值或(||)。

我之所以没有启用我的NavigationLink是因为切换不起作用,不是因为布尔值和disabled视图修饰符的使用不正确。

在设备上运行,而不是模拟器使一切正常!但是,只要按任意一个Toggle(但只有一次),我仍然会看到警告消息:

invalid mode 'kCFRunLoopCommonModes' provided to CFRunLoopRunSpecific - break on _CFRunLoopError_RunCalledWithInvalidMode to debug. This message will only appear once per execution.

我也得到了在模拟器上运行的错误消息,但是NavigationLink从未启用。

我正在2016 Macbook Pro的Catalina 5 beta上运行Xcode 5 beta。删除派生数据,重新启动Xcode,重新启动计算机,重置模拟器,更改模拟器,无济于事。按下第一个invalid mode 'kCFRunLoopCommonModes'时,我仍然看到Toggle,并且NavigationLink从未启用。

所以新的问题是:

在模拟器上运行:如何解决invalid mode 'kCFRunLoopCommonModes'导致我绑定到@State的{​​{1}}永远不会成为Toggle的问题?

原始问题

使用XCode 11 beta 5和SwiftUI。在true中,我有两个WelcomeScene视图,一个视图用于接受条款和条件,另一个用于隐私政策。这些切换分别更新两个单独的Toggle属性。

在场景的底部,我有一个@State(按钮),它将转到下一个场景,我希望默认情况下 禁用 。 >,并且只有在 NagivationLinkhasAgreedToTermsAndConditions的状态均为hasAgreedToPrivacyPolicy时,才能启用

启动true时,会有一个NavigationLink参数,它带有一个isActive,听起来像是对的。但是,Binding<Bool>属性不能标记为@Binding,因此我不能使其成为依赖于lazyhasAgreedToTermsAndConditions的计算属性。

还有一个hasAgreedToPrivacyPolicy视图修饰符,它使用disabled,这也是不正确的,因为它没有被更新...

Bool
当两个切换均为struct WelcomeScene: View { @State var hasAgreedToTermsAndConditions: Bool = false @State var hasAgreedToPrivacyPolicy: Bool = false var body: some View { VStack { Image(named: "MyImage") Spacer() Text("Welcome friend!".uppercased()) .font(.system(size: 55)) Toggle(isOn: $hasAgreedToTermsAndConditions) { Text("I agree to the Terms and Conditions") }.toggleStyle(DefaultToggleStyle()) Toggle(isOn: $hasAgreedToPrivacyPolicy) { Text("I agree to the Privacy Policy") }.toggleStyle(DefaultToggleStyle()) // This does not compile 'Binding<Bool> is not convertible to Bool', but I cannot figure out how to create a compupted property binding using those 2 states... NavigationLink("Proceed", destination: SignInScene(), isActive: ($hasAgreedToTermsAndConditions && $hasAgreedToPrivacyPolicy)) }.padding(30) } } 时,如何默认设置NavigationLink disabled并仅设置enabled

2 个答案:

答案 0 :(得分:0)

我没有找到从某个州获得出版商的任何方法,因此有必要将其转移到ObservableObject中。从@Published个项目中,人们可以通过$hasAgreedToPrivacyPolicy访问它的发布者,可以在Publishers.CombineLatest之类的东西中使用它来结合两个发布者值,并基于该集合的其他变量。

两个导航链接是必需的,因为一旦一个链接处于活动状态,使其变为非活动状态并不会隐藏详细信息视图中显示的内容。第二个基本上是在那里显示内容,而条款尚未接受。

注意:此处显示的是一个拆分视图(使用NavigationLink中嵌入的NavigationView)。您还可以使用.sheet(由isActive控制)或有条件的在视图之间进行切换。

import SwiftUI
import Combine

class Settings: ObservableObject {
    @Published var hasAgreedToTermsAndConditions: Bool = false
    @Published var hasAgreedToPrivacyPolicy: Bool = false

    @Published var isActive = false
    @Published var isNotActive = true

    private var cancellable: AnyCancellable? = nil

    init() {
        self.cancellable = Publishers.CombineLatest($hasAgreedToPrivacyPolicy, $hasAgreedToTermsAndConditions).map{
            return $0.0 && $0.1
        }.sink{
            self.isActive = $0
            self.isNotActive = !$0
        }
    }
}

struct WelcomeSceneView: View {
    @ObservedObject var settings = Settings()

    var body: some View {
        NavigationView {
            VStack {
                Image(uiImage: welcomeLogo)

                Spacer()

                Text("Welcome friend!".uppercased())
                //.font(.system(size: 55))

                Toggle(isOn: $settings.hasAgreedToTermsAndConditions) {
                    Text("I agree to the Terms and Conditions")
                }.toggleStyle(DefaultToggleStyle())

                Toggle(isOn: $settings.hasAgreedToPrivacyPolicy) {
                    Text("I agree to the Privacy Policy")
                }.toggleStyle(DefaultToggleStyle())


                // This does not compile 'Binding<Bool> is not convertible to Bool', but I cannot figure out how to create a compupted property binding using those 2 states...
                Text("is active: \(settings.isActive.description)")
                NavigationLink("Can proceed", destination: SignInScene(), isActive: $settings.isActive)
                .hidden()
                NavigationLink("Can not proceed", destination: PleaseConfirmView(), isActive: $settings.isNotActive)
                .hidden()
            }.padding(30)
        }
    }

    let welcomeLogo = UIImage(systemName: "headphones")!
}

struct SignInScene: View {
    var body: some View {
        Text("Some sign in scene")
    }
}

struct PleaseConfirmView: View {
    var body: some View {
        Text("Please confirm")
    }
}

答案 1 :(得分:0)

这里的主要问题是您误解了所使用的NavigationLink初始化的含义。

/// Creates an instance that presents `destination` when active, with a
/// `Text` label generated from a title string.
public init(_ titleKey: LocalizedStringKey, destination: Destination, isActive: Binding<Bool>)

isActive并不意味着仅当布尔值为true时,链接才可单击。这意味着您正在创建一个NavigationLink,当单击并在 isActive绑定变为true时触发导航两者

您可以通过disabled修饰符来实现所需的目标:

struct WelcomeScene: View {
    @State var hasAgreedToTermsAndConditions: Bool = false
    @State var hasAgreedToPrivacyPolicy: Bool = false

    var body: some View {
        NavigationView {
            VStack {

                Spacer()

                Toggle(isOn: $hasAgreedToTermsAndConditions) {
                    Text("I agree to the Terms and Conditions")
                }.toggleStyle(DefaultToggleStyle())

                Toggle(isOn: $hasAgreedToPrivacyPolicy) {
                    Text("I agree to the Privacy Policy")
                }.toggleStyle(DefaultToggleStyle())

                NavigationLink("Proceed", destination: SignInScene())
                    .disabled(!hasAgreedToTermsAndConditions || !hasAgreedToPrivacyPolicy)
            }.padding(30)
        }
    }
}