2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【SwiftUI】自作カレンダーを作ってみよう!

Last updated at Posted at 2022-02-08

こんにちは、goorerです。

今回はSwiftUI自作カレンダーの作り方をご紹介したいと思います。(昨年の知識)

以下、少々お見苦しいコードがお目に触れますがご勘弁ください。先に謝っておきます!すみません!!!!

1.曜日を表示する

まずは、核となる部分に入る前のウォーミングアップということでカレンダーの一番上にある曜日を表示してみましょう。

weekday.swift
// 曜日は日本表記でなく英語表記でもOK
let dayofweek = ["日","月","火","水","木","金","土"]
var body: some View {
    VStack {
        HStack {
            ForEach(0..<self.dayofweek.count){ index in
                ZStack {
                    RoundedRectangle(cornerRadius:5).frame(width:40,height:40)
                        .foregroundColor(Color.clear)
                    Text(dayofweek[index]) // ここで曜日を表示
                        .font(.system(size:10))
                }
            }
        }
    }
}

VStackやHStack等の説明は今回は皆さん知っているという前提で省略します。

このコードを書き込むとこうなります。綺麗に曜日が一列で表示できましたね。

CA004889-8518-4AAE-A226-1BA347D0463B.png

2.日付を取得する

カレンダーを表示することにおいて最も重要な日付を取得しましょう。今回のカレンダーは開いたら現在の日付を取得して取得した日付のカレンダーを表示する使用です。

AppDelegate.swift
class AppDelegate: UIResponder, UIApplicationDelegate {
    // 現在の日付から年のみを取得
    let year : Int = Calendar.current.component(.year, from: Date())
    // 月のみを取得
    let month : Int = Calendar.current.component(.month, from: Date())
}
// 日は今回使用しないため取得していません。使用する方は取得しましょう!
@State var year : Int = AppDelegate().year
@State var month : Int = AppDelegate().month
let dayofweek = ["日","月","火","水","木","金","土"]
var body: some View {
    VStack {
    Text("\(self.month) \(String(self.year))")
        .font(.system(size: 20))
        HStack {
            ForEach(0..<self.dayofweek.count){ index in
                ZStack {
                    RoundedRectangle(cornerRadius: 5).frame(width:40,height:40)
                        .foregroundColor(Color.clear)
                    Text(dayofweek[index])
                        .font(.system(size:10))
                }
            }
        }
    }
}

1.のコードに追加をして、年と月を表示しましょう。

952C816C-F63F-420B-A6EA-02C4CADF16E9.png

3.うるう年の判定をする

うるう年の判定もカレンダーを表示する上で重要なことです。判定のコードも短いのでサラッと紹介したいと思います。

leapYear.swift
func LeapYear(year:Int) -> Bool {
    let result =  (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0 )) ? true : false
    return result
}

判定の部分は一行で書きたかったので、今回三項演算子を使用しています。switch文でも書けると思います。

戻り値は、trueがうるう年です。

4.日付から曜日を取得する

2.で取得した日付から当月1日の曜日を取得しましょう。1日目の曜日はカレンダーを表示する上で必要です。

使用するのは、ツェラーの公式という公式です。これは指定した日付が何曜日かを算出するというものです。従って当月1日を指定すれば自ずと曜日が分かります。

zeller.swift
func DayofWeekCalc(year:Int,month:Int,day:Int) -> Int{
    var result : Int = 0
    if month == 1 || month ==  2 { //monthが1,2の時は前年として計算をする
        var changeyear : Int = year
        var changemonth : Int = month
        changeyear -= 1
        changemonth += 12
        result  = (day + (26 * (changemonth + 1))/10 + changeyear + (changeyear / 4) + (5 * (changeyear / 100)) + ((changeyear / 100)/4) + 5) % 7 as Int
    }else{
        result  = (day + (26 * (month + 1))/10 + year + (year / 4) + (5 * (year / 100)) + ((year / 100)/4) + 5) % 7 as Int
    }  
    return result
}

ポイントは、1,2月は前年の続きとして計算することです。戻り値は0~6で、それぞれ日〜土の順になっています。

5.週数を取得する

続きまして、先ほど取得した当月の週数を取得します。週数は、4~6のいずれかとなっています。

week.swift
func GetWeekNumber(year:Int,month:Int) -> Int {
    var result : Int = 0
    if CaseFourWeek(year: year, month: month){
        result = 4
    }else if CaseSixWeek(year: year, month: month){
        result = 6
    }else{
        result = 5
    }
    return result
}

週数4の場合

week.swift
private func CaseFourWeek(year:Int,month:Int) -> Bool {
    let firstdayofweek = DayofWeekCalc(year: year, month: month, day: 1)
    let result = (!LeapYear(year: year) && month == 2 && (firstdayofweek == 0)) ? true : false
    return result
}

週数6の場合

