swift UIProgressView 進度條

UIProgressView 多用于顯示某項任務的進度,比如下載的進度娱挨,也是一個比較簡單的控件余指。

import Foundation
import UIKit
import _SwiftUIKitOverlayShims

extension UIProgressView {

    
    public enum Style : Int {

        // 默認樣式 進度條會有個背景
        // 高度 2px
        case `default`
      
                // 沒有背景,多用于UIBarButtonItem 中
        // 比如打開某個頁面跷坝,需要加載數(shù)據(jù)酵镜,可以添加這個進度條顯示加載的進度
        // 高度 3px
        case bar
    }
}

@available(iOS 2.0, *)
open class UIProgressView : UIView, NSCoding {

    
    public init(frame: CGRect)

    public init?(coder aDecoder: NSCoder)

    public convenience init(progressViewStyle style: UIProgressView.Style) // sets the view height according to the style

    
    open var progressViewStyle: UIProgressView.Style // default is UIProgressViewStyleDefault

    // 0.0 - 1.0 之間的Float值
    open var progress: Float // 0.0 .. 1.0, default is 0.0. values outside are pinned.

    @available(iOS 5.0, *)
    // 進度條的顏色
    open var progressTintColor: UIColor?

    @available(iOS 5.0, *)
    // 未完成部分的顏色
    open var trackTintColor: UIColor?

    @available(iOS 5.0, *)
    // 可以使用圖標表示已完成部分進度
    open var progressImage: UIImage?

    @available(iOS 5.0, *)
    // 未完成部分也可以使用圖片
    open var trackImage: UIImage?

    
    @available(iOS 5.0, *)
    // 設置進度條的值碉碉, 可以使用動畫
    open func setProgress(_ progress: Float, animated: Bool)

    
    @available(iOS 9.0, *)
    // 配合Progess, 用于觀察進度值
    open var observedProgress: Progress?
}

屬性

上面的接口基本上將屬性進行了初略的介紹,下面圖示對其進行簡單的描述

UIProgressView 基本樣式屬性.jpg

Progress View 的高度設置

上面的介紹中淮韭,已經(jīng)講明了2中不同樣式的progress view 都存在一個默認的高度

  • default: 2px
  • bar: 3px

直接設置其高度是不會生效的誉裆,比如:

let pv = UIProgressView(progressViewStyle: .default)
// 通過設置其frame的height 無效
pv.frame = CGRect(x: 0, y: 0, width: 200, height: 20)

有2中方式對高度進行設置:

  1. 使用自動布局約束,對高度進行約束

    let pv = UIProgressView(progressViewStyle: .default)
    pv.progressTintColor = .red // 完成部分 進度條顏色為紅色
    pv.trackTintColor = .gray  // 未完成部分 顏色為灰色
    pv.progress = 0.5 // 設置其初始進度為 50% 位置缸濒, 默認是 0.0
    pv.translatesAutoresizingMaskIntoConstraints = false
    // 或者使用Interface Builder進行約束布局
    NSLayoutConstraint.activate([
         // 對高度進行約束
        pv.heightAnchor.constraint(equalToConstant: 20)
        // ...
    ])
    
  2. 使用 tranformy 進行轉(zhuǎn)換

    let pv = UIProgressView(progressViewStyle: .default)
    pv.transform = CGAffineTransform
    
    // 對高度擴大4倍
    // 即 default類型的高度為 2 * 4
    // bar類型的高度為 3 * 4
    pv.transform = pv.transform.scaledBy(x: 1, y: 4)
    // 或者使用
    pv.transform = CGAffineTransform(scaleX: 1.0, y: 4)
    

自定義進度條樣式

1. 使用自定義繪制圖片作為進度條

先在IB中使用autolayout將progress view的高度設置為 20足丢, 然后添加一個按鈕,使用 Timer庇配, 每隔 1s 將進度加 0.1斩跌。

使用 progressImage 設置自定義 進度圖片, 這個樣式為帶有黑色描邊的黃色進度條

import UIKit

class ViewController: UIViewController {

    // 對應IB中的UIProgressView
    @IBOutlet weak var progressView: UIProgressView!
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
    
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        
        let r = UIGraphicsImageRenderer(size: CGSize(width: 10, height: 10))
        let im = r.image { (ctx) in
            let con = ctx.cgContext
            // 設置填充顏色為黃色
            con.setFillColor(UIColor.yellow.cgColor)
            con.fill(CGRect(x: 0, y: 0, width: 10, height: 10))
            let b = con.boundingBoxOfClipPath.insetBy(dx: 1, dy: 1)
            con.setLineWidth(2)
            con.setStrokeColor(UIColor.black.cgColor) // 設置描邊顏色為 黑色
            con.stroke(b)
            con.strokeEllipse(in: b)
            
        }.resizableImage(withCapInsets: UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4), resizingMode: .stretch)
        
        // 將其設置為上面繪制的顏色
        self.progressView.progressImage = im
    }

    // IB中的UIButton的action
    @IBAction func startDownload(_ sender: Any) {
        self.progressView.progress = 0.0
        
        // 每個1s調(diào)用一次 inc函數(shù) 
        Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(inc), userInfo: nil, repeats: true)
    }


    @objc func inc(_ t: Timer) {
        // 使用 self.progressView.progress 獲取當前的進度值
        var val = Float(self.progressView.progress)
        val += 0.1
        self.progressView.setProgress(val, animated: true)
        
        if val >= 1.0 {
            t.invalidate()
        }
    }
}

