728x90
만들고자 하는 View는 위와 같은 형태로 현재까지 어떤 특정한 작업의 진행 정도를 시각적으로 보여주는 뷰이다.
CirlcleLayer
class CircleLayer: CALayer {
public var progressWidth: CGFloat = 0
public var progressColor: UIColor?
public var progressBackgroundColor: UIColor?
public var progress: CGFloat = 0
public var clockwise: Bool = false
public var progressLayer: CAShapeLayer?
var center: CGPoint {
return CGPoint(x: frame.size.width / 2, y: frame.size.height / 2)
}
var radius: CGFloat {
return min(frame.size.width, frame.size.height) / 2
}
private let startAngle: Radians = Degrees(0).toRadians()
private let endAngle: Radians = Degrees(360).toRadians()
override func draw(in ctx: CGContext) {
super.draw(in: ctx)
addCenterCircle(ctx)
addProgressBackground(ctx)
if progressLayer == nil {
let progressLayer = CAShapeLayer()
self.progressLayer = progressLayer
setProgressLayerStyle()
addSublayer(progressLayer)
} else {
setProgressLayerStyle()
}
}
private func addCenterCircle(_ ctx: CGContext) {
ctx.addArc(center: center, radius: radius - progressWidth / 2, startAngle: startAngle, endAngle: endAngle, clockwise: clockwise)
ctx.setFillColor(UIColor.white.cgColor)
ctx.fillPath()
}
private func addProgressBackground(_ ctx: CGContext) {
ctx.addArc(center: center, radius: radius - progressWidth / 2, startAngle: startAngle, endAngle: endAngle, clockwise: clockwise)
ctx.setLineWidth(progressWidth)
ctx.setStrokeColor(progressBackgroundColor?.cgColor ?? UIColor.white.cgColor)
ctx.strokePath()
ctx.fillPath()
}
private func setProgressLayerStyle() {
progressLayer?.path = UIBezierPath(arcCenter: center, radius: radius - progressWidth / 2, startAngle: startAngle - Degrees(90).toRadians(), endAngle: endAngle - Degrees(90).toRadians(), clockwise: clockwise).cgPath
progressLayer?.lineCap = .round
progressLayer?.lineWidth = progressWidth
progressLayer?.fillColor = UIColor.clear.cgColor
progressLayer?.strokeColor = progressColor?.cgColor
progressLayer?.strokeStart = 0
progressLayer?.strokeEnd = progress
progressLayer?.setNeedsDisplay()
}
func animate() {
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = 0
animation.toValue = progress
animation.repeatCount = 1
animation.duration = 0.7
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
progressLayer?.add(animation, forKey: "StrokeAnimation")
}
}
CircleProgressView의 layer가 될 CircleLayer이다.
크게 세가지 부분으로 구분된다.
1. 중앙 부분의 하얀색 원 (CenterCircle)
2. Progress Cirlce
3. Progress Cirlce의 뒷배경 (ProgressBackground)
여기서 Progress Circle은 따로 애니메이션을 넣을 것이므로 ctx로 그리지 않고 CAShapeLayer로 만들어서 sublayer로 추가했다.
addArc로 원을 그리되 fillColor는 쓰지 않고 storkeColor와 strokeStart, strokeEnd를 써서 현재 progress만큼 원의 테두리가 그려지게 했다.
CircleProgressView
public class CircleProgressView: UIView {
@IBInspectable
public var progressColor: UIColor? {
didSet {
circleLayer.progressColor = progressColor
circleLayer.setNeedsDisplay()
}
}
@IBInspectable
public var progressBackgroundColor: UIColor? {
didSet {
circleLayer.progressBackgroundColor = progressBackgroundColor
circleLayer.setNeedsDisplay()
}
}
@IBInspectable
public var progress: CGFloat = 0 {
didSet {
circleLayer.progress = progress
circleLayer.setNeedsDisplay()
}
}
@IBInspectable
public var progressWidth: CGFloat = 0 {
didSet {
circleLayer.progressWidth = progressWidth
circleLayer.setNeedsDisplay()
}
}
@IBInspectable
public var clockwise: Bool {
set {
circleLayer.clockwise = !newValue
circleLayer.setNeedsDisplay()
}
get {
return !circleLayer.clockwise
}
}
private var circleLayer: CircleLayer {
return layer as! CircleLayer
}
public override class var layerClass: AnyClass {
return CircleLayer.self
}
public override init(frame: CGRect) {
super.init(frame: frame)
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
}
public func startProgressAnimate() {
circleLayer.animate()
}
}
CircleLayer가 들어가는 CircleProgressView는
progress
progressWidth
progressColor
progressBackgroundColor
clockwise
위의 값들을 IBInspectable property로 선언하여 Xib에서 값을 설정할 수 있도록 했다.
그리고 clockwise 값은 이상하게도 원을 그릴 때 분명 시계방향으로 그려져야 하는데 시계반대방향으로 그려지고
시계반대방향으로 설정하면 시계방향으로 그려져서 set 블록에서 반대로 set하도록 했다.
아래는 strokeEnd 애니메이션 효과이다
728x90
'iOS > 예제' 카테고리의 다른 글
[iOS 예제] Roulette 만들기 (0) | 2022.06.25 |
---|---|
[iOS 예제] Ripple Effect (0) | 2022.06.19 |
[iOS 예제] UIView에 원형으로 shadow 넣기 (0) | 2021.12.07 |
[iOS 예제] Drag and Drop가능한 UIView만들기 (0) | 2021.12.05 |
[iOS 예제] InfiniteTextView 무한 스크롤 텍스트뷰 만들기 (0) | 2021.06.17 |
댓글