week.swift
private func CaseSixWeek(year:Int,month:Int) -> Bool{
     let firstdayofweek = DayofWeekCalc(year: year, month: month, day: 1)
     let days = DayNumber(year: year, month: month)
     let result = ((firstdayofweek == 6 && days == 30) || (firstdayofweek >= 5 && days == 31)) ? true : false
     return result
}

週数4でも6でもない場合は、週数5と決まるので判定はありません。

6.日数を取得する

最後に取得するのは、当月の日数です。日数に関しては詳しい説明は要らないと思いますので、早速コードをご覧ください。

currentMonth.swift
func DayNumber(year:Int,month:Int) -> Int{
    var result : Int = 0
        
    switch month {
    case 1,3,5,7,8,10,12:
        result = 31
    case 4,6,9,11:
        result = 30
    case 2:
        if LeapYear(year: year){
            result = 29
        }else{
            result = 28
        }
    default: break
    }   
    return result
}

7.実際にカレンダーを表示する

カレンダー表示に必要なパーツは揃ったので、そろそろ表示をしていきます。表示方法は多数あると思いますが、今回紹介するのはほんの一例です。

CalendarView

CalendarView
calendarView.swift
struct CalendarView: View {
    @State var year : Int = AppDelegate().year
    @State var month : Int = AppDelegate().month
    let dayofweek = ["日","月","火","水","木","金","土"]
    var body: some View {
        ZStack{
            VStack {
                Text("\(self.month) \(String(self.year))")
                    .font(.system(size: 20))
                Spacer().frame(height:50)
                
                HStack {
                    ForEach(0..<self.dayofweek.count){ index in
                        ZStack {
                            RoundedRectangle(cornerRadius: 5).frame(width:40,height:40)
                                .foregroundColor(Color.clear)
                            Text(dayofweek[index])
                                .font(.system(size:10))
                        }
                    }
                }
                Spacer()
                // カレンダーの月を進める・戻るボタン
                HStack {
                    Button(action: {
                        self.LastMonth()
                    }, label: {
                        Text("Back")
                    })
                    Button(action: {
                        self.NextMonth()
                    }, label: {
                        Text("Next")
                    })
                }
            }
            VStack {
                Spacer().frame(height:115)
                CalendarList(year: $year, month: $month, 
week: CalendarModel().GetWeekNumber(year: self.year, month: self.month), 
start: CalendarModel().DayofWeekCalc(year: self.year, month: self.month, day: 1), days: CalendarModel().DayNumber(year: self.year, month: self.month))
                Spacer()
            }
        }
    }
    func NextMonth(){
        if self.month != 12{
            self.month += 1
        }else if self.month == 12{
            self.year += 1
            self.month = 1
        }
    }
        
    func LastMonth(){
        if self.month != 1{
            self.month -= 1
        }else if self.month == 1{
            self.year -= 1
            self.month = 12
        }
    }
}

実際にカレンダーの中を表示しているのは下記のCalendarListです。

CalendarList

CalendarList
struct CalendarList: View {
    @Binding var year : Int
    @Binding var month : Int
    var startdaynumber : Int
    var weeknumber : Int
    var days : Int
    var middleweek : Int
    var lastweeknumber : Int
    let column = 7
    
