给 CAShapeLayer 一个 3d 效果
Giving a CAShapeLayer a 3d effect
我正在 swift 玩倒数计时器。我想给倒计时圈一个3D效果的阴影。
下面的代码将完美地操作倒计时时间,但我想它更多的是我试图用它编辑的视觉效果。有什么办法可以使倒计时圆变大,同时赋予它 3d 效果。如果您 运行 代码,您将看到它只是一种二维填充。我一直在玩弄不同颜色和 alpha 的重叠圆圈,比如深色,试图让它看起来更 3d,但它绝对不是最有效的,因为它涉及一次绘制多个圆圈。有没有办法获得类似下图的 3d 效果,而无需重新绘制多个重叠的圆圈。下面的代码用于计数器的基本二维平面版本。
//for countdown timer: ----------------
let timeLeftShapeLayer = CAShapeLayer()
let bgShapeLayer = CAShapeLayer()
var timeLeft: TimeInterval = 10
var endTime: Date?
var timeLabel = UILabel()
var timer = Timer()
// here you create your basic animation object to animate the strokeEnd
let strokeIt = CABasicAnimation(keyPath: "strokeEnd")
func drawBgShape() {
bgShapeLayer.path = UIBezierPath(arcCenter: CGPoint(x: view.frame.midX , y: view.frame.midY), radius:
100, startAngle: -90.degreesToRadians, endAngle: 270.degreesToRadians, clockwise: true).cgPath
bgShapeLayer.strokeColor = UIColor.white.cgColor
bgShapeLayer.fillColor = UIColor.clear.cgColor
bgShapeLayer.lineWidth = 15
view.layer.addSublayer(bgShapeLayer)
}
func drawTimeLeftShape() {
timeLeftShapeLayer.path = UIBezierPath(arcCenter: CGPoint(x: view.frame.midX , y: view.frame.midY), radius:
100, startAngle: -90.degreesToRadians, endAngle: 270.degreesToRadians, clockwise: true).cgPath
timeLeftShapeLayer.strokeColor = UIColor.red.cgColor
timeLeftShapeLayer.fillColor = UIColor.clear.cgColor
timeLeftShapeLayer.lineWidth = 15
view.layer.addSublayer(timeLeftShapeLayer)
}
func addTimeLabel() {
timeLabel = UILabel(frame: CGRect(x: view.frame.midX-50 ,y: view.frame.midY/3, width: 100, height: 50))
timeLabel.textAlignment = .center
timeLabel.text = timeLeft.time
view.addSubview(timeLabel)
}
@objc func updateTime() {
if timeLeft > 0 {
timeLeft = endTime?.timeIntervalSinceNow ?? 0
timeLabel.text = timeLeft.time
} else {
timeLabel.text = "00:00"
timer.invalidate()
}
}
//-------------timer finish------------(extension for timer at bottom of file------
扩展:
//extensions for timer
extension TimeInterval {
var time: String {
return String(format:"%02d:%02d", Int(self/60), Int(ceil(truncatingRemainder(dividingBy: 60))) )
}
}
extension Int {
var degreesToRadians : CGFloat {
return CGFloat(self) * .pi / 180
}
}
ViewDidload:
//for countdown timer: -------------
view.backgroundColor = UIColor(white: 0.94, alpha: 1.0)
drawBgShape()
drawTimeLeftShape()
addTimeLabel()
// here you define the fromValue, toValue and duration of your animation
strokeIt.fromValue = 0
strokeIt.toValue = 1
strokeIt.duration = timeLeft
// add the animation to your timeLeftShapeLayer
timeLeftShapeLayer.add(strokeIt, forKey: nil)
// define the future end time by adding the timeLeft to now Date()
endTime = Date().addingTimeInterval(timeLeft)
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
要获得 3D 效果,您通常会使用颜色渐变。在您的用例中,您将使用径向 CAGradientLayer。您必须屏蔽此层以仅查看您希望可见的区域。要填充的路径由外圈和内圈的区域组成。
这个填充路径可以创建如下:
let path = UIBezierPath(arcCenter: centerPoint, radius: outerRadius, startAngle: 0, endAngle: .pi * 2, clockwise: true)
let inner = UIBezierPath(arcCenter: centerPoint, radius: outerRadius - thickness, startAngle: 0, endAngle: .pi * 2, clockwise: true)
path.append(inner.reversing())
对于渐变,您可以使用 locations
参数指定一个 NSNumber 对象数组,这些对象定义渐变停止点的位置。这些值必须在 [0,1] 范围内。 CGColor类型对应的关联颜色设置在colors
属性.
在简单的情况下,您可以定义如下内容:
gradient.locations = [0, //0
NSNumber(value: innerRadius / outerRadius), //1
NSNumber(value: middle / outerRadius), //2
1] //3
let colors = [color, //0
color, //1
color.lighter(), //2
color, //3
]
gradient.colors = colors.map { [=11=].cgColor }
但是,只有在应用相应路径的蒙版后,才能看到所需的 3D 外观,请参见右图:
动画
很容易看出,你可以用一张CAGradientLayer
作为背景,一张作为前景。那么问题自然而然地出现了,我们如何用前景渐变来动画填充过程?
这可以通过将前景渐变置于背景渐变之上并使用 CAShapeLayer 作为前景渐变的遮罩来实现。这样做时,动画的完成方式类似于您问题中使用 strokeEnd
属性 的示例。由于是遮罩,前景渐变逐渐可见
渐变
渐变可以包含多个区域。 3D 效果通常通过组合相似颜色的稍浅或较深的渐变来实现。对于演示示例,我使用了这个经过最少修改的漂亮 answer 来获得更亮或更暗的颜色变体。
演示
使用以上几点,这可能如下所示:
颜色、距离、渐变当然要看需求了,这只是个例子,怎么做出来的。对于前景渐变,阴影区域(内部圆形区域)和外部圆形区域(在光线中)选择了两种相似但不同的颜色。
Self-Contained 完整示例
CircleProgressView.swift
import UIKit
class CircleProgressView: UIView {
private let backgroundGradient = CAGradientLayer()
private let foregroundGradient = CAGradientLayer()
private let timeLeftShapeLayer = CAShapeLayer()
private let backgroundMask = CAShapeLayer()
private let thickness: CGFloat
private let innerBackgroundColor: UIColor
private let outerBackgroundColor: UIColor
private let innerForegroundColor: UIColor
private let outerForegroundColor: UIColor
init(_ thickness: CGFloat,
_ innerBackgroundColor: UIColor,
_ outerBackgroundColor: UIColor,
_ innerForegroundColor: UIColor,
_ outerForegroundColor: UIColor) {
self.thickness = thickness
self.innerBackgroundColor = innerBackgroundColor
self.outerBackgroundColor = outerBackgroundColor
self.innerForegroundColor = innerForegroundColor
self.outerForegroundColor = outerForegroundColor
super.init(frame: .zero)
backgroundGradient.type = .radial
layer.addSublayer(backgroundGradient)
foregroundGradient.type = .radial
layer.addSublayer(foregroundGradient)
timeLeftShapeLayer.strokeColor = UIColor.white.cgColor
timeLeftShapeLayer.fillColor = UIColor.clear.cgColor
timeLeftShapeLayer.lineWidth = thickness
layer.addSublayer(timeLeftShapeLayer)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func circle(_ gradient: CAGradientLayer,
_ path: UIBezierPath,
_ outerRadius: CGFloat,
_ innerColor: UIColor,
_ outerColor: UIColor) {
let innerRadius = outerRadius - thickness
let middle = outerRadius - thickness / 2
let slice: CGFloat = thickness / 16
gradient.frame = bounds
gradient.locations = [0, //0
NSNumber(value: (innerRadius) / outerRadius), //1
NSNumber(value: (middle - slice) / outerRadius), //2
NSNumber(value: (middle) / outerRadius), //3
NSNumber(value: (middle + slice) / outerRadius), //4
1] //5
let colors = [innerColor, //0
innerColor, //1
innerColor.darker(), //2
outerColor, //3
outerColor.lighter(), //4
outerColor //5
]
gradient.colors = colors.map { [=12=].cgColor }
gradient.bounds = path.bounds
gradient.startPoint = CGPoint(x: 0.5, y: 0.5)
gradient.endPoint = CGPoint(x: 1, y: 1)
}
override func layoutSubviews() {
super.layoutSubviews()
let outerRadius: CGFloat = min(bounds.width, bounds.height) / 2.0
let centerPoint = CGPoint(x: bounds.midX, y: bounds.midY)
let path = UIBezierPath(arcCenter: centerPoint, radius: outerRadius, startAngle: 0, endAngle: .pi * 2, clockwise: true)
let inner = UIBezierPath(arcCenter: centerPoint, radius: outerRadius - thickness, startAngle: 0, endAngle: .pi * 2, clockwise: true)
path.append(inner.reversing())
circle(backgroundGradient, path, outerRadius, innerBackgroundColor, outerBackgroundColor)
backgroundMask.frame = bounds
backgroundMask.path = path.cgPath
backgroundMask.lineWidth = 0
backgroundGradient.mask = backgroundMask
circle(foregroundGradient, path, outerRadius, innerForegroundColor, outerForegroundColor)
let middlePath = UIBezierPath(arcCenter: centerPoint, radius: outerRadius - thickness / 2, startAngle: 0, endAngle: .pi * 2, clockwise: true)
middlePath.lineWidth = thickness
timeLeftShapeLayer.path = middlePath.cgPath
foregroundGradient.mask = timeLeftShapeLayer
timeLeftShapeLayer.strokeEnd = 0
}
func startAnimation() {
timeLeftShapeLayer.removeAllAnimations()
timeLeftShapeLayer.strokeEnd = 1
DispatchQueue.main.async {
let strokeIt = CABasicAnimation(keyPath: "strokeEnd")
strokeIt.fromValue = 0
strokeIt.toValue = 1
strokeIt.duration = 5
self.timeLeftShapeLayer.add(strokeIt, forKey: nil)
}
}
}
UIColor+Brightness.swift
仅出于完整性考虑,请注意,原文可在 找到。
import UIKit
extension UIColor {
func lighter(amount : CGFloat = 0.15) -> UIColor {
return hueColorWithBrightness(amount: 1 + amount)
}
func darker(amount : CGFloat = 0.15) -> UIColor {
return hueColorWithBrightness(amount: 1 - amount)
}
private func hueColorWithBrightness(amount: CGFloat) -> UIColor {
var hue: CGFloat = 0
var saturation: CGFloat = 0
var brightness: CGFloat = 0
var alpha: CGFloat = 0
if getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) {
return UIColor( hue: hue,
saturation: saturation,
brightness: brightness * amount,
alpha: alpha )
} else {
return self
}
}
}
ViewController.swift
调用并不奇怪,应该看起来像这样:
import UIKit
class ViewController: UIViewController {
private var circleProgressView: CircleProgressView?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(red: 0xBB / 0xFF, green: 0xBB / 0xFF, blue: 0xBB / 0xFF, alpha: 1)
let innerBackgroundColor = UIColor(red: 0x65 / 0xFF, green: 0x79 / 0xFF, blue: 0x85 / 0xFF, alpha: 1)
let outerBackgroundColor = innerBackgroundColor
let innerForegroundColor = UIColor(red: 0xCF / 0xFF, green: 0xC9 / 0xFF, blue: 0x22 / 0xFF, alpha: 1)
let outerForegroundColor = UIColor(red: 0xF3 / 0xFF, green: 0xCA / 0xFF, blue: 0x46 / 0xFF, alpha: 1)
let progressView = CircleProgressView(24, innerBackgroundColor, outerBackgroundColor, innerForegroundColor, outerForegroundColor)
circleProgressView = progressView
progressView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(progressView)
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
button.setTitle("Start", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(onStart), for: .touchUpInside)
let margin: CGFloat = 24
NSLayoutConstraint.activate([
progressView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: margin),
progressView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: margin),
progressView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -margin),
button.topAnchor.constraint(equalTo: progressView.bottomAnchor, constant: margin),
button.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: margin),
button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -margin),
button.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -margin),
])
}
@objc func onStart() {
circleProgressView?.startAnimation()
}
}
我正在 swift 玩倒数计时器。我想给倒计时圈一个3D效果的阴影。 下面的代码将完美地操作倒计时时间,但我想它更多的是我试图用它编辑的视觉效果。有什么办法可以使倒计时圆变大,同时赋予它 3d 效果。如果您 运行 代码,您将看到它只是一种二维填充。我一直在玩弄不同颜色和 alpha 的重叠圆圈,比如深色,试图让它看起来更 3d,但它绝对不是最有效的,因为它涉及一次绘制多个圆圈。有没有办法获得类似下图的 3d 效果,而无需重新绘制多个重叠的圆圈。下面的代码用于计数器的基本二维平面版本。
//for countdown timer: ----------------
let timeLeftShapeLayer = CAShapeLayer()
let bgShapeLayer = CAShapeLayer()
var timeLeft: TimeInterval = 10
var endTime: Date?
var timeLabel = UILabel()
var timer = Timer()
// here you create your basic animation object to animate the strokeEnd
let strokeIt = CABasicAnimation(keyPath: "strokeEnd")
func drawBgShape() {
bgShapeLayer.path = UIBezierPath(arcCenter: CGPoint(x: view.frame.midX , y: view.frame.midY), radius:
100, startAngle: -90.degreesToRadians, endAngle: 270.degreesToRadians, clockwise: true).cgPath
bgShapeLayer.strokeColor = UIColor.white.cgColor
bgShapeLayer.fillColor = UIColor.clear.cgColor
bgShapeLayer.lineWidth = 15
view.layer.addSublayer(bgShapeLayer)
}
func drawTimeLeftShape() {
timeLeftShapeLayer.path = UIBezierPath(arcCenter: CGPoint(x: view.frame.midX , y: view.frame.midY), radius:
100, startAngle: -90.degreesToRadians, endAngle: 270.degreesToRadians, clockwise: true).cgPath
timeLeftShapeLayer.strokeColor = UIColor.red.cgColor
timeLeftShapeLayer.fillColor = UIColor.clear.cgColor
timeLeftShapeLayer.lineWidth = 15
view.layer.addSublayer(timeLeftShapeLayer)
}
func addTimeLabel() {
timeLabel = UILabel(frame: CGRect(x: view.frame.midX-50 ,y: view.frame.midY/3, width: 100, height: 50))
timeLabel.textAlignment = .center
timeLabel.text = timeLeft.time
view.addSubview(timeLabel)
}
@objc func updateTime() {
if timeLeft > 0 {
timeLeft = endTime?.timeIntervalSinceNow ?? 0
timeLabel.text = timeLeft.time
} else {
timeLabel.text = "00:00"
timer.invalidate()
}
}
//-------------timer finish------------(extension for timer at bottom of file------
扩展:
//extensions for timer
extension TimeInterval {
var time: String {
return String(format:"%02d:%02d", Int(self/60), Int(ceil(truncatingRemainder(dividingBy: 60))) )
}
}
extension Int {
var degreesToRadians : CGFloat {
return CGFloat(self) * .pi / 180
}
}
ViewDidload:
//for countdown timer: -------------
view.backgroundColor = UIColor(white: 0.94, alpha: 1.0)
drawBgShape()
drawTimeLeftShape()
addTimeLabel()
// here you define the fromValue, toValue and duration of your animation
strokeIt.fromValue = 0
strokeIt.toValue = 1
strokeIt.duration = timeLeft
// add the animation to your timeLeftShapeLayer
timeLeftShapeLayer.add(strokeIt, forKey: nil)
// define the future end time by adding the timeLeft to now Date()
endTime = Date().addingTimeInterval(timeLeft)
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
要获得 3D 效果,您通常会使用颜色渐变。在您的用例中,您将使用径向 CAGradientLayer。您必须屏蔽此层以仅查看您希望可见的区域。要填充的路径由外圈和内圈的区域组成。
这个填充路径可以创建如下:
let path = UIBezierPath(arcCenter: centerPoint, radius: outerRadius, startAngle: 0, endAngle: .pi * 2, clockwise: true)
let inner = UIBezierPath(arcCenter: centerPoint, radius: outerRadius - thickness, startAngle: 0, endAngle: .pi * 2, clockwise: true)
path.append(inner.reversing())
对于渐变,您可以使用 locations
参数指定一个 NSNumber 对象数组,这些对象定义渐变停止点的位置。这些值必须在 [0,1] 范围内。 CGColor类型对应的关联颜色设置在colors
属性.
在简单的情况下,您可以定义如下内容:
gradient.locations = [0, //0
NSNumber(value: innerRadius / outerRadius), //1
NSNumber(value: middle / outerRadius), //2
1] //3
let colors = [color, //0
color, //1
color.lighter(), //2
color, //3
]
gradient.colors = colors.map { [=11=].cgColor }
但是,只有在应用相应路径的蒙版后,才能看到所需的 3D 外观,请参见右图:
动画
很容易看出,你可以用一张CAGradientLayer
作为背景,一张作为前景。那么问题自然而然地出现了,我们如何用前景渐变来动画填充过程?
这可以通过将前景渐变置于背景渐变之上并使用 CAShapeLayer 作为前景渐变的遮罩来实现。这样做时,动画的完成方式类似于您问题中使用 strokeEnd
属性 的示例。由于是遮罩,前景渐变逐渐可见
渐变
渐变可以包含多个区域。 3D 效果通常通过组合相似颜色的稍浅或较深的渐变来实现。对于演示示例,我使用了这个经过最少修改的漂亮 answer 来获得更亮或更暗的颜色变体。
演示
使用以上几点,这可能如下所示:
颜色、距离、渐变当然要看需求了,这只是个例子,怎么做出来的。对于前景渐变,阴影区域(内部圆形区域)和外部圆形区域(在光线中)选择了两种相似但不同的颜色。
Self-Contained 完整示例
CircleProgressView.swift
import UIKit
class CircleProgressView: UIView {
private let backgroundGradient = CAGradientLayer()
private let foregroundGradient = CAGradientLayer()
private let timeLeftShapeLayer = CAShapeLayer()
private let backgroundMask = CAShapeLayer()
private let thickness: CGFloat
private let innerBackgroundColor: UIColor
private let outerBackgroundColor: UIColor
private let innerForegroundColor: UIColor
private let outerForegroundColor: UIColor
init(_ thickness: CGFloat,
_ innerBackgroundColor: UIColor,
_ outerBackgroundColor: UIColor,
_ innerForegroundColor: UIColor,
_ outerForegroundColor: UIColor) {
self.thickness = thickness
self.innerBackgroundColor = innerBackgroundColor
self.outerBackgroundColor = outerBackgroundColor
self.innerForegroundColor = innerForegroundColor
self.outerForegroundColor = outerForegroundColor
super.init(frame: .zero)
backgroundGradient.type = .radial
layer.addSublayer(backgroundGradient)
foregroundGradient.type = .radial
layer.addSublayer(foregroundGradient)
timeLeftShapeLayer.strokeColor = UIColor.white.cgColor
timeLeftShapeLayer.fillColor = UIColor.clear.cgColor
timeLeftShapeLayer.lineWidth = thickness
layer.addSublayer(timeLeftShapeLayer)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func circle(_ gradient: CAGradientLayer,
_ path: UIBezierPath,
_ outerRadius: CGFloat,
_ innerColor: UIColor,
_ outerColor: UIColor) {
let innerRadius = outerRadius - thickness
let middle = outerRadius - thickness / 2
let slice: CGFloat = thickness / 16
gradient.frame = bounds
gradient.locations = [0, //0
NSNumber(value: (innerRadius) / outerRadius), //1
NSNumber(value: (middle - slice) / outerRadius), //2
NSNumber(value: (middle) / outerRadius), //3
NSNumber(value: (middle + slice) / outerRadius), //4
1] //5
let colors = [innerColor, //0
innerColor, //1
innerColor.darker(), //2
outerColor, //3
outerColor.lighter(), //4
outerColor //5
]
gradient.colors = colors.map { [=12=].cgColor }
gradient.bounds = path.bounds
gradient.startPoint = CGPoint(x: 0.5, y: 0.5)
gradient.endPoint = CGPoint(x: 1, y: 1)
}
override func layoutSubviews() {
super.layoutSubviews()
let outerRadius: CGFloat = min(bounds.width, bounds.height) / 2.0
let centerPoint = CGPoint(x: bounds.midX, y: bounds.midY)
let path = UIBezierPath(arcCenter: centerPoint, radius: outerRadius, startAngle: 0, endAngle: .pi * 2, clockwise: true)
let inner = UIBezierPath(arcCenter: centerPoint, radius: outerRadius - thickness, startAngle: 0, endAngle: .pi * 2, clockwise: true)
path.append(inner.reversing())
circle(backgroundGradient, path, outerRadius, innerBackgroundColor, outerBackgroundColor)
backgroundMask.frame = bounds
backgroundMask.path = path.cgPath
backgroundMask.lineWidth = 0
backgroundGradient.mask = backgroundMask
circle(foregroundGradient, path, outerRadius, innerForegroundColor, outerForegroundColor)
let middlePath = UIBezierPath(arcCenter: centerPoint, radius: outerRadius - thickness / 2, startAngle: 0, endAngle: .pi * 2, clockwise: true)
middlePath.lineWidth = thickness
timeLeftShapeLayer.path = middlePath.cgPath
foregroundGradient.mask = timeLeftShapeLayer
timeLeftShapeLayer.strokeEnd = 0
}
func startAnimation() {
timeLeftShapeLayer.removeAllAnimations()
timeLeftShapeLayer.strokeEnd = 1
DispatchQueue.main.async {
let strokeIt = CABasicAnimation(keyPath: "strokeEnd")
strokeIt.fromValue = 0
strokeIt.toValue = 1
strokeIt.duration = 5
self.timeLeftShapeLayer.add(strokeIt, forKey: nil)
}
}
}
UIColor+Brightness.swift
仅出于完整性考虑,请注意,原文可在 找到。
import UIKit
extension UIColor {
func lighter(amount : CGFloat = 0.15) -> UIColor {
return hueColorWithBrightness(amount: 1 + amount)
}
func darker(amount : CGFloat = 0.15) -> UIColor {
return hueColorWithBrightness(amount: 1 - amount)
}
private func hueColorWithBrightness(amount: CGFloat) -> UIColor {
var hue: CGFloat = 0
var saturation: CGFloat = 0
var brightness: CGFloat = 0
var alpha: CGFloat = 0
if getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) {
return UIColor( hue: hue,
saturation: saturation,
brightness: brightness * amount,
alpha: alpha )
} else {
return self
}
}
}
ViewController.swift
调用并不奇怪,应该看起来像这样:
import UIKit
class ViewController: UIViewController {
private var circleProgressView: CircleProgressView?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(red: 0xBB / 0xFF, green: 0xBB / 0xFF, blue: 0xBB / 0xFF, alpha: 1)
let innerBackgroundColor = UIColor(red: 0x65 / 0xFF, green: 0x79 / 0xFF, blue: 0x85 / 0xFF, alpha: 1)
let outerBackgroundColor = innerBackgroundColor
let innerForegroundColor = UIColor(red: 0xCF / 0xFF, green: 0xC9 / 0xFF, blue: 0x22 / 0xFF, alpha: 1)
let outerForegroundColor = UIColor(red: 0xF3 / 0xFF, green: 0xCA / 0xFF, blue: 0x46 / 0xFF, alpha: 1)
let progressView = CircleProgressView(24, innerBackgroundColor, outerBackgroundColor, innerForegroundColor, outerForegroundColor)
circleProgressView = progressView
progressView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(progressView)
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
button.setTitle("Start", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(onStart), for: .touchUpInside)
let margin: CGFloat = 24
NSLayoutConstraint.activate([
progressView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: margin),
progressView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: margin),
progressView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -margin),
button.topAnchor.constraint(equalTo: progressView.bottomAnchor, constant: margin),
button.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: margin),
button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -margin),
button.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -margin),
])
}
@objc func onStart() {
circleProgressView?.startAnimation()
}
}