Search code examples
xcodeswiftuitoggledropdowntimepicker

I am trying to create a toggle containing a dropdown with both a start and stop time range picker. Then stack 7 toggles representing days of the week


Here are the issues

  1. The code throws a compiling error when more than 5 toggles are repeated.
  2. Need to close picker without toggling to off position.
  3. Selected start and stop time show in toggle bar but then it will reset

Current Code: Don't laugh too hard ;-)

import SwiftUI

struct ContentView: View {
@State private var toggleStateM = false
@State private var toggleStateT = false
@State private var toggleStateW = false
@State private var toggleStateTH = false
@State private var toggleStateF = false
//@State private var toggleStateST = false
//@State private var toggleStateSN = false

@State private var startSentryTimeM = Date()
@State private var endSentryTimeM = Date()
@State private var startSentryTimeT = Date()
@State private var endSentryTimeT = Date()
@State private var startSentryTimeW = Date()
@State private var endSentryTimeW = Date()
@State private var startSentryTimeTH = Date()
@State private var endSentryTimeTH = Date()
@State private var startSentryTimeF = Date()
@State private var endSentryTimeF = Date()
// @State private var startSentryTimeST = Date()
// @State private var endSentryTimeST = Date()
// @State private var startSentryTimeSN = Date()
// @State private var endSentryTimeSN = Date()

var body: some View {
    VStack(alignment: .center) {

//Monday************

        Toggle(isOn: $toggleStateM) {
            HStack{
            Spacer()
                Text("Monday")
            Spacer()
                Text(" \(startSentryTimeM.formatted(date: .omitted, time: .shortened))")
                Text("to")
                Text(" \(endSentryTimeM.formatted(date: .omitted, time: .shortened))")
            Spacer()
                }
        
            }
        .frame(width: 350, height: 35, alignment: .center)
        .padding(.leading, 20)

        if toggleStateM{
          HStack{
              
              VStack(alignment: .center){
                Text("Sentry Start Time")
                DatePicker("", selection: $startSentryTimeM, displayedComponents: .hourAndMinute)
                      .labelsHidden()
                      
                      
                }
                .padding(.trailing, 20)
                
              VStack(alignment: .center){
                
                  Text("Sentry End Time")
                  DatePicker("", selection: $endSentryTimeM, displayedComponents: .hourAndMinute)
                      .labelsHidden()
                      
                }
            }
      
        }
    

//Tuesday******************

        Toggle(isOn: $toggleStateT) {
            HStack{
            Spacer()
                Text("Tuesday")
            Spacer()
                Text(" \(startSentryTimeT.formatted(date: .omitted, time: .shortened))")
                Text("to")
                Text(" \(endSentryTimeT.formatted(date: .omitted, time: .shortened))")
            Spacer()
                    
          }
        }
        .frame(width: 350, height: 35, alignment: .center)
        .padding(.leading, 20)


          if toggleStateT{
            HStack{
            
                VStack(alignment: .center){
                    Text("Sentry Start Time")
                    DatePicker("", selection: $startSentryTimeT, displayedComponents: .hourAndMinute)
                        .labelsHidden()
            }
            .padding(.trailing, 20)
           
                VStack(alignment: .center){
                Text("Sentry End Time")
              DatePicker("", selection: $endSentryTimeT, displayedComponents: .hourAndMinute)
            .labelsHidden()
            }
            .padding(.leading, 20)
            }
          }

//Wednesday*************

          Toggle(isOn: $toggleStateW) {
            HStack{
            Spacer()
              }
              Text("Wedsnesday")
            Spacer()
              Text(" \(startSentryTimeW.formatted(date: .omitted, time: .shortened))")
              Text("to")
              Text(" \(endSentryTimeW.formatted(date: .omitted, time: .shortened))")
           Spacer()
          }

          .frame(width: 350, height: 35, alignment: .center)
          .padding(.leading, 20)
         
          
        if toggleStateW{
          HStack{
            
              VStack(alignment: .center){
            Text("Sentry Start Time")
            DatePicker("", selection: $startSentryTimeW, displayedComponents: .hourAndMinute)
          .labelsHidden()
            }.padding(.trailing, 20)
           
              VStack(alignment: .center){
                Text("Sentry End Time")
              DatePicker("", selection: $endSentryTimeW, displayedComponents: .hourAndMinute)
            .labelsHidden()
            }
            .padding(.leading, 20)
          }
      }

//Thursday************

            Toggle(isOn: $toggleStateTH) {
                HStack{
                Spacer()
                    Text("Thursday")
                Spacer()
                    Text(" \(startSentryTimeTH.formatted(date: .omitted, time: .shortened))")
                    Text("to")
                    Text(" \(endSentryTimeTH.formatted(date: .omitted, time: .shortened))")
                Spacer()
                    }
                }
            .frame(width: 350, height: 35, alignment: .center)
            .padding(.leading, 20)

            if toggleStateTH{
              HStack{
                  
                  VStack(alignment: .center){
                    Text("Sentry Start Time")
                    DatePicker("", selection: $startSentryTimeTH, displayedComponents: .hourAndMinute)
                          .labelsHidden()
                    }
                    .padding(.trailing, 20)
                  VStack(alignment: .center){
                    Text("Sentry End Time")
                      DatePicker("", selection: $endSentryTimeTH, displayedComponents: .hourAndMinute)
                          .labelsHidden()
                    }
                    .padding(.leading, 20)
                }
            }

//Friday***************

            Toggle(isOn: $toggleStateF) {
                HStack{
                Spacer()
                    Text("Friday     ")
                Spacer()
                    Text(" \(startSentryTimeF.formatted(date: .omitted, time: .shortened))")
                    Text("to")
                    Text(" \(endSentryTimeF.formatted(date: .omitted, time: .shortened))")
                Spacer()
                    }
                }
            .frame(width: 350, height: 35, alignment: .center)
            .padding(.leading, 20)

            if toggleStateF{
              HStack{
                  
                  VStack(alignment: .center){
                    Text("Sentry Start Time")
                    DatePicker("", selection: $startSentryTimeF, displayedComponents: .hourAndMinute)
                          .labelsHidden()
                    }
                    .padding(.trailing, 20)
                  VStack(alignment: .center){
                    Text("Sentry End Time")
                      DatePicker("", selection: $endSentryTimeF, displayedComponents: .hourAndMinute)
                          .labelsHidden()
                    }
                    .padding(.leading, 20)
                  
                }
            }

//Saturday****************

       /*   Toggle(isOn: $toggleStateST) {
            
            Text("Saturday" )
           
          }
          .padding(.bottom, -5)
          .padding(.leading, 15)
          .padding(.trailing, 20)
          if toggleStateM{
          HStack{
              
            VStack{
            Text("Sentry Start Time")
            DatePicker("", selection: $startSentryTimeST, displayedComponents: .hourAndMinute)
          .labelsHidden()
            }.padding(.trailing, 20)
           
            VStack{
                Text("Sentry End Time")
              DatePicker("", selection: $endSentryTimeST, displayedComponents: .hourAndMinute)
            .labelsHidden()
            }.padding(.leading, 20)
          }
        */
      }
      }
     

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
}

}


