Skip to content

Instantly share code, notes, and snippets.

@alahdal
Created October 6, 2020 17:12
Show Gist options
  • Select an option

  • Save alahdal/deb37df908be07d2a64456229276665e to your computer and use it in GitHub Desktop.

Select an option

Save alahdal/deb37df908be07d2a64456229276665e to your computer and use it in GitHub Desktop.

Revisions

  1. alahdal created this gist Oct 6, 2020.
    246 changes: 246 additions & 0 deletions Calendar.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,246 @@
    //Orginal code from: https://gist.github.com/mecid/f8859ea4bdbd02cf5d440d58e936faec
    //I just made some modification in appearnce, show monthly navigator and weekdays.

    import SwiftUI

    struct ContentView: View {

    @Environment(\.calendar) var calendar
    private var year: DateInterval {
    calendar.dateInterval(of: .month, for: Date())!
    }

    var body: some View {
    VStack{
    CalendarView(interval: self.year) { date in
    Text("30")
    .hidden()
    .padding(8)
    .background(1 != 1 ? Color.red : Color.blue) // Make your logic
    .clipShape(Rectangle())
    .cornerRadius(4)
    .padding(4)
    .overlay(
    Text(String(self.calendar.component(.day, from: date)))
    .foregroundColor(Color.black)
    .underline(2 == 2) //Make your own logic
    )
    }
    Spacer()
    }
    }
    }


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




    fileprivate extension DateFormatter {
    static var month: DateFormatter {
    let formatter = DateFormatter()
    formatter.dateFormat = "MMMM"
    return formatter
    }

    static var monthAndYear: DateFormatter {
    let formatter = DateFormatter()
    formatter.dateFormat = "MMMM yyyy"
    return formatter
    }
    }

    fileprivate extension Calendar {
    func generateDates(
    inside interval: DateInterval,
    matching components: DateComponents
    ) -> [Date] {
    var dates: [Date] = []
    dates.append(interval.start)

    enumerateDates(
    startingAfter: interval.start,
    matching: components,
    matchingPolicy: .nextTime
    ) { date, _, stop in
    if let date = date {
    if date < interval.end {
    dates.append(date)
    } else {
    stop = true
    }
    }
    }

    return dates
    }
    }

    struct WeekView<DateView>: View where DateView: View {
    @Environment(\.calendar) var calendar

    let week: Date
    let content: (Date) -> DateView

    init(week: Date, @ViewBuilder content: @escaping (Date) -> DateView) {
    self.week = week
    self.content = content
    }

    private var days: [Date] {
    guard
    let weekInterval = calendar.dateInterval(of: .weekOfYear, for: week)
    else { return [] }
    return calendar.generateDates(
    inside: weekInterval,
    matching: DateComponents(hour: 0, minute: 0, second: 0)
    )
    }

    var body: some View {
    HStack {
    ForEach(days, id: \.self) { date in
    HStack {
    if self.calendar.isDate(self.week, equalTo: date, toGranularity: .month) {
    self.content(date)
    } else {
    self.content(date).hidden()
    }
    }
    }
    }
    }
    }

    struct MonthView<DateView>: View where DateView: View {
    @Environment(\.calendar) var calendar

    @State private var month: Date
    let showHeader: Bool
    let content: (Date) -> DateView

    init(
    month: Date,
    showHeader: Bool = true,
    localizedWeekdays: [String] = [],
    @ViewBuilder content: @escaping (Date) -> DateView
    ) {
    self._month = State(initialValue: month)
    self.content = content
    self.showHeader = showHeader
    }

    private var weeks: [Date] {
    guard
    let monthInterval = calendar.dateInterval(of: .month, for: month)
    else { return [] }
    return calendar.generateDates(
    inside: monthInterval,
    matching: DateComponents(hour: 0, minute: 0, second: 0, weekday: calendar.firstWeekday)
    )
    }

    func changeDateBy(_ months: Int) {
    if let date = Calendar.current.date(byAdding: .month, value: months, to: month) {
    self.month = date
    }
    }

    private var header: some View {
    let component = calendar.component(.month, from: month)
    let formatter = component == 1 ? DateFormatter.monthAndYear : .month
    return HStack{
    Text(formatter.string(from: month))
    .font(.title)
    .padding(.horizontal)
    Spacer()
    HStack{
    Group{
    Button(action: {
    self.changeDateBy(-1)
    }) {
    Image(systemName: "chevron.left.square") //
    .resizable()
    }
    Button(action: {
    self.month = Date()
    }) {
    Image(systemName: "dot.square")
    .resizable()
    }
    Button(action: {
    self.changeDateBy(1)
    }) {
    Image(systemName: "chevron.right.square") //"chevron.right.square"
    .resizable()
    }
    }
    .foregroundColor(Color.blue)
    .frame(width: 25, height: 25)

    }
    .padding(.trailing, 20)
    }
    }

    var body: some View {
    VStack {
    if showHeader {
    header
    }
    HStack{
    ForEach(0..<7, id: \.self) {index in
    Text("30")
    .hidden()
    .padding(8)
    .clipShape(Circle())
    .padding(.horizontal, 4)
    .overlay(
    Text(getWeekDaysSorted()[index].uppercased()))
    }
    }

    ForEach(weeks, id: \.self) { week in
    WeekView(week: week, content: self.content)
    }
    }
    }

    func getWeekDaysSorted() -> [String]{
    let weekDays = Calendar.current.shortWeekdaySymbols
    let sortedWeekDays = Array(weekDays[Calendar.current.firstWeekday - 1 ..< Calendar.current.shortWeekdaySymbols.count] + weekDays[0 ..< Calendar.current.firstWeekday - 1])
    return sortedWeekDays
    }
    }

    struct CalendarView<DateView>: View where DateView: View {
    @Environment(\.calendar) var calendar

    let interval: DateInterval
    let content: (Date) -> DateView

    init(interval: DateInterval, @ViewBuilder content: @escaping (Date) -> DateView) {
    self.interval = interval
    self.content = content
    }

    private var months: [Date] {
    calendar.generateDates(
    inside: interval,
    matching: DateComponents(day: 1, hour: 0, minute: 0, second: 0)
    )
    }

    var body: some View {

    ForEach(months, id: \.self) { month in
    MonthView(month: month, content: self.content)
    }

    }
    }