Home > OS >  Vertification check help in swiftui
Vertification check help in swiftui

Time:04-06

I am trying to only allow the user to select only one of the topics rather than at least one topic. I have written the code for this, however, it does not seem to do what I hope. I believe the issue could be with the line shown below, however, as a first time swift user I am unaware of what the exact issue could be. allTopics.filter { $0 }.count == 0 && NumberSelected != nil}

(I have removed several sections of code from here for other text etc that will not be relevant to this question.)

import SwiftUI
struct ContentView: View {

//Creating Variables for Revision Topics
@State private var setMemory = false
@State private var setSocialInfluence = false
@State private var setApproaches = false
@State private var setPsychopathology = false
@State private var setBiopsychology = false
@State private var setAttachment = false
@State private var setIssuesandDebates = false
@State private var setSchizophrenia = false
@State private var setResearchMethods = false
@State private var oneClicked = false

//Creating Buttons for Number of Questions
        let buttons = ["10", "20", "30", "40", "50"]
@State public var NumberSelected: Int?


//Creating Variables for 'Continue' Button
let button = ["Continue"]
@State public var buttonContinue: Int?

//Making Sure User Selects Topic(s) and Number of Questions
private var allTopics: [Bool] {
   [setMemory, setSocialInfluence, setApproaches, setPsychopathology, setBiopsychology, setAttachment, setIssuesandDebates, setSchizophrenia, setResearchMethods]}

   private var TopicSelected: Bool {
   allTopics.contains { $0 }}

   private var isFormValid: Bool {
   allTopics.filter { $0 }.count == 0 && NumberSelected != nil}


var body: some View {
    

            Group{
                VStack(alignment: .leading, spacing: 5) {
            Toggle("Memory",isOn: $setMemory)
                .toggleStyle(.button)
                .tint(Color(red: 0.902, green: 0.755, blue: 0.161))
            Toggle("Approaches",isOn: $setApproaches)
                .toggleStyle(.button)
                .tint(Color(red: 0.945, green: 0.442, blue: 0.022))
            Toggle("Biopsychology",isOn: $setBiopsychology)
                .toggleStyle(.button)
                .tint(Color(red: 0.817, green: 0.065, blue: 0.287))
                
            Toggle("Issues & Debates",isOn: $setIssuesandDebates)
                .toggleStyle(.button)
                .tint(Color(red: 0.399, green: 0.06, blue: 0.947))
            Toggle("Research Methods Year 1 & 2",isOn: $setResearchMethods)
                .toggleStyle(.button)
                    .tint(Color(red: 0.105, green: 0.561, blue: 0.896))}
                .padding(.leading, -135.0)
                .padding(.top, -10)
                
        VStack(alignment: .leading, spacing: 5) {
            Toggle("Social Influence",isOn: $setSocialInfluence)
                .toggleStyle(.button)
                .tint(Color(red: 0.902, green: 0.755, blue: 0.17))
            Toggle("Psychopathology",isOn: $setPsychopathology)
                .toggleStyle(.button)
                .tint(Color(red: 0.945, green: 0.442, blue: 0.022))
            Toggle("Attachment",isOn: $setAttachment)
                .toggleStyle(.button)
                .tint(Color(red: 0.817, green: 0.065, blue: 0.287))
            Toggle("Schizophrenia",isOn: $setSchizophrenia)
                .toggleStyle(.button)
            .tint(Color(red: 0.394, green: 0.061, blue: 0.943))}
                .padding(.top, -192)
                .padding(.leading, 180)
    }
        
//Number of Questions - Selected Buttons
        HStack(spacing: 15) {
            ForEach(0..<buttons.count, id: \.self) {button in
                Button(action: {
                    self.NumberSelected = button
                    
                }) {
                    Text("\(self.buttons[button])")
                        .foregroundColor(Color("Black-White"))
                        .font(.title3)
                        .padding()
                        .background(self.NumberSelected == button ? Color("Custom Gray"): Color("White-Black"))
                    .clipShape(Capsule())}}
            
            }
            
        }
    }
        
//Continue Button           
        HStack(spacing: 15) {
        ForEach(0..<button.count, id: \.self) {button in
                Button(action: {
                    self.buttonContinue = button
                    
                }) {
                    
//Links Continue Button To Next Page
                    NavigationLink(destination: SecondView()) {
                        Text("Continue")
                    }

//'Continue' Button is Disabled if User Has Not Selected Values
                    .clipShape(Capsule())}}.disabled(!isFormValid)  
        }
        
    }
    }
}
    

CodePudding user response:

You really need to restructure your code. While your approach with @State var is working for a few choices it becomes very cumbersome to deal with as your code advances.

My solution has implications for your code other than just the exclusive selection of toggles. This issues you will have to address on your own, or ask a new question.

  • Create an enum holding your choices

     enum SelectedOption: CaseIterable{
      case memory, approaches, biopsychology
    
      var prettyDescription: String{ // used for getting a description in the view
          switch self{
          case .memory:
              return "Memory"
          case .approaches:
              return "Approaches"
          case .biopsychology:
              return "Biopsychology"
          }
      }
     }
    

I´ve done only 3 but this can be expanded without to much effort.

  • Delete the now deprected @State vars from your view and replace them with a single one:

    @State private var selectedOption: SelectedOption?
    
  • Change the VStack containing your Toggles:

      VStack{
              ForEach(SelectedOption.allCases, id: \.self){ enumCase in
                  Toggle(enumCase.prettyDescription, isOn: Binding(get: { selectedOption == enumCase }, set: { _,_ in selectedOption = enumCase }))
                                  .toggleStyle(.button)
                                  .tint(Color(red: 0.817, green: 0.065, blue: 0.287))
              }
          }
    

You are now itterating over all cases SelectedOption can contain and using a binding to determine whether the toggle should be selected or not depending on selectedOption

CodePudding user response:

Here is a solution, the short answer is to put all the related data together in a struct, then have an Array of objects that you can iterate over to create any View and easily pass the data along.

import SwiftUI
//Contains all the items for each topic
struct RevisionTopics: Identifiable, Hashable{
    let id: UUID = UUID()
    var name: String
    var color: Color
    //Add variables as needed so eveything is contained.
    //Questions, answers, etc
}
//Keeps work out of the `View`
class TopicsViewModel: ObservableObject{
    ///Holds the single selected topic
    @Published var selectedTopic: RevisionTopics? = nil
    ///Source of truth for all the topics
    @Published var topics: [RevisionTopics] = [.init(name: "Memory", color: Color(red: 0.902, green: 0.755, blue: 0.161)), .init(name: "Social Influence", color: Color(red: 0.902, green: 0.755, blue: 0.17)), .init(name: "Approaches", color: Color(red: 0.945, green: 0.442, blue: 0.022)), .init(name: "Psychopathology", color: Color(red: 0.945, green: 0.442, blue: 0.022)), .init(name: "Biopsychology", color: Color(red: 0.817, green: 0.065, blue: 0.287)), .init(name: "Attachment", color: Color(red: 0.817, green: 0.065, blue: 0.287)), .init(name: "Issues and Debates", color: Color(red: 0.399, green: 0.06, blue: 0.947)), .init(name: "Schizophrenia", color: Color(red: 0.394, green: 0.061, blue: 0.943)), .init(name: "Research Methods", color: Color(red: 0.105, green: 0.561, blue: 0.896))]
    @Published var buttons: [Int] = [10,20,30,40,50]
    @Published var numberOfQuestions: Int = 0
}

struct ContentView: View {
    @StateObject var topicsVM: TopicsViewModel = .init()
    var body: some View {
        NavigationView {
            ScrollView{
                //Topics
                //Option 1 - use picker
                Picker("Topics", selection: $topicsVM.selectedTopic){
                    Text("Select a topic").tag(nil as RevisionTopics?)
                    ForEach(topicsVM.topics){ topic in
                        Text(topic.name).tag(topic as RevisionTopics?).foregroundColor(topic.color)
                    }
                }.pickerStyle(.wheel)
                //Option 2 - keep toggles with custom binding
                ForEach(topicsVM.topics){ topic in
                    Toggle(topic.name, isOn:
                            Binding(get: {
                        topicsVM.selectedTopic == topic
                    }, set: { new in
                        if new{
                            topicsVM.selectedTopic = topic
                        }else{
                            topicsVM.selectedTopic = nil
                        }
                    })).tint(topic.color)
                }
                
                HStack(spacing: 15) {
                    ForEach(topicsVM.buttons, id: \.self) {button in
                        Button(action: {
                            topicsVM.numberOfQuestions = button
                            
                        }) {
                            Text("\(button)")
                                .foregroundColor(Color.blue)
                                .font(.title3)
                                .padding()
                                .background(topicsVM.numberOfQuestions == button ? Color.red:Color.gray)
                            .clipShape(Capsule())}}
                    
                }
                
                if let selected = topicsVM.selectedTopic, topicsVM.numberOfQuestions != 0{
                    
                    NavigationLink("Continue", destination: {
                        Text(selected.name)
                        //SecondView(topic: selected)
                    })
                }else{
                    Text("**To Continue**")
                    if topicsVM.selectedTopic == nil{
                        Text("Select a topic")
                    }
                    if topicsVM.numberOfQuestions == 0{
                        Text("Select the number of questions")
                    }
                }
                
            }
        }
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

enter image description here

  • Related