日時を選択する

Published by @SoNiceInfo at 6/24/2020


日時を選択するにはDatePickerを使います

DatePickerの基本的な使い方

日時を格納する変数はDate型です。
displayedComponentsに必要な項目を指定します。

//
//  ContentView.swift
//

import SwiftUI

struct ContentView : View {
    @State var selectedDate = Date()

    var body: some View {
        VStack {
            DatePicker(selection: $selectedDate, displayedComponents: .hourAndMinute) {
                Text("Hour\nMinute")
            }

            DatePicker(selection: $selectedDate, displayedComponents: .date) {
                Text("Date")
            }

            DatePicker(selection: $selectedDate, displayedComponents: [.date, .hourAndMinute]) {
                Text("Both")
            }

            Text(selectedDate.description)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
datepickerの基本的な使い方

DatePickerのLabelを非表示にする

DatePickerの標準のLabelを利用すると左のおかしな場所にラベルが出てきます。
.labelsHidden()を使うことでLabelを非表示にできます。

//
//  ContentView.swift
//

import SwiftUI

struct ContentView : View {
    @State var selectedDate = Date()

    var body: some View {
        VStack {
            DatePicker(selection: $selectedDate, displayedComponents: .hourAndMinute) {
                Text("Hour\nMinute")
            }.labelsHidden()

            DatePicker(selection: $selectedDate, displayedComponents: .date) {
                Text("Date")
            }.labelsHidden()

            DatePicker(selection: $selectedDate, displayedComponents: [.date, .hourAndMinute]) {
                Text("Both")
            }.labelsHidden()

            Text(selectedDate.description)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
datepickerのLabelを非表示にする方法

日時のフォーマットを整える

日時の形式をDateFormatterを利用して変更することができます。
日付・時間の有無, 日付の出力方法, タイムゾーンを設定できます。

//
//  ContentView.swift
//

import SwiftUI

struct ContentView : View {
    @State var selectedDate = Date()
    @Environment(\.timeZone) var timeZone

    var dateFormat: DateFormatter {
        let dformat = DateFormatter()
        dformat.dateStyle = .medium
        dformat.timeStyle = .medium
        dformat.dateFormat = "yyyy-MM-dd HH:mm"
        dformat.timeZone  = timeZone
        return dformat
    }

    var body: some View {
        VStack {
            DatePicker(selection: $selectedDate, displayedComponents: .hourAndMinute) {
                Text("Hour\nMinute")
            }.labelsHidden()

            DatePicker(selection: $selectedDate, displayedComponents: .date) {
                Text("Date")
            }.labelsHidden()

            DatePicker(selection: $selectedDate, displayedComponents: [.date, .hourAndMinute]) {
                Text("Both")
            }.labelsHidden()

            Text("Original: \(selectedDate.description)")
            Text("DateFormat: \(selectedDate, formatter: dateFormat)")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
dateformatterを使って形式を変更した結果

時間を等間隔にしたDatePickerを作る

UIKitのUIDatePickerを使うと時間を等間隔にしたDatePickerを作ることができます。
SwiftUIからUIKitのモジュールを使用する方法は地図を表示するで解説しているので参考にしてください。
時間を等間隔にするポイントはpicker.minuteInterval = MINUTESです。MINUTESの部分を変えることで間隔を変更することができます。

//
//  ContentView.swift
//

import SwiftUI

struct ContentView: View {
    @State var selectedDate = Date()
    @Environment(\.timeZone) var timeZone

    var dateFormat: DateFormatter {
        let dformat = DateFormatter()
        dformat.dateStyle = .medium
        dformat.timeStyle = .medium
        dformat.dateFormat = "yyyy-MM-dd HH:mm"
        dformat.timeZone  = timeZone
        return dformat
    }

    var body: some View {
        VStack {
            MyDatePicker(selection: $selectedDate, minuteInterval: 5, displayedComponents: .hourAndMinute)
            MyDatePicker(selection: $selectedDate, minuteInterval: 30, displayedComponents: [.hourAndMinute, .date])

            Text("Original: \(selectedDate.description)")
            Text("DateFormat: \(selectedDate, formatter: dateFormat)")
        }
    }

}

struct MyDatePicker: UIViewRepresentable {

    @Binding var selection: Date
    let minuteInterval: Int
    let displayedComponents: DatePickerComponents

    func makeCoordinator() -> Coordinator {
        return Coordinator(self)
    }

    func makeUIView(context: UIViewRepresentableContext<MyDatePicker>) -> UIDatePicker {
        let picker = UIDatePicker()
        // listen to changes coming from the date picker, and use them to update the state variable
        picker.addTarget(context.coordinator, action: #selector(Coordinator.dateChanged), for: .valueChanged)
        picker.minuteInterval = minuteInterval
        return picker
    }

    func updateUIView(_ picker: UIDatePicker, context: UIViewRepresentableContext<MyDatePicker>) {
        picker.date = selection

        switch displayedComponents {
        case .hourAndMinute:
            picker.datePickerMode = .time
        case [.hourAndMinute, .date]:
            picker.datePickerMode = .dateAndTime
        default:
            break
        }
    }

    class Coordinator {
        let datePicker: MyDatePicker
        init(_ datePicker: MyDatePicker) {
            self.datePicker = datePicker
        }

        @objc func dateChanged(_ sender: UIDatePicker) {
            datePicker.selection = sender.date
        }
    }
}
数分間隔のDatePickerを作成する

参考

DatePicker - SwiftUI | Apple Developer Documentation
DateFormatterの使い方まとめ - Qiita
swift - DatePicker using Time-Interval in SwiftUI - Stack Overflow

    アプリをリリースしました!

    ToDoアプリ

    iCloudを利用したデバイス間データ共有、ダークモードに対応しています。

    リリースしたToDoアプリのスクリーンショット

    IPアドレス履歴保存アプリ

    取得したIPアドレスを確認、位置情報とともに保存できます。

    リリースしたIPアドレス保存アプリのスクリーンショット