Swift功能实现之日历相册

  项目中有相册的功能,现在又要在相册中添加新的需求,将相册的图片以日历的形式显示。这里主要用到了Calendar这个类。下面介绍一下这个功能的实现,写了一个demo,demo只是写了一下日历的写法,并没有附上实际的图片数据,大家可以自己按自己的需求设计数据model进行图片赋值。

功能演示:


功能演示

Calendar

  Calendar是Swift中提供的日历类,它提供了大部分的关于日期的计算接口。这里我就不具体的展开介绍这个类的使用,我只列出在demo中用到的方法,简单的介绍它们的作用。

//调用Calendar类,创建实例对象
var myCalendar = Calendar.current

let currentDate = Date()

//获取指定日期是周几(默认周日是1,周一是2,依次类推)
let week: Int = myCalendar.ordinality(of: .day, in: .weekOfMonth, for: currentDate) ?? 0

//获取指定日期当月的天数
let dasOfMonth: Int = myCalendar.range(of: .day, in: .month, for: currentDate)?.count ?? 0

//获取指定日期之后天数的日期Date
var dateComponents = DateComponents()
dateComponents.day = 3
let data = myCalendar.date(byAdding: dateComponents, to: currentDate)

这几个都是非常常用的方法,更多的使用方法,大家可以自己去了解。

功能代码实现

这里主要是在于跳转到月份日期页面时,cell的日期计算和显示。我们需要确认:

  1. 该月的第一天是从周几开始。
  2. 确认该月的section中每个cell的日期。
  3. 该月section的cell数需要多少行显示。
  4. 判断每个cell的Date是否跟该月属于同一个月,属于:显示日期。不属于:显示空白。

OK下面开始上具体的项目代码:

  • 创建一个工具类,来进行日期的计算和数据的处理。CalendarDateTool.swift
import UIKit

class CalendarDateTool: NSObject {
    
    let formate = DateFormatter()
    
    static let instance: CalendarDateTool = CalendarDateTool()
    class func shared() -> CalendarDateTool {
        return instance
    }
    
    //MARK: 日历显示行数
    class func calendarGetRows(currentDate: Date) -> Int {
        //当前日期(都是每月的1号)是星期几 (默认是周日为1,周一为2,依次类推)
        let week: Int = Calendar.current.ordinality(of: .day, in: .weekOfMonth, for: currentDate) ?? 0
        //当前月的天数
        let daysOfMouth: Int = Calendar.current.range(of: .day, in: .month, for: currentDate)?.count ?? 0
        //进行行数的计算,需理解。
        let temp = daysOfMouth - (8 - week)
        let rows = temp % 7 > 0 ? temp / 7 + 2 : temp / 7 + 1
        return rows
    }
    
    //MARK: 获取选中月份第一天的日期Date
    class func getCurrentDate(yearStr: String, mouthStr: String) -> Date {
        let dateFormat = DateFormatter()
        dateFormat.dateFormat = "yyyy-MM-dd"
        let str = yearStr + "-" + mouthStr + "-" + "01"
        let date = dateFormat.date(from: str)
        return date!
    }
    
    
    //MARK:获取cell的日期 按照indexPath获取cell的日期
    class func dateForCell(indexPath: IndexPath, currentDate: Date) -> Date {
        let week: Int = Calendar.current.ordinality(of: .day, in: .weekOfMonth, for: currentDate) ?? 0
        var dateComponents = DateComponents()
        dateComponents.day = (1 - week) + indexPath.row
        let date: Date = Calendar.current.date(byAdding: dateComponents, to: currentDate)!
        return date
    }
    
    //MARK: 判断两个日期月份是否同一个月
    class func checkSameMouth(dateOne: Date, dateTwo: Date) -> Bool {
        let cal = Calendar.current
        let yearOne: Int = cal.component(.year, from: dateOne)
        let monthOne: Int = cal.component(.month, from: dateOne)
        let yearTwo: Int = cal.component(.year, from: dateTwo)
        let monthTwo: Int = cal.component(.month, from: dateTwo)
        var rt: Bool = false
        if yearOne == yearTwo && monthOne == monthTwo {
            rt = true
        } else {
            rt = false
        }
        return rt
    }
    
    //MARK: cell得到日期
    func getCellDateStrFromFormat(formatStr: String, cellDate: Date) -> String {
        self.formate.dateFormat = formatStr
        return self.formate.string(from: cellDate)
    }

}
  • 对UIColor扩展方法
import UIKit

extension UIColor {
    
