iOs 自定義UIView 日曆的實現 Swift2.1

學習Swift有一個月了,動手寫一個UIView吧。swift

全部源代碼在最後,直接用就能夠了,第一次寫Swift,和C#,Java仍是有區別的ide

(博客園能夠考慮在代碼插入中添加Swift的着色了)函數

1  函數準備。Swift的日曆函數,隨着版本的變化,變更很大。學習

 

    //MARK: - Calendar
    //按照蘋果的習慣,週日放在第一位
    let weekdayForDisplay = ["週日","週一","週二","週三","週四","週五","週六"]
    
    
    //獲取周 週日:1 - 週六:7
    func getWeekDay(year:Int,month:Int,day:Int) ->Int{
        let dateFormatter:NSDateFormatter = NSDateFormatter();
        dateFormatter.dateFormat = "yyyy/MM/dd";
        let date:NSDate? = dateFormatter.dateFromString(String(format:"%04d/%02d/%02d",year,month,day));
        if date != nil {
            let calendar:NSCalendar = NSCalendar.currentCalendar()
            let dateComp:NSDateComponents = calendar.components(NSCalendarUnit.NSWeekdayCalendarUnit, fromDate: date!)
            return dateComp.weekday;
        }
        return 0;
    }
    
    //這個月的最後一天
    //先得到下個月的第一天,而後在此基礎上減去24小時
    //注意這裏的時間Debug的時候是UTC
    func getLastDay(var year:Int,var month:Int) -> Int?{
        let dateFormatter:NSDateFormatter = NSDateFormatter();
        dateFormatter.dateFormat = "yyyy/MM/dd";
        if month == 12 {
            month = 0
            year++
        }
        let targetDate:NSDate? = dateFormatter.dateFromString(String(format:"%04d/%02d/01",year,month+1));
        if targetDate != nil {
            
            let orgDate = NSDate(timeInterval:(24*60*60)*(-1), sinceDate: targetDate!)
            let str:String = dateFormatter.stringFromDate(orgDate)
            return Int((str as NSString).componentsSeparatedByString("/").last!);
        }
        
        return nil;
    }

下面是NSDateCompents的一個坑,Swift 1 和 Swift 2 寫法不同字體

        let today = NSDate()
        let calendar = NSCalendar(identifier: NSGregorianCalendar)
        let comps:NSDateComponents = calendar!.components([NSCalendarUnit.Year,NSCalendarUnit.Month,NSCalendarUnit.Day], fromDate: today)

Swift 2 OptionSetType ,比較一下OC和Swift的寫法spa

 

Objective-Ccode

unsigned unitFlags = NSCalendarUnitYear
                   | NSCalendarUnitMonth
                   | NSCalendarUnitDay
                   | NSCalendarUnitWeekday
                   | NSCalendarUnitHour
                   | NSCalendarUnitMinute
                   | NSCalendarUnitSecond;component

Swift 2.0orm

let unitFlags: NSCalendarUnit = [ .Year,
                                  .Month,
                                  .Day,
                                  .Weekday,
                                  .Hour,
                                  .Minute,
                                  .Second ]blog

Swift 1.2

let unitFlags: NSCalendarUnit = .CalendarUnitYear
                              | .CalendarUnitMonth
                              | .CalendarUnitDay
                              | .CalendarUnitWeekday
                              | .CalendarUnitHour
                              | .CalendarUnitMinute
                              | .CalendarUnitSecond

 

 

Swift2.0 的語法和1.2有區別   OptionSetType

 

2.接下來就是繪圖,繪圖就是各類被塞爾曲線

重點以下

 

如何居中

        let paragraph = NSMutableParagraphStyle()
        paragraph.alignment = NSTextAlignment.Center
        
        let text  =  NSMutableAttributedString(string: weekdayForDisplay[i],attributes: [NSParagraphStyleAttributeName: paragraph])
        let CellRect = CGRect(x: leftside  , y:padding + mergin, width: WeekdayColumnWidth, height: RowHeight)
        text.drawInRect(CellRect)

紅字粗體
        text.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(),range:NSMakeRange(0,text.length))
        text.addAttribute(NSFontAttributeName, value: UIFont.boldSystemFontOfSize(NSDefaultFontSize),range:NSMakeRange(0,text.length))

 

3.接下來是如何捕獲點擊事件

