iOs 自定义UIView 日历的实现 Swift2.1

学习Swift有一个月了,动手写一个UIView吧。

所有源代码在最后,直接用就可以了,第一次写Swift,和C#,Java还是有区别的

(博客园可以考虑在代码插入中添加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的写法

Objective-C

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

Swift
2.0

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

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)" )
    }

}
时间: 2024-12-12 23:01:50

iOs 自定义UIView 日历的实现 Swift2.1的相关文章

ios 继承UIView实现自定义视图——实现画图

主要的原理包括: 继承UIView ,重载drawrect和重载触摸事件 待实现的功能还有,路径数组保存等. 用可变数据保存path路径 画曲线是通过二次贝塞尔曲线实现的 这里可以得到画图的UIImage对象 UIGraphicsBeginImageContext(self.bounds.size); [self.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *result=UIGraphicsGetImageFrom

iOS开发--xib自定义UIView,与IB 、 xib 、 代码 定义与初始化对比

一.自定义UIView 二.关于xib定义的view实例化,初始化. 三.使用IB VS xib  VS 使用代码定义与实例化

IOS使用XIB自定义UIView

对于复杂的界面,用代码自定义UIView 是一件很痛苦的事情,所以可以用XIB来布局. 大致步骤如下: 1. 在你项目中 新建 类,继承UIView: 2. 在新建个XIB(XIB 的名称要跟新建 类名 一样): 3. 在XIB 中 选中View  改它Class 为你建的 类名: 4. 要使用这个UIView跟平常就不一样了.  因为  不是我们来  实例化它.keyi通过 这个静态方法 来实例 化.在这个静态方法中,传递需要的参数. +(LKTextView *)instanceTextVi

iOS 自定义页面的切换动画与交互动画

在iOS7之前,开发者为了寻求自定义Navigation Controller的Push/Pop动画,只能受限于子类化一个UINavigationController,或是用自定义的动画去覆盖它.但是随着iOS7的到来,Apple针对开发者推出了新的工具,以更灵活地方式管理UIViewController切换. 自定义导航栏的Push/Pop动画 为了在基于UINavigationController下做自定义的动画切换,先建立一个简单的工程,这个工程的rootViewController是一个

iOS自定义转场动画实战讲解

iOS自定义转场动画实战讲解 转场动画这事,说简单也简单,可以通过presentViewController:animated:completion:和dismissViewControllerAnimated:completion:这一组函数以模态视图的方式展现.隐藏视图.如果用到了navigationController,还可以调用pushViewController:animated:和popViewController这一组函数将新的视图控制器压栈.弹栈. 下图中所有转场动画都是自定义的

iOS 自定义layer的两种方式

在iOS中,你能看得见摸得着的东西基本都是UIView,比如一个按钮,一个标签,一个文本输入框,这些都是UIView: 其实UIView之所以能显示在屏幕上,完全是因为它内部的一个图层 在创建UIView对象时,UIView内部会自动创建一个图层(即CALayer对象),通过UIView的layer属性可以访问这个图层 @property(nonatomic,readonly,retain) CALayer *layer; 每一个UIView内部都默认关联一个CALayer,我们可称这个Laye

iOS自定义tabbar后popToRootViewContriller和poptoviewcontroller时出现两个tabbar 的解决办法

iOS自定义tabbar后popToRootViewContriller和poptoviewcontroller时出现两个tabbar  的解决办法 问题:iOS自定义tabbar后popToRootViewContriller和poptoviewcontroller时出现两个tabbar 1.自定义代码: - (void)viewWillAppear:(BOOL)animated { [super  viewWillAppear:animated]; // 删除系统自动生成的UITabBarB

IOS 自定义 滑动返回 手势

/** 只需要在你自定义的导航控制器中,改成如下代码即可,自定义手势返回 */ #define KEY_WINDOW [[UIApplication sharedApplication] keyWindow] #define kScreenW KEY_WINDOW.bounds.size.width #define kAnimateDuration 0.3 #define kDefaultAlapa 0.5 #define kDefaultScale 0.95 #define iOS7 ([UI

IOS开发—IOS自定义任意位置右滑POP视图控制器

IOS自定义任意位置右滑POP视图控制器 IOS7.0之后系统提供了原生的从左边缘滑动pop出栈的方法,也可以自定义左边缘pop出栈,将在下一篇介绍,本篇介绍通过添加手势的方法实现IOS当前屏幕任意位置(非指定左边缘)右滑pop视图控制器出栈.代码如下: // // LXXPopViewController.m // 任意点右滑Pop // // Created by Lotheve on 15/6/12. // Copyright (c) 2015年Lotheve. All rights re