    //MARK: 随机赋色
    class func randomColor() -> UIColor {
        var returnColor = UIColor()
        let color1 = UIColor.init(hexString: "#e8eaf2")
        let color2 = UIColor.init(hexString: "#ecefef")
        let color3 = UIColor.init(hexString: "#efefec")
        let color4 = UIColor.init(hexString: "#f6f6f7")
        let colorArr = [color1, color2, color3, color4]
        let temp = Int(arc4random() % 4)
        returnColor = colorArr[temp]
        return returnColor
    }
    
    
    //MARK: 按色号进行赋色
    convenience init(hexString: String) {
        //处理数值
        var cString = hexString.uppercased().trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
        
        let length = (cString as NSString).length
        //错误处理
        if (length < 6 || length > 7 || (!cString.hasPrefix("#") && length == 7)){
            //返回whiteColor
            self.init(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0)
            return
        }
        
        if cString.hasPrefix("#"){
            cString = (cString as NSString).substring(from: 1)
        }
        
        //字符chuan截取
        var range = NSRange()
        range.location = 0
        range.length = 2
        
        let rString = (cString as NSString).substring(with: range)
        
        range.location = 2
        let gString = (cString as NSString).substring(with: range)
        
        range.location = 4
        let bString = (cString as NSString).substring(with: range)
        
        //存储转换后的数值
        var r:UInt32 = 0,g:UInt32 = 0,b:UInt32 = 0
        //进行转换
        Scanner(string: rString).scanHexInt32(&r)
        Scanner(string: gString).scanHexInt32(&g)
        Scanner(string: bString).scanHexInt32(&b)
        //根据颜色值创建UIColor
        self.init(red: CGFloat(r)/255.0, green: CGFloat(g)/255.0, blue: CGFloat(b)/255.0, alpha: 1.0)
    }   
}
  • 工具类创建完毕,下面是封装的一个CalendarView,继承UIView
import UIKit

let WIDTH = UIScreen.main.bounds.width
let HEIGHT = UIScreen.main.bounds.height
let backColor = UIColor(red:0.95, green:0.95, blue:0.95, alpha:1.00)


class CalendarView: UIView, UICollectionViewDelegate, UICollectionViewDataSource {
    
    let identifier = "pool"
    let headerIdentifier = "header"
    var navi: UINavigationController?
    
    var collection: UICollectionView!
    let mouthArr: Array<String> = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"]
    let yearArr: Array<String> = ["2017", "2016", "2015", "2014", "2013"]

    override init(frame: CGRect) {
        super .init(frame: frame)
        self.setUpUI()
    }
    
    func setUpUI() {
        self.backgroundColor = backColor
        
        let layout = UICollectionViewFlowLayout()
        layout.itemSize = CGSize(width: (WIDTH - 25) / 6, height: (WIDTH - 25) / 6)
        layout.minimumInteritemSpacing = 3
        layout.minimumLineSpacing = 5
        layout.scrollDirection = .vertical
        layout.sectionInset = UIEdgeInsets.init(top:0, left: 5, bottom: 0, right: 5)
        layout.headerReferenceSize = CGSize(width: WIDTH, height: 50)
        
        collection = UICollectionView.init(frame: CGRect.init(x: 0, y: 1, width: WIDTH, height: HEIGHT - 1), collectionViewLayout: layout)
        collection.backgroundColor = UIColor.white
        collection.delegate = self
        collection.dataSource = self
        collection.register(CalendarCollectionCell.self, forCellWithReuseIdentifier: identifier)
        collection.register(CalendarOneHeader.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: headerIdentifier)
        self.addSubview(collection)
    }
    
    
    //MARK: collectionViewDelegate
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 12
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collection.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! CalendarCollectionCell
        cell.mouthLabel.text = mouthArr[indexPath.row] + "月"
        return cell
    }
    
    
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return yearArr.count
    }
    
    
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        if kind == UICollectionElementKindSectionHeader {
            let reusableView = collection.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerIdentifier, for: indexPath) as! CalendarOneHeader
            reusableView.yearLabel.text = yearArr[indexPath.section] + "年"
            return reusableView
        }
        return UICollectionReusableView()
    }
    
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let nextVC = CalendarControllerTwo()
        let str = self.yearArr[indexPath.section] + "年" + self.mouthArr[indexPath.row] + "月"
        nextVC.headerText = str
        //处理选中月份的一号日期数据,传到下一页。
        nextVC.currentDate = CalendarDateTool.getCurrentDate(yearStr: yearArr[indexPath.section], mouthStr: mouthArr[indexPath.row])
        self.navi?.pushViewController(nextVC, animated: true)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    
}

自定义的cell和头视图
cell

import UIKit

class CalendarCollectionCell: UICollectionViewCell {
    