Solution

  • I've tried to reduce your code down into manageable chunks, there may be bugs in what I've written.

    I've moved your individual days to be data driven, that way we can just repeat views, but with different bindings

    enter image description here

    import SwiftUI
    import Combine
    
    class Sentry: ObservableObject {
      @Published var isOn: Bool = false
      let day: String
      @Published var startTime: Date = Date()
      @Published var finishTime: Date = Date()
      
      init(isOn: Bool, day: String, startTime: Date = Date(), finishTime: Date = Date()) {
        self.isOn = isOn
        self.day = day
        self.startTime = startTime
        self.finishTime = finishTime
      }
    }
    
    extension Sentry: Hashable {
      
      static func == (lhs: Sentry, rhs: Sentry) -> Bool {
        lhs.day == rhs.day
      }
      
      func hash(into hasher: inout Hasher) {
        hasher.combine(self.day)
      }
      
    }
    
    class Manager: ObservableObject {
      
      var cancellables = Set<AnyCancellable>()
      @Published var sentries: [Sentry] = []
      
      init(sentries: [Sentry]) {
        self.sentries = sentries
        
        self.sentries.forEach({ sentry in
          sentry.objectWillChange
            .receive(on: DispatchQueue.main).sink { (_) in
                self.objectWillChange.send()
            }
            .store(in: &cancellables)
        })
            
      }
    }
    
    struct TestView: View {
      
      @EnvironmentObject var manager: Manager
      
      var body: some View {
        
        VStack(alignment: .center) {
          ForEach(manager.sentries, id: \.self) { sentry in
            ToggleSwitch(sentry: sentry)
            if sentry.isOn {
              DatePickers(sentry: sentry)
            }
          }
        }
      }
      
    }
    
    struct ToggleSwitch: View {
      
      @ObservedObject var sentry: Sentry
      
      var body: some View {
        Toggle(isOn: $sentry.isOn) {
          HStack{
            Spacer()
            Text(sentry.day)
            Spacer()
            Text(" \(sentry.startTime.formatted(date: .omitted, time: .shortened))")
            Text("to")
            Text(" \(sentry.finishTime.formatted(date: .omitted, time: .shortened))")
            Spacer()
          }
        }
        .frame(width: 350, height: 35, alignment: .center)
        .padding(.leading, 20)
      }
      
    }
    
    struct DatePickers: View {
      
      @ObservedObject var sentry: Sentry
      
      var body: some View {
        HStack {
          VStack(alignment: .center){
            Text("Sentry Start Time")
            DatePicker("", selection: $sentry.startTime, displayedComponents: .hourAndMinute)
              .labelsHidden()
          }
          .padding(.trailing, 20)
          
          VStack(alignment: .center){
            Text("Sentry End Time")
            DatePicker("", selection: $sentry.finishTime, displayedComponents: .hourAndMinute)
              .labelsHidden()
          }
        }
      }
      
    }
    
    struct TestView_Previews: PreviewProvider {
      
      static var sentryMon = Sentry(isOn: true, day: "Monday")
      static var sentryTue = Sentry(isOn: false, day: "Tuesday")
      static var sentryWed = Sentry(isOn: false, day: "Wednesday")
      static var sentryThu = Sentry(isOn: false, day: "Thrusday")
      static var sentryFri = Sentry(isOn: false, day: "Friday")
      static var sentrySat = Sentry(isOn: false, day: "Saturday")
      static var sentrySun = Sentry(isOn: false, day: "Sunday")
      
      static var previews: some View {
        TestView()
          .environmentObject(Manager(sentries: [sentryMon, sentryTue, sentryWed, sentryThu, sentryFri, sentrySat, sentrySun]))
      }
    }