iOS 炫酷时钟

轮盘时钟

代码


import UIKit

/// 每个数字指针的组件
class ComponentView: UIView {

    lazy var timeLabel: UILabel = {
        let label = UILabel()
        label.textColor = .timeColor
        label.backgroundColor = .randomColor
        label.font = UIFont.timeFont
        label.isUserInteractionEnabled = false
        return label
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupView()
    }

    func setupView() {
        timeLabel.frame = CGRect(x: bounds.width - 30, y: 0, width: 30, height: bounds.height)
        addSubview(timeLabel)
    }
}


/// 每一圈轮盘的视图(月,日,时,分,秒)
class TimeView: UIView {

    let unit: String
    let start: Int
    let end: Int

    init(frame: CGRect, unit: String, start: Int, end: Int) {
        self.unit = unit
        self.start = start
        self.end = end
        super.init(frame: frame)
        drawTime()
        isUserInteractionEnabled = false
    }

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

    func drawTime() {
        for index in start...end {
            let component = ComponentView(frame: CGRect(x: center.x, y: center.y, width: bounds.width / 2, height: 30))
            component.timeLabel.text = String(format: "%0.2ld\(unit)", index)
            addSubview(component)
            component.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
            component.layer.position = center
            let angle = -CGFloat.pi * 2 * CGFloat(index) / CGFloat(end - start + 1)
            component.transform = component.transform.rotated(by: angle)
        }
    }
}


/// 轮盘时钟视图
class ClockView: UIView {

    lazy var offset: CGFloat = {
        return bounds.width / 6
    }()

    /// 秒
    lazy var secondView: UIView = {
        let view = TimeView(frame: CGRect(x: 0, y: 0, width: bounds.width, height: bounds.width), unit: "秒", start: 0, end: 59)
        view.center = center
        return view
    }()

    /// 分
    lazy var minuteView: UIView = {
        let view = TimeView(frame: CGRect(x: 0, y: 0, width: bounds.width - offset, height: bounds.width - offset), unit: "分", start: 0, end: 59)
        view.center = center
        return view
    }()
    
    /// 时
    lazy var hourView: UIView = {
        let view = TimeView(frame: CGRect(x: 0, y: 0, width: bounds.width - offset * 2, height: bounds.width - offset * 2), unit: "时", start: 0, end: 23)
        view.center = center
        return view
    }()

    /// 日
    lazy var dayView: UIView = {
        let view = TimeView(frame: CGRect(x: 0, y: 0, width: bounds.width - offset * 3, height: bounds.width - offset * 3), unit: "日", start: 1, end: Date().monthLength)
        view.center = center
        return view
    }()

    /// 月
    lazy var monthView: UIView = {
        let view = TimeView(frame: CGRect(x: 0, y: 0, width: bounds.width - offset * 4, height: bounds.width - offset * 4), unit: "月", start: 1, end: 12)
        view.center = center
        return view
    }()

    /// 年
    lazy var yearLabel: UILabel = {
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
        label.center = center
        label.textColor = .timeColor
        label.textAlignment = .center
        label.backgroundColor = .randomColor
        label.font = UIFont.timeFont
        return label
    }()
    
    var timer: Timer?

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
        timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(handleTick), userInfo: nil, repeats: true)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupView()
    }

    func setupView() {
        addSubview(secondView)
        addSubview(minuteView)
        addSubview(hourView)
        addSubview(dayView)
        addSubview(monthView)
        addSubview(yearLabel)
    }

    @objc func handleTick() {
        let date = Date()
        // 普通动画
//        UIView.animate(withDuration: 1) {
//            self.secondView.transform = CGAffineTransform.init(rotationAngle: CGFloat.pi * 2 * CGFloat(date.second.intValue + 1) / 60)
//            self.minuteView.transform = CGAffineTransform.init(rotationAngle: CGFloat.pi * 2 * CGFloat(date.minute.intValue) / 60)
//            self.hourView.transform = CGAffineTransform.init(rotationAngle: CGFloat.pi * 2 * CGFloat(date.hour.intValue) / 24)
//            self.dayView.transform = CGAffineTransform.init(rotationAngle: CGFloat.pi * 2 * CGFloat(date.day.intValue) / CGFloat(date.monthLength))
//            self.monthView.transform = CGAffineTransform.init(rotationAngle: CGFloat.pi * 2 * CGFloat(date.month.intValue) / 12)
//            self.yearLabel.text = "\(date.year)年"
//        }
        
        // 弹簧动画
        UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.2, options: UIView.AnimationOptions.curveEaseInOut, animations: {
            self.secondView.transform = CGAffineTransform.init(rotationAngle: CGFloat.pi * 2 * CGFloat(date.second.intValue + 1) / 60)
            self.minuteView.transform = CGAffineTransform.init(rotationAngle: CGFloat.pi * 2 * CGFloat(date.minute.intValue) / 60)
            self.hourView.transform = CGAffineTransform.init(rotationAngle: CGFloat.pi * 2 * CGFloat(date.hour.intValue) / 24)
            self.dayView.transform = CGAffineTransform.init(rotationAngle: CGFloat.pi * 2 * CGFloat(date.day.intValue) / CGFloat(date.monthLength))
            self.monthView.transform = CGAffineTransform.init(rotationAngle: CGFloat.pi * 2 * CGFloat(date.month.intValue) / 12)
            self.yearLabel.text = "\(date.year)年"
        }, completion: nil)
        
    }

    deinit {
        timer?.invalidate()
        timer = nil
    }
}

/// 日期的扩展
public extension Date {

    /// 获取当前月的所有天数
    var monthLength: Int {
        let calendar = Calendar.current
        let range = calendar.range(of: Calendar.Component.day, in: Calendar.Component.month, for: Date())
        return range?.count ?? 30
    }

    /// 获取当前时间的年份
    var year: String {
        return string(from:"yyyy")
    }

    /// 获取当前时间的月份
    var month: String {
        return string(from: "MM")
    }

    /// 获取当前时间的所在日
    var day: String {
        return string(from: "dd")
    }

    /// 获取当前时间的小时
    var hour: String {
        return string(from: "HH")
    }

    /// 获取当前时间的分钟
    var minute: String {
        return string(from: "mm")
    }

    /// 获取当前时间的秒
    var second: String {
        return string(from: "ss")
    }

    private func string(from format: String) -> String {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = format
        return dateFormatter.string(from: self)
    }
}

/// 字符串的扩展
public extension String {
    var intValue: Int {
        return Int(self) ?? 0
    }
}

/// 字体扩展
public extension UIFont {
    static var timeFont: UIFont {
        return UIFont.systemFont(ofSize: 10)
    }
}

/// 颜色扩展
public extension UIColor {
    static var randomColor: UIColor {
        let red = CGFloat.random(in: 0.0...1.0)
        let green = CGFloat.random(in: 0.0...1.0)
        let blue = CGFloat.random(in: 0.0...1.0)
        let alpha = CGFloat.random(in: 0.0...0.0)
        return UIColor(red: red, green: green, blue: blue, alpha: alpha)
    }
    
    static var timeColor: UIColor {
        return white
    }
}





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

推荐阅读更多精彩内容