    var picture: UIImageView!
    var mouthLabel: UILabel!
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        picture = UIImageView.init(frame: self.bounds)
        picture.backgroundColor = UIColor.randomColor()
        mouthLabel = UILabel.init(frame: CGRect(x: 0, y: 0, width: 50, height: 30))
        mouthLabel.center = self.contentView.center
        mouthLabel.textColor = UIColor.white
        mouthLabel.font = UIFont.boldSystemFont(ofSize: 18)
        mouthLabel.textAlignment = NSTextAlignment.center
        configUI()
    }
    
    func configUI() {
        self.contentView.addSubview(picture)
        self.contentView.addSubview(mouthLabel)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}

header

import UIKit

class CalendarOneHeader: UICollectionReusableView {
    
    var yearLabel: UILabel!
    
    override init(frame: CGRect) {
        super .init(frame: frame)
        
        yearLabel = UILabel.init(frame: CGRect(x: 18, y: 20, width: self.frame.width - 40, height: 18))
        yearLabel.textAlignment = NSTextAlignment.left
        yearLabel.font = UIFont.systemFont(ofSize: 13)
        yearLabel.textColor = UIColor.gray
        self.addSubview(yearLabel)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}
  • 创建跳转页面:月份页面。
import UIKit

class CalendarControllerTwo: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
   
    let identifier = "pool"
    let headerIdentifier = "header"
    
    var collection: UICollectionView!
    var headerText: String!  //头视图的字符串
    var currentDate: Date!   //选中的月份的一号日期(Data)数据
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setUpUI()
    }
    
    
    func setUpUI() {
        self.view.backgroundColor = backColor
        
        let layout = UICollectionViewFlowLayout()
        layout.itemSize = CGSize(width: (WIDTH - 6) / 7, height: (WIDTH - 6) / 7)
        layout.minimumLineSpacing = 1
        layout.minimumInteritemSpacing = 1
        layout.scrollDirection = .vertical
        layout.sectionInset = UIEdgeInsets.init(top: 0, left: 0, bottom: 0, right: 0)
        layout.headerReferenceSize = CGSize(width: WIDTH, height: 60)
        
        collection = UICollectionView.init(frame: CGRect.init(x: 0, y: 1, width: WIDTH, height: HEIGHT - 1), collectionViewLayout: layout)
        collection.backgroundColor = UIColor.white
        collection.delegate = self
        collection.dataSource = self
        collection.register(CalendarCollectionCellTwo.self, forCellWithReuseIdentifier: identifier)
        collection.register(CalendarTwoHeader.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: headerIdentifier)
        self.view.addSubview(collection)
        
    }
    
    //MARK: collectionViewDelegate
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        if kind == UICollectionElementKindSectionHeader {
            let reusableView = collection.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerIdentifier, for: indexPath) as! CalendarTwoHeader
            reusableView.yearLabel.text = headerText
            return reusableView
        }
        return UICollectionReusableView()
    }

    
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        //获取该月份返回cell的个数方法
        return CalendarDateTool.calendarGetRows(currentDate: currentDate) * 7
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collection.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! CalendarCollectionCellTwo
        let cellDate = CalendarDateTool.dateForCell(indexPath: indexPath, currentDate: currentDate)
        cell.currentDate = currentDate
        cell.cellDate = cellDate
        return cell
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

自定义的cell和头视图
cell:

import UIKit

class CalendarCollectionCellTwo: UICollectionViewCell {
    var picture: UIImageView!
    var dayLabel: UILabel!
    
    var cellDate = Date() {
        didSet {
            //判断是否为同一月份
            if CalendarDateTool.checkSameMouth(dateOne: cellDate, dateTwo: currentDate) {
                showDate()
            } else {
                showSpace()
            }
        }
    }
    var currentDate: Date!
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        picture = UIImageView.init(frame: self.bounds)
        dayLabel = UILabel.init(frame: CGRect(x: self.frame.width - 25, y: self.frame.height - 25, width: 20, height: 20 ))
        dayLabel.textColor = UIColor.white
        dayLabel.font = UIFont.boldSystemFont(ofSize: 12)
        dayLabel.textAlignment = NSTextAlignment.center
        configUI()
    }
    
    func configUI() {
        self.contentView.addSubview(picture)
        self.contentView.addSubview(dayLabel)
    }
    
    //当cell的日期该月份为同月时正常显示cell的样式
    func showDate() {
        self.isUserInteractionEnabled = true
        picture.backgroundColor = UIColor.randomColor()
        let day = CalendarDateTool.shared().getCellDateStrFromFormat(formatStr: "dd", cellDate: cellDate)
        dayLabel.text = day
        
    }
    
    //当不属于同一个月时,显示空白
    func showSpace() {
        self.isUserInteractionEnabled = false
        picture.backgroundColor = UIColor.clear
        dayLabel.text = ""
    }
    
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}

