とこなつとこなつ

swift について勉強したことの忘備録とか

swift で UICollectionView を使って曜日を表示したカレンダーを作成する

swift でカレンダーを作成しました。
↓こんな感じです。曜日も表示してます。ちょっと味気ないけどとりあえずできたので。
f:id:tokonatsutan:20150714203540p:plainf:id:tokonatsutan:20150714203550p:plain

storyboard はこんな感じ
f:id:tokonatsutan:20150714203731p:plain
UICollectionView を配置して、cell に自前のクラスを設定するくらい。
日付を表示するラベルを自前のクラスに持たせてるので。あとは delegate を ViewController に繋いでるくらい。
vie は CollectionView と繋いでます。多分 view って書いたつもりなんでしょうね。

コードはこんな感じ。解説とかはコード中に記載。あとで見て分かるかな…

import UIKit

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout  {

    let dayPerWeeks = 7
    let cellMargin: CGFloat = 2
    // 表示中の月のデータのコンテナ
    var currentDate: NSDate!
    let week: [String] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
    @IBOutlet weak var vie: UICollectionView! // vie ってなんだ
    
    override func viewDidLoad() {
        super.viewDidLoad()
        currentDate = NSDate()
        var date = NSDateFormatter()
        date.dateFormat = "yyyy/M"
        self.title = date.stringFromDate(currentDate)
    }
    
    // 先月押下時のアクション
    @IBAction func prevTap(sender: AnyObject) {
        var calendar = NSCalendar.currentCalendar()
        var dateComponents = NSDateComponents()
        // 1月マイナス
        dateComponents.month = -1
        var tmpDate = NSDate()
        self.currentDate = calendar.dateByAddingComponents(dateComponents, toDate: self.currentDate, options: NSCalendarOptions(0))!
        var formattar = NSDateFormatter()
        formattar.dateFormat = "yyyy/M"
        self.title = formattar.stringFromDate(currentDate)
        // view を更新する
        self.vie.reloadData()    
    }
    
    // 次月ボタン押下時のアクション
    @IBAction func nextTap(sender: AnyObject) {
        var calendar = NSCalendar.currentCalendar()
        var dateComponents = NSDateComponents()
        // 1月プラス
        dateComponents.month = 1
        var tmpDate = NSDate()
        self.currentDate = calendar.dateByAddingComponents(dateComponents, toDate: self.currentDate, options: NSCalendarOptions(0))!
        var formattar = NSDateFormatter()
        formattar.dateFormat = "yyyy/M"
        self.title = formattar.stringFromDate(currentDate)
        // view を更新する
        self.vie.reloadData()   
    }
    
    // その月の初日を返す
    func firstDateOfMonth() -> NSDate {
        var calendar = NSCalendar.currentCalendar()
        var component =  calendar.components(NSCalendarUnit.CalendarUnitYear|NSCalendarUnit.CalendarUnitMonth|NSCalendarUnit.CalendarUnitDay|NSCalendarUnit.CalendarUnitDay,fromDate:currentDate)
        component.day = 1
        var firstDateOfMonth = calendar.dateFromComponents(component)
        
        return firstDateOfMonth!
    }
    
    // index -> 日付へ変換する
    func dateForCellAtIndexPath(indexPass:NSIndexPath) -> NSDate {
        // 月の初日が何番目かを調べる
        var calendar: NSCalendar = NSCalendar.currentCalendar()
        var firstDay = calendar.ordinalityOfUnit(NSCalendarUnit.CalendarUnitDay,
            inUnit: NSCalendarUnit.CalendarUnitWeekOfMonth,
            forDate: firstDateOfMonth())
        
        // 「月の初日」と「indexPath.item番目のセルに表示する日」の差を計算する
        var dateComponents = NSDateComponents()
        
        // +7 は曜日表示分ずらすため
        dateComponents.day = indexPass.item - (firstDay - 1 + 7)
        var date = calendar.dateByAddingComponents(dateComponents, toDate: firstDateOfMonth(), options: NSCalendarOptions(0))!
        
        return date
    }
    
    // Mark: UICollectionViewDataSource methods
    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        var calendar = NSCalendar.currentCalendar()
        
        // その月が何週あるか調べる
        var rangeOfWeeks = calendar.rangeOfUnit(NSCalendarUnit.CalendarUnitWeekOfMonth,
                                                inUnit: NSCalendarUnit.CalendarUnitMonth,
                                                forDate: firstDateOfMonth())
        // 1ヶ月が何週あるか
        var numberOfWeeks = rangeOfWeeks.length
        // その月のカレンダーの総日数。
        // +1 しているのは曜日の列分追加するため
        var numberOfItems = (numberOfWeeks + 1) * dayPerWeeks
        
        return numberOfItems
    }
    
    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        // UICollectionViewCell. カレンダーの日付ラベルを持ってるくらい。
        var cell: customCell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! customCell
        // 1列目( < 7)は曜日をセットする
        if indexPath.row < 7 {
            cell.label!.text = week[indexPath.row]
        } else {
            // 2列目からはカレンダーに表示する日付
            var formatter = NSDateFormatter()
            formatter.dateFormat = "d"
            cell.label!.text = formatter.stringFromDate(self.dateForCellAtIndexPath(indexPath))
        }
        return cell
    }
    
    // Mark: UICollectionViewDelegateFlowLayout methods
    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
        
        let numberOfMargin = CGFloat(8)
        let rateOfWidth = CGFloat(1.5)
        
        var width: CGFloat = floor((collectionView.frame.size.width - CGFloat(cellMargin) * numberOfMargin) / CGFloat(dayPerWeeks))
        
        // width と height は 1:1.5
        var height: CGFloat = width * rateOfWidth
        
        return CGSizeMake(width, height)
    }
    
    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets {
        return UIEdgeInsetsMake(cellMargin, cellMargin, cellMargin, cellMargin)
    }
    
    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
        return cellMargin
    }
    
    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
        return cellMargin
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }   
}

コードはなんとか動くものが作れるけど AutoLayout が難しい。
StoryBoard に表示してあるものをそのまま画面サイズに合わせて表示してくれればいいのに…
いずれその辺なんとかしたいな。