    init(year:Binding<Int>,month:Binding<Int>,week:Int,start:Int,days:Int){
        self._year = year
        self._month = month
        self.weeknumber = week
        self.startdaynumber = start
        self.days = days
        self.middleweek = (days - (7 - start)) / 7
        self.lastweeknumber = (days - (7 - start)) % 7
    }
    var body: some View {
        // 1週
        VStack {
            HStack {
                if self.startdaynumber != 0{
                    ForEach(0..<self.startdaynumber,id:\.self){ index in
                        ZStack{
                            RoundedRectangle(cornerRadius: 5).frame(width:40,height:40)
                                .foregroundColor(Color.clear)
                            Text("")
                                .font(.system(size: 20))
                                .foregroundColor(.black)
                        }
                    }
                }
                ForEach(0..<(self.column-self.startdaynumber),id:\.self){ index in
                    ZStack{
                        RoundedRectangle(cornerRadius: 5).frame(width:40,height:40)
                            .foregroundColor(Color.clear)
                        Text("\(index+1)")
                            .font(.system(size: 20))
                            .foregroundColor(.black)
                    }
                }
            }
            // 2週
            HStack{
                ForEach(0..<self.column,id:\.self){ index in
                    ZStack{
                        RoundedRectangle(cornerRadius: 5).frame(width:40,height:40)
                            .foregroundColor(Color.clear)
                        Text("\((self.column-self.startdaynumber)+1+index)")
                            .font(.system(size: 20))
                            .foregroundColor(.black)
                    }
                }
            }
            
            // 3週
            HStack{
                ForEach(0..<self.column,id:\.self){ index in
                    ZStack{
                        RoundedRectangle(cornerRadius: 5).frame(width:40,height:40)
                            .foregroundColor(Color.clear)
                        Text("\(((7-self.startdaynumber)+1+index)+7)")
                            .font(.system(size: 20))
                            .foregroundColor(.black)
                    }
                }
            }
            
            // 4,5,6週
            if self.weeknumber == 4{
                
                HStack{
                    if self.lastweeknumber != 0{
                        ForEach(0..<self.lastweeknumber,id:\.self){ index in
                            ZStack{
                                RoundedRectangle(cornerRadius: 5).frame(width:40,height:40)
                                    .foregroundColor(Color.clear)
                                Text("\(((7-self.startdaynumber)+1+index)+14)")
                                    .font(.system(size: 20))
                                    .foregroundColor(.black)
                            }
                        }
                    }else{
                        ForEach(0..<7,id:\.self){ index in
                            ZStack{
                                RoundedRectangle(cornerRadius: 5).frame(width:40,height:40)
                                    .foregroundColor(Color.clear)
                                Text("\(((7-self.startdaynumber)+1+index)+14)")
                                    .font(.system(size: 20))
                                    .foregroundColor(.black)
                            }
                        }
                    }
                }
            }else if self.weeknumber == 5{
                HStack{
                    ForEach(0..<self.column,id:\.self){ index in
                        ZStack{
                            RoundedRectangle(cornerRadius: 5).frame(width:40,height:40)
                                .foregroundColor(Color.clear)
                            Text("\(((7-self.startdaynumber)+1+index)+14)")
                                .font(.system(size: 20))
                                .foregroundColor(.black)
                        }
                    }
                }
                HStack{
                    if self.lastweeknumber != 0{
                        ForEach(0..<self.lastweeknumber,id:\.self){ index in
                            ZStack{
                                RoundedRectangle(cornerRadius: 5).frame(width:40,height:40)
                                    .foregroundColor(Color.clear)
                                Text("\(((7-self.startdaynumber)+1+index)+21)")
                                    .font(.system(size: 20))
                                    .foregroundColor(.black)
                            }
                        }
                        
                        ForEach(0..<(7-self.lastweeknumber),id:\.self){ index in
                            ZStack{
                                RoundedRectangle(cornerRadius: 5).frame(width:40,height:40)
                                    .foregroundColor(Color.clear)
                                Text("")
                                    .font(.system(size: 20))
                                    .foregroundColor(.black)
                          
                            }
                        }
                    }else{
                        ForEach(0..<7,id:\.self){ index in
                            ZStack{
                                RoundedRectangle(cornerRadius: 5).frame(width:40,height:40)
                                    .foregroundColor(Color.clear)
                                Text("\(((7-self.startdaynumber)+1+index)+21)")
                                    .font(.system(size: 20))
                                    .foregroundColor(.black)
                            }
                        }
                    }
                    
                }
            }else if self.weeknumber == 6{
                HStack{
                    ForEach(0..<self.column,id:\.self){ index in
                        ZStack{
                            RoundedRectangle(cornerRadius: 5).frame(width:40,height:40)
                                .foregroundColor(Color.clear)
                            Text("\(((7-self.startdaynumber)+1+index)+14)")
                                .font(.system(size: 20))
                                .foregroundColor(.black)
                        }
                    }
                }
                HStack{
                    ForEach(0..<self.column,id:\.self){ index in
                        ZStack{
                            RoundedRectangle(cornerRadius: 5).frame(width:40,height:40)
                                .foregroundColor(Color.clear)
                            Text("\(((7-self.startdaynumber)+1+index)+21)")
                                .font(.system(size: 20))
                                .foregroundColor(.black)
                        }
                    }
                }
                HStack{
                    if self.lastweeknumber != 0{
                        ForEach(0..<self.lastweeknumber,id:\.self){ index in
                            ZStack{
                                RoundedRectangle(cornerRadius: 5).frame(width:40,height:40)
                                    .foregroundColor(Color.clear)
                                Text("\(((7-self.startdaynumber)+1+index)+28)")
                                    .font(.system(size: 20))
                                    .foregroundColor(.black)
                            }
                        }
                        if self.lastweeknumber != 0{
                            ForEach(0..<(7-self.lastweeknumber),id:\.self){ index in
                                ZStack{
                                    RoundedRectangle(cornerRadius: 5).frame(width:40,height:40)
                                        .foregroundColor(Color.clear)
                                    Text("")
                                        .font(.system(size: 20))
                                        .foregroundColor(.black)
                                }
                            }
                        }
                    }else{
                        ForEach(0..<self.lastweeknumber,id:\.self){ index in
                            ZStack{
                                RoundedRectangle(cornerRadius: 5).frame(width:40,height:40)
                                    .foregroundColor(Color.clear)
                                Text("\(((7-self.startdaynumber)+1+index)+28)")
                                    .font(.system(size: 20))
                                    .foregroundColor(.black)
                            }
                        }
                    }
                }
            }
        }
    }
}

以上で完成です!
長々と拙いソースをご覧頂きありがとうございました。
至らない部分が多いと思いますが、温かい目で見ていただけると幸いです。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?