環(huán)形倒計(jì)時(shí)

以前項(xiàng)目中有一個(gè)小小的需求,是圓形倒計(jì)時(shí)view,話不多說(shuō)請(qǐng)看圖


circle.gif

?怎么實(shí)現(xiàn)這樣的需求了?
?圓形可以通過(guò)貝塞爾曲線畫(huà)出來(lái),倒計(jì)時(shí)可以通過(guò)NSTimer或者CADisplayLink來(lái)實(shí)現(xiàn),我選擇的是后者,后者的刷新頻率是1/60s,和UI的刷新屏幕是一毛一樣的,可以避免卡頓的問(wèn)題。

// 默認(rèn)init方法起始點(diǎn)在CGFloat.pi * (-0.5)圓的最上方
//drawView = MZDrawView(frame: CGRect(x: 100, y: 100, width: 100, height: 200))
// 當(dāng)然也可以設(shè)置圓的起始點(diǎn)
drawView = MZDrawView(frame: CGRect(x: 100, y: 100, width: 100, height: 100), angle: CGFloat.pi * (0.5))
drawView?.backPathColor = UIColor.red
drawView?.cycleColor = UIColor.cyan
// 倒計(jì)時(shí)時(shí)間
drawView?.duration = 10
// 半徑 (不設(shè)置的話就默認(rèn)為MZDrawView的最大內(nèi)切圓的半徑)
drawView?.radius = 20
self.view.addSubview(drawView!)

  • 初始化DrawView,可以指定起始點(diǎn)
// MARK: init func
    
    convenience init(frame: CGRect ,angle:CGFloat) {
        self.init(frame: frame)
        self.angle = angle
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.addSubview(self.timerLabel)
        let width = self.frame.size.width - lineWidth * 2 - Margin * 2
        let height = self.frame.size.height - lineWidth * 2 - Margin * 2
        self.timerLabel.frame = CGRect(x: (self.frame.size.height - height)  * 0.5, y: (self.frame.size.width - width)  * 0.5, width: width, height: height)
        self.timerLabel.textColor = textColor ?? UIColor.white
        
    }
  • drawView的基本屬性可以設(shè)置倒計(jì)時(shí)view的樣式
 /// 圓圈的寬度
    var lineWidth: CGFloat = 5.0
    private var displayLink: CADisplayLink?
    var backPathColor: UIColor = UIColor.clear
    /// 圓圈顏色
    var cycleColor: UIColor?
    /// 倒計(jì)時(shí)時(shí)間label的文字顏色
    var textColor: UIColor? {
        didSet {
            self.timerLabel.textColor = textColor ?? UIColor.white
        }
    }
    // 默認(rèn)起始和結(jié)束都在圓的頂點(diǎn)
    var angle: CGFloat = CGFloat.pi * (-0.5)
    /// 自定義半徑
    var radius: CGFloat = 0.0
    /// 自定義圓的中心點(diǎn)
    var cycleCenter: CGPoint = CGPoint.zero
    // 倒計(jì)時(shí)時(shí)間
    var duration: Double = 0.0 {
        didSet {
            if duration < 0.0 { return }
            // 先停掉link
            invalidateLink()
            // 在開(kāi)啟link
            self.displayLink = CADisplayLink(target: self, selector: #selector(countDown))
            self.displayLink?.add(to: RunLoop.current, forMode: .commonModes)
            
        }
    }
    // 當(dāng)前進(jìn)度
    var progress: Double = 1.0 {
        didSet {
            if progress < 0 || progress > 1 {
                return
            }
            // ceil(返回不小于x的最小整數(shù)值)
            self.timerLabel.text = "\(ceil(self.progress * duration))"
            self.setNeedsDisplay()
        }
    }
  • invalid計(jì)時(shí)器方法

    ?當(dāng)計(jì)時(shí)器還在work的時(shí)候,如果不需要使用了一定要停掉計(jì)時(shí)器
    ?當(dāng)計(jì)時(shí)器還在work的時(shí)候,如果不需要使用了一定要停掉計(jì)時(shí)器
    ?當(dāng)計(jì)時(shí)器還在work的時(shí)候,如果不需要使用了一定要停掉計(jì)時(shí)器

/// 在不用的時(shí)候要call一下 停掉link
    // 比如 vc的deinit方法里
    open func invalidateLink() {
        guard let displayLink = self.displayLink else {
            return
        }
        displayLink.isPaused = true
        displayLink.invalidate()
    }
  • 根據(jù)時(shí)間計(jì)算比例然后畫(huà)圓
/// 倒計(jì)時(shí) 1/60s調(diào)用一次
    @objc private func countDown() {
        if progress < 0.0 || progress > 1.0 {
            invalidateLink()
            self.progress = 0.0
            return
        }
        self.progress -= Double((1/60) / self.duration)
    }

詳情請(qǐng)見(jiàn)demo

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容