gpt4 book ai didi

ios - CAShapeLayer() 绘制奇怪的线/路径

转载 作者:搜寻专家 更新时间:2023-11-01 06:11:24 25 4
gpt4 key购买 nike

我有一个 CAShapeLayer() ,上面有一个渐变,它正在被动画化,但不知何故它看起来像下图: image

怎么会变成这样?

我的代码:

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%,您会看到它试图绘制一个宽且极短的圆角矩形,例如:

enter image description here

相反,只需将路径设为一条线,它就会按预期工作:

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 点高度,因此如果您想使这些形状图层更厚,只需增加它们各自的 lineWidth 值即可。


一些不相关的观察:

  • viewDidLayoutSubviews()viewDidAppear(_:) 应该调用它们的 super 实现。

  • viewDidLayoutSubviews() 可以调用多次,所以你不想每次都实例化一个新的 trackLayer。或者,如果您这样做,请务必删除之前的那个。

  • 添加 subview /子图层时,谨慎使用bounds 而不是frame。在这种情况下,这可能无关紧要,但在某些情况下,您可能会遇到各种奇怪的问题,因为 frame 在 View 的父 View 的坐标系中,而 bounds 是相关 View 的坐标系。


就个人而言,如果您要将此代码保留在 View Controller 中,我建议:

  • viewDidLoad中添加形状层和渐变;
  • viewDidLayoutSubviews 更新渐变的路径和边界;
  • 我会将这些不同的形状层和渐变方法放在它们自己的私有(private)扩展中。

更好的是,所有这些动画代码根本不属于应用程序的 View Controller ,而是属于 UIView 子类(或 subview Controller )。

因此,也许:

@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)
}
}

那么 View Controller 仅仅是:

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)
}
}

关于ios - CAShapeLayer() 绘制奇怪的线/路径,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56364941/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com