您的位置:首页 > 移动开发 > Swift

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

2015-10-14 13:04 471 查看
学习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)" )
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: