↑のようにスワイプで切り替えれるようなカレンダーを。
ひとまずswiftの試しが主目的ですが、swiftらしくない部分もあるかもしれません。
試し試しで実装した所があるのでベストな実装とは思えないので、
もう少し暖めようかと思ったのですが、ひとまずちょっと公開してみることにしました。
Xcode 6.1.1にて実装しました。
##1.構成
・各日を表示するView(DayViewとします。)
・各月を表示するView(MonthViewとします。)
・月を切り替えるためのView(CalenderViewとします。)
この3つのViewを扱うようにします。
##2.DayViewについて
曜日に合わせて表示を切り替えるなど想定されるため、
year/month/day/week
の四つの情報を取得するようにしています。
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で割った商を扱っています。
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
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にて利用する場合は以下のような形を想定しています。
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)
}
}
(縦幅は適当です。。