Search code examples
swiftswiftuiswiftui-environment

Swift UI - Toggle Not Hiding Text Elements


I have a simple app with two tab pages - preferences page and directory page. Directory page shows the name, address and other basic info about people (mock data) and preferences page displays toggles which should determine which fields about a person should be shown.

Disabling the toggles does not hide the disabled properties (e.g., name, address) shown on the directory page, and even logs a warning in the console: invalid mode 'kCFRunLoopCommonModes' provided to CFRunLoopRunSpecific - break on _CFRunLoopError_RunCalledWithInvalidMode to debug. This message will only appear once per execution.

ContentView (Root View of App - points to the two pages)

struct ContentView: View {
    var body: some View {
        TabView {
            DirectoryView()
                .tabItem {
                    Text("Directory")
                    Image(systemName: "list.bullet.rectangle.portrait")
                }
            PreferencesView()
                .tabItem {
                    Text("Preferences")
                    Image(systemName: "gear")
                }
        }
        .environmentObject(PreferencesModel())
    }
}

DirectoryView (Displays people's info)

struct DirectoryView : View {
    @EnvironmentObject var preferencesModel:PreferencesModel
    let personModel = PersonModel()
    
    var body: some View {
        ScrollView {
            ForEach(personModel.people) { person in
                VStack(alignment: .leading) {
                    if preferencesModel.displayName {
                        HStack {
                            Text("Name: ")
                                .font(.body)
                                .fontWeight(.bold)
                            Text(person.name)
                        }
                        .frame(width: UIScreen.main.bounds.size.width, height: 20, alignment: .leading)
                    }
                    if preferencesModel.displayAddress {
                        HStack {
                            Text("Address: ")
                                .font(.body)
                                .fontWeight(.bold)
                            Text(person.address)
                        }
                        .frame(width: UIScreen.main.bounds.size.width, height: 20, alignment: .leading)
                    }
                    if preferencesModel.displayCompany {
                        HStack {
                            Text("Company: ")
                                .font(.body)
                                .fontWeight(.bold)
                            Text(person.company)
                        }
                        .frame(width: UIScreen.main.bounds.size.width, height: 20, alignment: .leading)
                    }
                    if preferencesModel.displayExperience {
                        HStack {
                            Text("Years of Experience: ")
                                .font(.body)
                                .fontWeight(.bold)
                            Text(String(person.yearsOfExperience))
                        }
                        .frame(width: UIScreen.main.bounds.size.width, height: 20, alignment: .leading)
                    }
                }
                .padding()
                Divider()
            }
        }
    }
}

PreferencesModel (ViewModel storing preferences around which fields to display)

class PreferencesModel: ObservableObject {
    var displayName = true
    var displayAddress = true
    var displayCompany = true
    var displayExperience = true
}

PreferencesView (View with the toggles)

struct PreferencesView: View {
    @EnvironmentObject var preferencesModel:PreferencesModel
    
    var body: some View {
        ScrollView {
            VStack {
                Toggle("Name", isOn: $preferencesModel.displayName)
                Toggle("Address", isOn: $preferencesModel.displayAddress)
                Toggle("Company", isOn: $preferencesModel.displayCompany)
                Toggle("Years of experience", isOn: $preferencesModel.displayExperience)
            }
        }
    }
}

Solution

  • You are missing @Published on the ObservableObject variables. You should also put the initial PreferenceModel in an @StateObject in the ContentView to protect it from re-creating.