最終效果:

progressImage.gif

進度條圖片:

自定義繪制 progressImage.jpg

2. 使用UIView對UIProgressView進行模擬

為了更加靈活的自定義進度樣式捞慌,可以使用 UIView 進行模擬操作耀鸦,然后調(diào)用 draw 方法進行繪制,它有一個 value 屬性啸澡,取值范圍為 0.0 - 1.0袖订, 然后調(diào)用 setNeedsDisplay 使得 draw 方法被重繪。

使用 UIGraphicsGetCurrentContext 繪制一個圓條形狀嗅虏,如下:

UIView IB.jpg

注意上面的 UIView 為了圖片展示洛姑,將其背景色設置為了橙色,實際運行的時候會將這個背景色去掉皮服。

// CustomProgressView.swift
import UIKit

class CustomProgressView: UIView {
    // 模擬progress屬性 取值范圍0.0-1.0
    var value: CGFloat = 0.0
    
    override func draw(_ rect: CGRect) {
        let c = UIGraphicsGetCurrentContext()!
        UIColor.white.set() // 設置進度條的背景色
        let ins: CGFloat = 2
        // 設置繪制區(qū)域 在原UIView尺寸的基礎上向內(nèi)縮 2 points
        let r = self.bounds.insetBy(dx: ins, dy: ins)
        // 2頭圓弧的半徑
        let radius: CGFloat = r.size.height / 2
        let d90 = CGFloat.pi / 2
      
        // 繪制路徑
        let path = CGMutablePath()
        path.move(to: CGPoint(x: r.maxX - radius, y: ins))
        path.addArc(center: CGPoint(x: radius+ins, y: radius+ins), radius: radius, startAngle: -d90, endAngle: d90, clockwise: true)
        path.addArc(center: CGPoint(x: r.maxX - radius, y: radius + ins), radius: radius, startAngle: d90, endAngle: -d90, clockwise: true)
        path.closeSubpath()
        c.addPath(path)
        c.setLineWidth(2)
        c.strokePath()
        c.addPath(path)
        c.clip()
        c.fill(CGRect(x: r.origin.x, y: r.origin.y, width: r.width * self.value, height: r.height))
    }
}

ViewController.swift

import UIKit

class ViewController: UIViewController {

   
    @IBOutlet weak var customProgressView: CustomProgressView!
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func startDownload(_ sender: Any) {
        self.customProgressView.value = 0.0
        // setNeedsDisplay使 draw 方法被調(diào)用
        self.customProgressView.setNeedsDisplay()
        Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(inc), userInfo: nil, repeats: true)
    }


    @objc func inc(_ t: Timer) {
        var val = Float(self.customProgressView.value)
        val += 0.1
        
        // 更新value值
        self.customProgressView.value = CGFloat(val)
        self.customProgressView.setNeedsDisplay()
        
        if val >= 1.0 {
            t.invalidate()
        }
    }
}

其效果:

CustomProgressView.gif

3.使用UIButton 繪制圓形進度條

使用 CAShapeLayerstrokeEnd 屬性來繪制一個圓弧型進度條楞艾,讓這個類繼承 UIButton

UIButton IB.jpg

CircleProgressButton:

import UIKit

class CircleProgressButton: UIButton {
    var progress: Float = 0 {
        didSet {
            if let layer = self.shapelayer {
                layer.strokeEnd = CGFloat(self.progress)
            }
        }
    }
    
    private var shapelayer: CAShapeLayer!
    private var didLayout = false
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        guard !self.didLayout else {
            return
        }
        
        self.didLayout = true
        
        let layer = CAShapeLayer()
        layer.frame = self.bounds
        layer.lineWidth = 2 // 圓弧的寬度
        layer.fillColor = nil // 填充顏色為空
        layer.strokeColor = UIColor.red.cgColor // 描邊顏色
        let b = UIBezierPath(ovalIn: self.bounds.insetBy(dx: 3, dy: 3)) // 貝塞爾路徑
        b.apply(CGAffineTransform(translationX: -self.bounds.width / 2, y: -self.bounds.height / 2))
        b.apply(CGAffineTransform(rotationAngle: -.pi/2.0))
        b.apply(CGAffineTransform(translationX: self.bounds.width / 2, y: self.bounds.height / 2))
        
        layer.path = b.cgPath
        self.layer.addSublayer(layer)
        layer.zPosition = -1
        layer.strokeStart = 0
        layer.strokeEnd = 0 // 使用這個模擬進度
        self.shapelayer = layer
    }
}