因爲是全手工繪製日曆的格子,因此,就用OnTouchBegan事件的屬性得到點擊位置,根據位置得知被按下的區域隸屬於哪一個日子。

    //記錄天天的格子的Rect
    var DayRect = [Int:CGRect]()
    
    
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        let SignleTouch = touches.first!
        let Touchpoint = SignleTouch.locationInView(self)
        let pick = getDayByTouchPoint(Touchpoint)
        print("TouchPoint : X = \(Touchpoint.x) Y = \(Touchpoint.y)  Day: \(pick)")
        
        if pick != 0 {self.PickedDay = pick }
    }
    
    //根據觸摸點獲取日期
    func getDayByTouchPoint(touchpoint:CGPoint) -> Int {
        for day in DayRect{
            if day.1.contains(touchpoint){
                return day.0
            }
        }
        return 0
    }

 

最終效果以下圖,能夠實現點擊選擇日期。整個代碼,8個小時能夠完成。

如今的問題是,若是選擇的日子變化了,我不知道怎麼告訴上層的 ViewController,SelectDateChanged。

若是能夠的話,最好可以出現 ActionConnection,能夠拖曳連線,將Action和代碼綁定。誰知道怎麼作嗎?

//
//  CalendarView.swift
//  PlanAndTarget
//
//  Created by  scs on 15/10/13.
//  Copyright © 2015年  scs. All rights reserved.
//

import UIKit

@IBDesignable
class CalendarView: UIView {
    //MARK: - Inspectable
    @IBInspectable
    var CurrentYear : Int = 2015{
        didSet{
            if self.CurrentYear < 0 {
                self.CurrentYear = 2015
            }
            setNeedsDisplay()
        }
    }
    
    @IBInspectable
    var CurrentMonth : Int = 10 {
        didSet{
            if self.CurrentMonth < 0 || self.CurrentMonth > 12 {
                self.CurrentMonth = 1
            }
            setNeedsDisplay()
        }
    }
    
    @IBInspectable
    var padding : CGFloat = 4 {
        didSet{
            if (self.padding > 50 ) {
                self.padding = 50
            }
            setNeedsDisplay()
        }
    }
    
    @IBInspectable
    var mergin : CGFloat = 4 {
        didSet{
            if (self.mergin > 50 ) {
                self.mergin = 50
            }
            setNeedsDisplay()
        }
    }
    
    @IBInspectable
    var RowHeight : CGFloat = 20{
        didSet{
            if (self.RowHeight > 100 ) {
                self.RowHeight = 100
            }
            setNeedsDisplay()
        }
    }
    
    @IBInspectable
    var PickedDay : Int = 1 {
        didSet{
            if (self.PickedDay < 0){
                self.PickedDay = 1
            }
            let lastDay = getLastDay( CurrentYear, month: CurrentMonth)
            if (self.PickedDay > lastDay!){
                self.PickedDay = lastDay!
            }
            setNeedsDisplay()
        }
    }
    
    
    
    
    //MARK: - Calendar
    //按照蘋果的習慣,週日放在第一位
    let weekdayForDisplay = ["週日","週一","週二","週三","週四","週五","週六"]
    
    
    //獲取周 週日:1 - 週六:7
    func getWeekDay(year:Int,month:Int,day:Int) ->Int{
        let dateFormatter:NSDateFormatter = NSDateFormatter();
        dateFormatter.dateFormat = "yyyy/MM/dd";
        let date:NSDate? = dateFormatter.dateFromString(String(format:"%04d/%02d/%02d",year,month,day));
        if date != nil {
            let calendar:NSCalendar = NSCalendar.currentCalendar()
            let dateComp:NSDateComponents = calendar.components(NSCalendarUnit.NSWeekdayCalendarUnit, fromDate: date!)
            return dateComp.weekday;
        }
        return 0;
    }
    
    //這個月的最後一天
    //先得到下個月的第一天,而後在此基礎上減去24小時
    //注意這裏的時間Debug的時候是UTC
    func getLastDay(var year:Int,var month:Int) -> Int?{
        let dateFormatter:NSDateFormatter = NSDateFormatter();
        dateFormatter.dateFormat = "yyyy/MM/dd";
        if month == 12 {
            month = 0
            year++
        }
        let targetDate:NSDate? = dateFormatter.dateFromString(String(format:"%04d/%02d/01",year,month+1));
        if targetDate != nil {
            
            let orgDate = NSDate(timeInterval:(24*60*60)*(-1), sinceDate: targetDate!)
            let str:String = dateFormatter.stringFromDate(orgDate)
            return Int((str as NSString).componentsSeparatedByString("/").last!);
        }
        
        return nil;
    }
    
