📆

DatePickerとToggleを使ってDate?型を扱う

2022/08/31に公開

概要

  • Date?を引数とし、それをDatePickerとToggleを持つViewで扱いたい。
  • つまりは下記のような動作をさせたい。

image

参考

実装

  • DatePickerとToggle用として、View自身にprivateなプロパティをもたせる
  • onAppearで初期値をそれらに設定する。
  • onChange(of:perform:) を使い、各アクションに対してプロパティを更新させる。
struct DateCellView: View {

    let title: String
    @Binding var date: Date?
    @State private var selectedDate = Date()
    @State private var isValid = false
    
    private let dateRange: ClosedRange<Date> = {
        return (Date.twitterStartedAt ?? Date())
        ...
        Date()
    }()

    var body: some View {
        HStack {
            Text(title)
            Spacer()
            DatePicker("",
                       selection: $selectedDate,
                       in: dateRange,
                       displayedComponents: [.date]
            )
            .onChange(of: selectedDate) { newValue in
                if isValid {
                    date = selectedDate
                }
            }
            
            Toggle(isOn: $isValid) {}
                .labelsHidden()
                .onChange(of: isValid) { isOn in
                    date = isOn ? selectedDate : nil
                }
        }
        .onAppear {
            if let date = date {
                selectedDate = date
                isValid = true
            }
        }
    }
}
extension Date {
    static var twitterStartedAt: Date? {
        let calendar = Calendar.current
        let startComponents = DateComponents(year: 2006, month: 7, day: 15)  // Twitterのサービス開始日時
        return calendar.date(from: startComponents)
    }
}
  • 呼び出し例
struct DebugDateCellView: View {
        
    @State private var date: Date? = nil

    var body: some View {
        VStack {
            HStack {
                Text("Current Value: ")
                if let date = date {
                    Text(date, style: .date)
                } else {
                    Text("isEmpty")
                }
            }
            
            DateCellView(title: "Title", date: $date)
                .frame(width: 300)
            Spacer()
        }
        .onAppear() {
            // 初期値を設定する場合
//            date = Date.twitterStartedAt
        }
    }
}

Discussion