swift で UICollectionView を使って曜日を表示したカレンダーを作成する
swift でカレンダーを作成しました。
↓こんな感じです。曜日も表示してます。ちょっと味気ないけどとりあえずできたので。
storyboard はこんな感じ
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 に表示してあるものをそのまま画面サイズに合わせて表示してくれればいいのに…
いずれその辺なんとかしたいな。