Ios CAShapeLayer()绘制奇怪的线/路径
我有一个顶部有渐变的Ios CAShapeLayer()绘制奇怪的线/路径,ios,swift,cashapelayer,cabasicanimation,Ios,Swift,Cashapelayer,Cabasicanimation,我有一个顶部有渐变的CAShapeLayer(),正在设置动画,但不知怎么的,它看起来像下图: 怎么会是这个样子 我的代码: override func viewDidLayoutSubviews() { displayLine() } override func viewDidAppear(_ animated: Bool) { animateStroke() } func displayLine() { let trackLayer = CAShapeLayer
CAShapeLayer()
,正在设置动画,但不知怎么的,它看起来像下图:
怎么会是这个样子
我的代码:
override func viewDidLayoutSubviews() {
displayLine()
}
override func viewDidAppear(_ animated: Bool) {
animateStroke()
}
func displayLine() {
let trackLayer = CAShapeLayer()
let rect = CGRect(x: topView.frame.width * 0.15, y: topView.frame.size.height / 1.5, width: topView.frame.width * 0.7, height: 2)
let path = UIBezierPath(roundedRect: rect, cornerRadius: 1)
trackLayer.path = path.cgPath
trackLayer.strokeColor = UIColor.groupTableViewBackground.cgColor
trackLayer.lineWidth = 3
trackLayer.fillColor = UIColor.clear.cgColor
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = UIColor.green.cgColor
shapeLayer.lineWidth = 4
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeEnd = 0
topView.layer.addSublayer(trackLayer)
topView.layer.addSublayer(shapeLayer)
let color = UIColor(red: 11/255, green: 95/255, blue: 244/255, alpha: 1).cgColor
let sndColor = UIColor(red: 255/255, green: 87/255, blue: 87/255, alpha: 1).cgColor
gradient.colors = [color, sndColor]
gradient.locations = [0.0, 1.0]
gradient.startPoint = CGPoint(x: 0, y: 0)
gradient.endPoint = CGPoint(x: 1, y: 0)
gradient.frame = topView.bounds
gradient.mask = shapeLayer
topView.layer.addSublayer(gradient)
}
func animateStroke() {
if !animated {
animated = true
let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
var value: Double?
let distance = currLeasingCar!.currentKm - currLeasing!.startKm
value = Double(distance) / Double(finalKm)
basicAnimation.toValue = value
basicAnimation.duration = 1.5
basicAnimation.fillMode = .forwards
basicAnimation.isRemovedOnCompletion = false
basicAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
shapeLayer.add(basicAnimation, forKey: "lineStrokeAnimation")
}
}
问题是您的路径是一个圆角矩形。在您与我们共享的图像中,大概有2-3%的笔划。将其更改为笔划90%,您将看到它试图绘制一个宽且极短的圆形矩形,例如: 相反,只需将路径设为一条直线,即可按预期工作:
let path = UIBezierPath()
let bounds = topView.bounds
path.move(to: CGPoint(x: bounds.minX + bounds.width * 0.15, y: bounds.minY + bounds.height / 1.5))
path.addLine(to: CGPoint(x: bounds.minX + bounds.width * 0.85, y: bounds.minY + bounds.height / 1.5))
您可能还希望使形状层的封口变圆:
trackLayer.lineCap = .round // or whatever you want
shapeLayer.lineCap = .round
当然,此更改丢失了原始路径的2点高度,因此如果要使这些形状层更厚,只需增加它们各自的线宽
值
一些不相关的观察结果:
和viewDidLayoutSubviews()
应该调用它们的ViewDidDisplay(:)
实现super
可以多次调用,因此您不希望每次都实例化一个新的viewdilayoutsubviews()
。或者,如果您这样做,请确保删除之前的一个trackLayer
- 添加子视图/子层时,谨慎使用
而不是边界
。在这种情况下,这可能无关紧要,但在某些情况下,您可能会遇到各种奇怪的问题,因为框架
位于视图的superview坐标系中,而frame
是所讨论视图的坐标系bounds
就个人而言,如果要将此代码保存在视图控制器中,我建议:
- 在
中添加形状层和渐变李>viewDidLoad
- 从
更新渐变的路径和边界李>viewdilayoutsubviews
- 我将这些不同的形状层和渐变方法放在它们自己的私有扩展中
UIView
子类(或子视图控制器)
因此,也许:
@IBDesignable
public class GradientProgressView: UIView {
private var shapeLayer: CAShapeLayer = {
let shapeLayer = CAShapeLayer()
shapeLayer.strokeColor = UIColor.green.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineCap = .round
return shapeLayer
}()
private var trackLayer: CAShapeLayer = {
let trackLayer = CAShapeLayer()
trackLayer.strokeColor = UIColor.groupTableViewBackground.cgColor
trackLayer.fillColor = UIColor.clear.cgColor
trackLayer.lineCap = .round
return trackLayer
}()
private var gradient: CAGradientLayer = {
let gradient = CAGradientLayer()
let color = UIColor(red: 11/255, green: 95/255, blue: 244/255, alpha: 1).cgColor
let sndColor = UIColor(red: 255/255, green: 87/255, blue: 87/255, alpha: 1).cgColor
gradient.colors = [color, sndColor]
gradient.locations = [0.0, 1.0]
gradient.startPoint = CGPoint(x: 0, y: 0)
gradient.endPoint = CGPoint(x: 1, y: 0)
return gradient
}()
override init(frame: CGRect = .zero) {
super.init(frame: frame)
addSubLayers()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
addSubLayers()
}
override public func layoutSubviews() {
super.layoutSubviews()
updatePaths()
}
override public func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setProgress(0.75, animated: false)
}
public func setProgress(_ progress: CGFloat, animated: Bool = true) {
if animated {
animateStroke(to: progress)
} else {
shapeLayer.strokeEnd = progress
}
}
}
private extension GradientProgressView {
func addSubLayers() {
layer.addSublayer(trackLayer)
layer.addSublayer(shapeLayer)
layer.addSublayer(gradient)
}
func updatePaths() {
let lineWidth = bounds.height / 2
trackLayer.lineWidth = lineWidth * 0.75
shapeLayer.lineWidth = lineWidth
let path = UIBezierPath()
path.move(to: CGPoint(x: bounds.minX + lineWidth / 2, y: bounds.midY))
path.addLine(to: CGPoint(x: bounds.maxX - lineWidth / 2, y: bounds.midY))
trackLayer.path = path.cgPath
shapeLayer.path = path.cgPath
gradient.frame = bounds
gradient.mask = shapeLayer
}
func animateStroke(to progress: CGFloat) {
let key = "lineStrokeAnimation"
layer.removeAnimation(forKey: key)
let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
basicAnimation.toValue = progress
basicAnimation.duration = 1.5
basicAnimation.fillMode = .forwards
basicAnimation.isRemovedOnCompletion = false
basicAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
shapeLayer.add(basicAnimation, forKey: key)
}
}
那么视图控制器仅仅是:
class ViewController: UIViewController {
@IBOutlet weak var gradientProgressView: GradientProgressView!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
updateProgress()
}
...
}
// MARK: - Progress related methods
private extension ViewController {
func updateProgress() {
let distance = currLeasingCar!.currentKm - currLeasing!.startKm
let value = CGFloat(distance) / CGFloat(finalKm)
gradientProgressView.setProgress(value)
}
}
问题是您的路径是一个圆角矩形。在您与我们共享的图像中,大概有2-3%的笔划。将其更改为笔划90%,您将看到它试图绘制一个宽且极短的圆形矩形,例如: 相反,只需将路径设为一条直线,即可按预期工作:
let path = UIBezierPath()
let bounds = topView.bounds
path.move(to: CGPoint(x: bounds.minX + bounds.width * 0.15, y: bounds.minY + bounds.height / 1.5))
path.addLine(to: CGPoint(x: bounds.minX + bounds.width * 0.85, y: bounds.minY + bounds.height / 1.5))
您可能还希望使形状层的封口变圆:
trackLayer.lineCap = .round // or whatever you want
shapeLayer.lineCap = .round
当然,此更改丢失了原始路径的2点高度,因此如果要使这些形状层更厚,只需增加它们各自的线宽
值
一些不相关的观察结果:
和viewDidLayoutSubviews()
应该调用它们的ViewDidDisplay(:)
实现super
可以多次调用,因此您不希望每次都实例化一个新的viewdilayoutsubviews()
。或者,如果您这样做,请确保删除之前的一个trackLayer
- 添加子视图/子层时,谨慎使用
而不是边界
。在这种情况下,这可能无关紧要,但在某些情况下,您可能会遇到各种奇怪的问题,因为框架
位于视图的superview坐标系中,而frame
是所讨论视图的坐标系bounds
就个人而言,如果要将此代码保存在视图控制器中,我建议:
- 在
中添加形状层和渐变李>viewDidLoad
- 从
更新渐变的路径和边界李>viewdilayoutsubviews
- 我将这些不同的形状层和渐变方法放在它们自己的私有扩展中
UIView
子类(或子视图控制器)
因此,也许:
@IBDesignable
public class GradientProgressView: UIView {
private var shapeLayer: CAShapeLayer = {
let shapeLayer = CAShapeLayer()
shapeLayer.strokeColor = UIColor.green.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineCap = .round
return shapeLayer
}()
private var trackLayer: CAShapeLayer = {
let trackLayer = CAShapeLayer()
trackLayer.strokeColor = UIColor.groupTableViewBackground.cgColor
trackLayer.fillColor = UIColor.clear.cgColor
trackLayer.lineCap = .round
return trackLayer
}()
private var gradient: CAGradientLayer = {
let gradient = CAGradientLayer()
let color = UIColor(red: 11/255, green: 95/255, blue: 244/255, alpha: 1).cgColor
let sndColor = UIColor(red: 255/255, green: 87/255, blue: 87/255, alpha: 1).cgColor
gradient.colors = [color, sndColor]
gradient.locations = [0.0, 1.0]
gradient.startPoint = CGPoint(x: 0, y: 0)
gradient.endPoint = CGPoint(x: 1, y: 0)
return gradient
}()
override init(frame: CGRect = .zero) {
super.init(frame: frame)
addSubLayers()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
addSubLayers()
}
override public func layoutSubviews() {
super.layoutSubviews()
updatePaths()
}
override public func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setProgress(0.75, animated: false)
}
public func setProgress(_ progress: CGFloat, animated: Bool = true) {
if animated {
animateStroke(to: progress)
} else {
shapeLayer.strokeEnd = progress
}
}
}
private extension GradientProgressView {
func addSubLayers() {
layer.addSublayer(trackLayer)
layer.addSublayer(shapeLayer)
layer.addSublayer(gradient)
}
func updatePaths() {
let lineWidth = bounds.height / 2
trackLayer.lineWidth = lineWidth * 0.75
shapeLayer.lineWidth = lineWidth
let path = UIBezierPath()
path.move(to: CGPoint(x: bounds.minX + lineWidth / 2, y: bounds.midY))
path.addLine(to: CGPoint(x: bounds.maxX - lineWidth / 2, y: bounds.midY))
trackLayer.path = path.cgPath
shapeLayer.path = path.cgPath
gradient.frame = bounds
gradient.mask = shapeLayer
}
func animateStroke(to progress: CGFloat) {
let key = "lineStrokeAnimation"
layer.removeAnimation(forKey: key)
let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
basicAnimation.toValue = progress
basicAnimation.duration = 1.5
basicAnimation.fillMode = .forwards
basicAnimation.isRemovedOnCompletion = false
basicAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
shapeLayer.add(basicAnimation, forKey: key)
}
}
那么视图控制器仅仅是:
class ViewController: UIViewController {
@IBOutlet weak var gradientProgressView: GradientProgressView!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
updateProgress()
}
...
}
// MARK: - Progress related methods
private extension ViewController {
func updateProgress() {
let distance = currLeasingCar!.currentKm - currLeasing!.startKm
let value = CGFloat(distance) / CGFloat(finalKm)
gradientProgressView.setProgress(value)
}
}
非常感谢。工作正常-关于
viewdilayoutsubviews()
,应该在哪里调用displayLine()
?@Tim-作为更高级的观察,无论如何,我不相信这些代码真的属于视图控制器。最好让它成为自己的班级。谢谢!工作正常-关于viewdilayoutsubviews()
,应该在哪里调用displayLine()
?@Tim-作为更高级的观察,无论如何,我不相信这些代码真的属于视图控制器。最好让它成为自己的班级。