ViewController.swift:

import UIKit

class ViewController: UIViewController {

        // UIButton 按鈕
    @IBOutlet weak var circleProgress: CircleProgressButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func startDownload(_ sender: Any) {
        
        self.circleProgress.progress = 0.0
        Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(inc), userInfo: nil, repeats: true)
    }


    @objc func inc(_ t: Timer) {
        var val = Float(self.circleProgress.progress)
        val += 0.1
         
        self.circleProgress.progress = val
        
        if val >= 1.0 {
            t.invalidate()
        }
    }
}


效果圖如下:

UIButton Progress 效果圖.jpg

配合 Progress 對象

Progress 類提供了很多和任務進度相關的屬性:

  • totalUnitCount: 總的任務量
  • completedUnitCount: 完成的任務量
  • fractionCompleted: 這個是 completedUnitCount/totalUnitCount, 即可以表示進度的概念

另外UIProgressView 有一個 observedProgress 屬性,可以將一個 Progress 對象賦值給它龄广,progress view 會自動的進行更新(一種 KVO)硫眯。

import UIKit

class MySpyProgressView: UIProgressView {
    override func setProgress(_ progress: Float, animated: Bool) {
        super.setProgress(progress, animated: animated)
        print(progress)
    }
    
    override var progress: Float {
        get {
            return super.progress
        }
        
        set {
            super.progress = newValue
            print(progress)
        }
    }
}

class ProgressingOperation {
    let progress: Progress
    init(units: Int) {
        // totalUnitCount 任務總量
        self.progress = Progress(totalUnitCount: Int64(units))
    }
    
    func start() {
        Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(inc), userInfo: nil, repeats: true)
    }
    
    @objc func inc(_ t: Timer) {
        // 這里假設完成量+1
        self.progress.completedUnitCount += 1
      
        // fractionCompleted 即進度
        if self.progress.fractionCompleted >= 1.0 {
            t.invalidate()
            print("完成")
        }
    }
}


class ViewController: UIViewController {
    
    // 按鈕
    lazy var startButton: UIButton = {
        let button = UIButton()
        button.setTitle("start", for: .normal)
        button.setTitleColor(.red, for: .normal)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.addTarget(self, action: #selector(startDownload), for: .touchUpInside)
        return button
    }()

    // progress view
    lazy var progressView: MySpyProgressView = {
        let pv = MySpyProgressView()
        pv.translatesAutoresizingMaskIntoConstraints = false
        return pv
    }()
    
    var op: ProgressingOperation?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.view.addSubview(startButton)
        self.view.addSubview(progressView)
    
        NSLayoutConstraint.activate([
            progressView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            progressView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            progressView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            progressView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20)
            ])
        
        NSLayoutConstraint.activate([
            startButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            startButton.topAnchor.constraint(equalTo: progressView.bottomAnchor, constant: 20)
            ])
        
        
    }
    
    @objc func startDownload(_ sender: Any) {
        self.progressView.progress = 0.0
        self.op = ProgressingOperation(units: 10)
        // 使用 observedProgress 屬性 這個屬性會自動的更新progress view
        self.progressView.observedProgress = self.op!.progress
        self.op?.start()
    }
    
}

上面是 Progress 的一種用法,這個類功能比較齊全择同,還有很多功能有待以后進一步的學習两入。

2019年06月12日11:45:53

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市敲才,隨后出現(xiàn)的幾起案子裹纳,更是在濱河造成了極大的恐慌,老刑警劉巖归斤,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件痊夭,死亡現(xiàn)場離奇詭異,居然都是意外死亡脏里,警方通過查閱死者的電腦和手機她我,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人番舆,你說我怎么就攤上這事酝碳。” “怎么了恨狈?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵疏哗,是天一觀的道長。 經(jīng)常有香客問我禾怠,道長返奉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任吗氏,我火速辦了婚禮芽偏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘弦讽。我一直安慰自己污尉,他們只是感情好,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布往产。 她就那樣靜靜地躺著被碗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪仿村。 梳的紋絲不亂的頭發(fā)上锐朴,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天,我揣著相機與錄音奠宜,去河邊找鬼包颁。 笑死,一個胖子當著我的面吹牛压真,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蘑险,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼滴肿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了佃迄?” 一聲冷哼從身側(cè)響起泼差,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎呵俏,沒想到半個月后堆缘,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡普碎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年吼肥,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡缀皱,死狀恐怖斗这,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情啤斗,我是刑警寧澤表箭,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站钮莲,受9級特大地震影響免钻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜崔拥,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一伯襟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧握童,春花似錦姆怪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至肥卡,卻和暖如春溪掀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背步鉴。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工揪胃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人氛琢。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓喊递,卻偏偏與公主長得像,于是被迫代替她去往敵國和親阳似。 傳聞我的和親對象是個殘疾皇子骚勘,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容