    //MARK: - Event
    //記錄天天的格子的Rect
    var DayRect = [Int:CGRect]()
    
    
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        let SignleTouch = touches.first!
        let Touchpoint = SignleTouch.locationInView(self)
        let pick = getDayByTouchPoint(Touchpoint)
        print("TouchPoint : X = \(Touchpoint.x) Y = \(Touchpoint.y)  Day: \(pick)")
        
        if pick != 0 {self.PickedDay = pick }
    }
    
    //根據觸摸點獲取日期
    func getDayByTouchPoint(touchpoint:CGPoint) -> Int {
        for day in DayRect{
            if day.1.contains(touchpoint){
                return day.0
            }
        }
        return 0
    }
    
    
    // Only override drawRect: if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func drawRect(rect: CGRect) {
        
        let paragraph = NSMutableParagraphStyle()
        paragraph.alignment = NSTextAlignment.Center
        //查資料可知默認字體爲12
        let NSDefaultFontSize : CGFloat = 12;
        
        //繪製表頭
        let UseableWidth :CGFloat = rect.width - (padding + mergin) * 2;
        let WeekdayColumnWidth : CGFloat = UseableWidth / 7
        var leftside  : CGFloat = padding + mergin
        for i in 0...6{
            let text  =  NSMutableAttributedString(string: weekdayForDisplay[i],attributes: [NSParagraphStyleAttributeName: paragraph])
            let CellRect = CGRect(x: leftside  , y:padding + mergin, width: WeekdayColumnWidth, height: RowHeight)
            text.drawInRect(CellRect)
            leftside += WeekdayColumnWidth
        }
        
        //繪製當月天天
        var rowCount = 1;
        leftside  = padding + mergin
        let today = NSDate()
        let calendar = NSCalendar(identifier: NSGregorianCalendar)
        let comps:NSDateComponents = calendar!.components([NSCalendarUnit.Year,NSCalendarUnit.Month,NSCalendarUnit.Day], fromDate: today)
        
        //Clear
        DayRect.removeAll()
        
        for day in 1...getLastDay(CurrentYear,month:CurrentMonth)!{
            let weekday = getWeekDay(CurrentYear, month: CurrentMonth, day: day)
            let text  =  NSMutableAttributedString(string: String(day),  attributes: [NSParagraphStyleAttributeName: paragraph])
            let LeftTopX = leftside + CGFloat(weekday - 1) * WeekdayColumnWidth
            let LeftTopY = padding + mergin + RowHeight * CGFloat(rowCount)
            let CellRect :CGRect = CGRect(x: LeftTopX, y: LeftTopY, width: WeekdayColumnWidth, height: RowHeight)
            if (PickedDay == day){
                //選中的日子,UI效果
                let PickRectPath = UIBezierPath(roundedRect: CellRect, cornerRadius: RowHeight/2)
                UIColor.blueColor().colorWithAlphaComponent(0.3).setFill()
                PickRectPath.fill()
            }
            
            if (comps.year == CurrentYear && comps.month == CurrentMonth && comps.day == day){
                text.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(),range:NSMakeRange(0,text.length))
                text.addAttribute(NSFontAttributeName, value: UIFont.boldSystemFontOfSize(NSDefaultFontSize),range:NSMakeRange(0,text.length))
            }
            
            
            text.drawInRect(CellRect)
            DayRect[day] = CellRect
            //繪製了週日以後,須要新的一行
            if weekday == 7 { rowCount++ }
        }
        
        //繪製外框
        let path : UIBezierPath = UIBezierPath(rect: CGRect(x: padding, y: padding, width: rect.width - padding * 2 , height: padding + mergin + RowHeight * CGFloat(rowCount - 1) + 10 ))
        path.stroke()
        
        //path = UIBezierPath(rect: CGRect(x: padding + mergin, y: padding + mergin, width: rect.width - (padding + mergin) * 2 , height: rect.height - (padding + mergin) * 2))
        //path.stroke()
        
        print("LastDay Of 2015/10 : \(getLastDay(CurrentYear, month: CurrentMonth))" )
        print("2015/10/18 : \(weekdayForDisplay[getWeekDay(CurrentYear, month: CurrentMonth, day: 18) - 1]  )" )
        print("Calendar Size Height: \(rect.height)  Width: \(rect.width)" )
    }
    
    
    
}
相關文章
相關標籤/搜索