header:

import UIKit

class CalendarTwoHeader: UICollectionReusableView {
    let labelWidth: CGFloat = (WIDTH - 6) / 7
    let labelHeight: CGFloat = 15
    var yearLabel: UILabel!
    
    override init(frame: CGRect) {
        super .init(frame: frame)
        
        yearLabel = UILabel.init(frame: CGRect(x: 18, y: 15, width: self.frame.width - 40, height: 15))
        yearLabel.textAlignment = NSTextAlignment.left
        yearLabel.font = UIFont.systemFont(ofSize: 15)
        yearLabel.textColor = UIColor.black
        self.addSubview(yearLabel)
        
        let label1 = UILabel.init(frame: CGRect.init(x: 0, y: yearLabel.frame.maxY + 10, width: labelWidth, height: labelHeight))
        label1.text = "周日"
        label1.textAlignment = NSTextAlignment.center
        label1.font = UIFont.systemFont(ofSize: 10)
        label1.textColor = UIColor.init(hexString: "#959595")
        
        let label2 = UILabel.init(frame: CGRect.init(x: label1.frame.maxX + 1, y: label1.frame.minY, width: labelWidth, height: labelHeight))
        label2.text = "周一"
        label2.textAlignment = NSTextAlignment.center
        label2.font = UIFont.systemFont(ofSize: 10)
        label2.textColor = UIColor.init(hexString: "#959595")
        
        let label3 = UILabel.init(frame: CGRect.init(x: label2.frame.maxX + 1, y: label1.frame.minY, width: labelWidth, height: labelHeight))
        label3.text = "周二"
        label3.textAlignment = NSTextAlignment.center
        label3.font = UIFont.systemFont(ofSize: 10)
        label3.textColor = UIColor.init(hexString: "#959595")
        
        let label4 = UILabel.init(frame: CGRect.init(x: label3.frame.maxX + 1, y: label1.frame.minY, width: labelWidth, height: labelHeight))
        label4.text = "周三"
        label4.textAlignment = NSTextAlignment.center
        label4.font = UIFont.systemFont(ofSize: 10)
        label4.textColor = UIColor.init(hexString: "#959595")
        
        let label5 = UILabel.init(frame: CGRect.init(x: label4.frame.maxX + 1, y: label1.frame.minY, width: labelWidth, height: labelHeight))
        label5.text = "周四"
        label5.textAlignment = NSTextAlignment.center
        label5.font = UIFont.systemFont(ofSize: 10)
        label5.textColor = UIColor.init(hexString: "#959595")

        let label6 = UILabel.init(frame: CGRect.init(x: label5.frame.maxX + 1, y: label1.frame.minY, width: labelWidth, height: labelHeight))
        label6.text = "周五"
        label6.textAlignment = NSTextAlignment.center
        label6.font = UIFont.systemFont(ofSize: 10)
        label6.textColor = UIColor.init(hexString: "#959595")

        let label7 = UILabel.init(frame: CGRect.init(x: label6.frame.maxX + 1, y: label1.frame.minY, width: labelWidth, height: labelHeight))
        label7.text = "周六"
        label7.textAlignment = NSTextAlignment.center
        label7.font = UIFont.systemFont(ofSize: 10)
        label7.textColor = UIColor.init(hexString: "#959595")
        
        self.addSubview(label1)
        self.addSubview(label2)
        self.addSubview(label3)
        self.addSubview(label4)
        self.addSubview(label5)
        self.addSubview(label6)
        self.addSubview(label7)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

ok 这里就将所有的代码列出,demo中每个cell都添加了UIImageView空间,设计好自己的model数据,将照片的日期分类清楚,进行赋值就可以形成日历相册了。数据处理这块还需要自己想清楚,处理好。

最后

最后附上demo的地址https://github.com/Sufviay/CalendarDemo,供参考。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,904评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,581评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,527评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,463评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,546评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,572评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,582评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,330评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,776评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,087评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,257评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,923评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,571评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,192评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,436评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,145评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,086评论 4 62
  • Sugar囡阅读 151评论 0 0
  • 数九寒天,天气越来越干,再有各种年终特色的加班加点,众人上火一片。恰好,小明家里的单位新发了一箱柑柚,外表光鲜,色...
    竞走的蜗牛阅读 147评论 0 0
  • 人生的长河 (梁子为外甥女笑笑画作配诗)
    梁子仗剑走天下阅读 710评论 0 6