【iOS】【swift】カレンダーを作ってみる

![ss.jpg](https://qiita-image-store.s3.amazonaws.com/0/35343/d2849f2f-ca23-9204-c76c-84478dce1def.jpeg)

↑のようにスワイプで切り替えれるようなカレンダーを。
ひとまずswiftの試しが主目的ですが、swiftらしくない部分もあるかもしれません。

試し試しで実装した所があるのでベストな実装とは思えないので、
もう少し暖めようかと思ったのですが、ひとまずちょっと公開してみることにしました。

Xcode 6.1.1にて実装しました。

##1.構成


・各日を表示するView(DayViewとします。)
・各月を表示するView(MonthViewとします。)
・月を切り替えるためのView(CalenderViewとします。)

この3つのViewを扱うようにします。



##2.DayViewについて

曜日に合わせて表示を切り替えるなど想定されるため、
year/month/day/week
の四つの情報を取得するようにしています。

```swift


class DayView: UIView {
    
    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    init(frame:CGRect,year:Int,month:Int,day:Int,weekday:Int){
        super.init(frame: frame)
        var dayWidth:Int = Int( (UIScreen.mainScreen().bounds.size.width) / 7.0 )
        var dayHeight:CGFloat = 30
        var dayLabel:UILabel = UILabel(frame: CGRectMake(0, 0, CGFloat(dayWidth),dayHeight))
        dayLabel.textAlignment = NSTextAlignment.Center
        dayLabel.text = String(format:"%02d", day)
        if weekday == 1 {
            //日曜日は赤
            dayLabel.textColor = UIColor.redColor()
        } else if weekday == 7 {
            //土曜日は青
            dayLabel.textColor = UIColor.blueColor()
        }
        self.addSubview(dayLabel)
    }
}

```


##3.MonthViewについて

その月の最終日の取得や曜日、第何週目かの取得を行い、
DayViewを表示させています。

ざっくりですが、各DayViewの横幅は、frameのwidthを7で割った商を扱っています。


```swift


class MonthView: UIView {

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    init(frame: CGRect,year:Int,month:Int) {
        super.init(frame:frame)
        self.setUpDays(year,month:month)
    }

    func setUpDays(year:Int,month:Int){
        
        var subViews:[UIView] = self.subviews as [UIView]
        for view in subViews {
            if view.isKindOfClass(DayView) {
                view.removeFromSuperview()
            }
        }
        
        var day:Int? = self.getLastDay(year,month:month);
        var dayWidth:Int = Int( frame.size.width / 7.0 )
        var dayHeight:Int = dayWidth + 5
        if day != nil {
            //初日の曜日を取得
            var weekday:Int = self.getWeekDay(year,month: month,day:1)
            for var i:Int = 0; i < day!;i++ {
                var week:Int    = self.getWeek(year,month: month,day:i+1)
                var x:Int       = ((weekday - 1 ) * (dayWidth));
                var y:Int       = (week-1) * dayHeight
                var frame:CGRect = CGRectMake(CGFloat(x),
                    CGFloat(y),
                    CGFloat(dayWidth),
                    CGFloat(dayHeight)
                );
                
                var dayView:DayView = DayView(frame: frame, year:year,month:month,day:i+1,weekday:weekday)
                self.addSubview(dayView)
                weekday++
                if weekday > 7 {
                    weekday = 1
                }
                
            }
        }
    }
    
    //その月の最終日の取得
    func getLastDay(var year:Int,var month:Int) -> Int?{
        var dateFormatter:NSDateFormatter = NSDateFormatter();
        dateFormatter.dateFormat = "yyyy/MM/dd";
        if month == 12 {
            month = 0
            year++
        }
        var targetDate:NSDate? = dateFormatter.dateFromString(String(format:"%04d/%02d/01",year,month+1));
        if targetDate != nil {
            //月初から一日前を計算し、月末の日付を取得
            var orgDate = NSDate(timeInterval:(24*60*60)*(-1), sinceDate: targetDate!)
            var str:String = dateFormatter.stringFromDate(orgDate)
            //lastPathComponentを利用するのは目的として違う気も。。
            return str.lastPathComponent.toInt();
        }
        
        return nil;
    }
    
    //曜日の取得
    func getWeek(year:Int,month:Int,day:Int) ->Int{
        var dateFormatter:NSDateFormatter = NSDateFormatter();
        dateFormatter.dateFormat = "yyyy/MM/dd";
        var date:NSDate? = dateFormatter.dateFromString(String(format:"%04d/%02d/%02d",year,month,day));
        if date != nil {
            var calendar:NSCalendar = NSCalendar.currentCalendar()
            var dateComp:NSDateComponents = calendar.components(NSCalendarUnit.WeekOfMonthCalendarUnit, fromDate: date!)
            return dateComp.weekOfMonth;
        }
        return 0;
    }
   
    //第何週の取得
    func getWeekDay(year:Int,month:Int,day:Int) ->Int{
        var dateFormatter:NSDateFormatter = NSDateFormatter();
        dateFormatter.dateFormat = "yyyy/MM/dd";
        var date:NSDate? = dateFormatter.dateFromString(String(format:"%04d/%02d/%02d",year,month,day));
        if date != nil {
            var calendar:NSCalendar = NSCalendar.currentCalendar()
            var dateComp:NSDateComponents = calendar.components(NSCalendarUnit.WeekdayCalendarUnit, fromDate: date!)
            return dateComp.weekday;
        }
        return 0;
    }
    

}

```

##4.CalenderViewについて

こちらでは、ScrollViewを配置し、その上に3つのMonthViewを配置するのみとしています。
この3つの表示をスクロールに合わせて表示を切り替えるような形で想定しています。

こちら参考させていただきました。
http://cocoadays.blogspot.jp/2010/09/1.html

```swift

class CalenderView: UIView,UIScrollViewDelegate{

    var currentYear:Int = 0
    var currentMonth:Int = 0
    var currentDay:Int = 0
    var scrollView:UIScrollView!
    var prevMonthView:MonthView!
    var currentMonthView:MonthView!
    var nextMonthView:MonthView!
    
    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override init(frame:CGRect){
        super.init(frame: frame)
        
        var dateFormatter:NSDateFormatter = NSDateFormatter();
        dateFormatter.dateFormat = "yyyy/MM/dd";
        var dateString:String = dateFormatter.stringFromDate(NSDate());
        var dates:[String] = dateString.componentsSeparatedByString("/")
        currentYear  = dates[0].toInt()!
        currentMonth = dates[1].toInt()!
        
        scrollView = UIScrollView(frame: self.bounds)
        scrollView.backgroundColor = UIColor.clearColor()
        scrollView.contentSize   = CGSizeMake(frame.size.width *  3.0,frame.size.height);
        scrollView.contentOffset = CGPointMake(frame.size.width , 0.0);
        scrollView.delegate = self;
        scrollView.pagingEnabled = true;
        scrollView.showsHorizontalScrollIndicator = false;
        scrollView.showsVerticalScrollIndicator = false;
        scrollView.scrollsToTop = false;
        
        
        self.addSubview(scrollView)
        
        
        currentMonthView = MonthView(frame: CGRectMake(frame.size.width, 0, frame.size.width,frame.size.height),
            year:currentYear,month:currentMonth)
        
        //翌月
        var ret = self.getNextYearAndMonth()
        nextMonthView =  MonthView(frame: CGRectMake(frame.size.width * 2.0, 0, frame.size.width,frame.size.height),
            year:ret.year,month:ret.month)
        
        //前月
        ret = self.getPrevYearAndMonth()
        prevMonthView = MonthView(frame: CGRectMake(0.0, 0, frame.size.width,frame.size.height),
            year:ret.year,month:ret.month)
        
        scrollView.addSubview(currentMonthView);
        scrollView.addSubview(nextMonthView);
        scrollView.addSubview(prevMonthView);

    }
    
    func scrollViewDidScroll(scrollView:UIScrollView)
    {
        var pos:CGFloat  = scrollView.contentOffset.x / scrollView.bounds.size.width
        var deff:CGFloat = pos - 1.0
        if fabs(deff) >= 1.0 {
            if (deff > 0) {
                self.showNextView()
            } else {
                self.showPrevView()
            }
        }
    }
    
    func showNextView (){
        currentMonth++;
        if( currentMonth > 12 ){
            currentMonth = 1;
            currentYear++;
        }
        var tmpView:MonthView = currentMonthView
        currentMonthView = nextMonthView
        nextMonthView    = prevMonthView
        prevMonthView    = tmpView

        var ret = self.getNextYearAndMonth()
        nextMonthView.setUpDays(ret.year, month:ret.month)
        
        self.resetContentOffSet()
        
    }
    
    func showPrevView () {
        currentMonth--
        if( currentMonth == 0 ){
            currentMonth = 12
            currentYear--
        }

        var tmpView:MonthView = currentMonthView
        currentMonthView = prevMonthView
        prevMonthView    = nextMonthView
        nextMonthView    = tmpView
        var ret = self.getPrevYearAndMonth()
        prevMonthView.setUpDays(ret.year, month:ret.month)

        //position調整
        self.resetContentOffSet()
    }
    
    
    func resetContentOffSet () {
        //position調整
        prevMonthView.frame = CGRectMake(0, 0, frame.size.width,frame.size.height)
        currentMonthView.frame = CGRectMake(frame.size.width, 0, frame.size.width,frame.size.height)
        nextMonthView.frame = CGRectMake(frame.size.width * 2.0, 0, frame.size.width,frame.size.height)
        
        var scrollViewDelegate:UIScrollViewDelegate = scrollView.delegate!
        scrollView.delegate = nil
        //delegateを呼びたくないので
        scrollView.contentOffset = CGPointMake(frame.size.width , 0.0);
        scrollView.delegate = scrollViewDelegate
        
    }
    
    func getNextYearAndMonth () -> (year:Int,month:Int){
        var next_year:Int = currentYear
        var next_month:Int = currentMonth + 1
        if next_month > 12 {
            next_month=1
            next_year++
        }
        return (next_year,next_month)
    }
    func getPrevYearAndMonth () -> (year:Int,month:Int){
        var prev_year:Int = currentYear
        var prev_month:Int = currentMonth - 1
        if prev_month == 0 {
            prev_month = 12
            prev_year--
        }
        return (prev_year,prev_month)
    }
    
}


```

##5 CalenderViewの利用

ViewControllerにて利用する場合は以下のような形を想定しています。

```swift

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        var calenderView:CalenderView = CalenderView(frame: CGRectMake(0, 20,
            UIScreen.mainScreen().bounds.size.width, 500));
        self.view.addSubview(calenderView)
    }
}

```

(縦幅は適当です。。


ソースはこちらに
https://github.com/kitanoow/SwiftCalendarDemo/