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?
}
屬性
上面的接口基本上將屬性進行了初略的介紹,下面圖示對其進行簡單的描述
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中方式對高度進行設置:
-
使用自動布局約束,對高度進行約束
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) // ... ])
-
使用
tranform
對y
進行轉(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()
}
}
}
最終效果:
進度條圖片:
2. 使用UIView對UIProgressView進行模擬
為了更加靈活的自定義進度樣式捞慌,可以使用 UIView
進行模擬操作耀鸦,然后調(diào)用 draw
方法進行繪制,它有一個 value
屬性啸澡,取值范圍為 0.0 - 1.0
袖订, 然后調(diào)用 setNeedsDisplay
使得 draw
方法被重繪。
使用 UIGraphicsGetCurrentContext
繪制一個圓條形狀嗅虏,如下:
注意上面的 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()
}
}
}
其效果:
3.使用UIButton 繪制圓形進度條
使用 CAShapeLayer
的 strokeEnd
屬性來繪制一個圓弧型進度條楞艾,讓這個類繼承 UIButton
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()
}
}
}
效果圖如下:
配合 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