首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用圆UIBezierPath和CABasicAnimation动画化UIBezierPath

用圆UIBezierPath和CABasicAnimation动画化UIBezierPath
EN

Stack Overflow用户
提问于 2017-07-25 15:39:14
回答 1查看 7.3K关注 0票数 9

我想在15秒内画一个从0到360度的圆圈。

动画很奇怪。我知道这可能是一个开始/结束的角度问题,我已经面临这样的问题与圆形动画,但我不知道如何解决这个问题。

代码语言:javascript
复制
var circle_layer=CAShapeLayer()
var circle_anim=CABasicAnimation(keyPath: "path")

func init_circle_layer(){
    let w=circle_view.bounds.width
    let center=CGPoint(x: w/2, y: w/2)

    //initial path
    let start_angle:CGFloat = -0.25*360*CGFloat.pi/180
    let initial_path=UIBezierPath(arcCenter: center, radius: w/2, startAngle: start_angle, endAngle: start_angle, clockwise: true)
    initial_path.addLine(to: center)

    //final path
    let end_angle:CGFloat=start_angle+360*CGFloat(CGFloat.pi/180)
    let final_path=UIBezierPath(arcCenter: center, radius: w/2, startAngle: start_angle, endAngle: end_angle, clockwise: true)
    final_path.addLine(to: center)

    //init layer
    circle_layer.path=initial_path.cgPath
    circle_layer.fillColor=UIColor(hex_code: "EA535D").cgColor
    circle_view.layer.addSublayer(circle_layer)

    //init anim
    circle_anim.duration=15
    circle_anim.fromValue=initial_path.cgPath
    circle_anim.toValue=final_path.cgPath
    circle_anim.isRemovedOnCompletion=false
    circle_anim.fillMode=kCAFillModeForwards
    circle_anim.delegate=self
}

func start_circle_animation(){
    circle_layer.add(circle_anim, forKey: "circle_anim")
}

我想从0华氏度开始,在一次完整的巡回赛后结束:

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-07-25 16:37:19

您不可能轻松地动画填充一个UIBezierPath (或者至少不引入奇怪的工件,除非在控制得很好的情况下)。但你可以动画的strokeEnd的一个pathCAShapeLayer。如果你使被撞击的路径的线宽非常宽(也就是最后一个圆的半径),并将路径的半径设置为圆形半径的一半,你就会得到你想要的东西。

代码语言:javascript
复制
private var circleLayer = CAShapeLayer()

private func configureCircleLayer() {
    let radius = min(circleView.bounds.width, circleView.bounds.height) / 2

    circleLayer.strokeColor = UIColor(hexCode: "EA535D").cgColor
    circleLayer.fillColor = UIColor.clear.cgColor
    circleLayer.lineWidth = radius
    circleView.layer.addSublayer(circleLayer)

    let center = CGPoint(x: circleView.bounds.width/2, y: circleView.bounds.height/2)
    let startAngle: CGFloat = -0.25 * 2 * .pi
    let endAngle: CGFloat = startAngle + 2 * .pi
    circleLayer.path = UIBezierPath(arcCenter: center, radius: radius / 2, startAngle: startAngle, endAngle: endAngle, clockwise: true).cgPath

    circleLayer.strokeEnd = 0
}

private func startCircleAnimation() {
    circleLayer.strokeEnd = 1
    let animation = CABasicAnimation(keyPath: "strokeEnd")
    animation.fromValue = 0
    animation.toValue = 1
    animation.duration = 15
    circleLayer.add(animation, forKey: nil)
}

对于最终控制,在执行复杂的UIBezierPath动画时,可以使用CADisplayLink,避免在使用pathCABasicAnimation时有时会产生的工件。

代码语言:javascript
复制
private var circleLayer = CAShapeLayer()
private weak var displayLink: CADisplayLink?
private var startTime: CFTimeInterval!

private func configureCircleLayer() {
    circleLayer.fillColor = UIColor(hexCode: "EA535D").cgColor
    circleView.layer.addSublayer(circleLayer)
    updatePath(percent: 0)
}

private func startCircleAnimation() {
    startTime = CACurrentMediaTime()
    displayLink = {
        let _displayLink = CADisplayLink(target: self, selector: #selector(handleDisplayLink(_:)))
        _displayLink.add(to: .current, forMode: .commonModes)
        return _displayLink
    }()
}

@objc func handleDisplayLink(_ displayLink: CADisplayLink) {   // the @objc qualifier needed for Swift 4 @objc inference
    let percent = CGFloat(CACurrentMediaTime() - startTime) / 15.0
    updatePath(percent: min(percent, 1.0))
    if percent > 1.0 {
        displayLink.invalidate()
    }
}

private func updatePath(percent: CGFloat) {
    let w = circleView.bounds.width
    let center = CGPoint(x: w/2, y: w/2)
    let startAngle: CGFloat = -0.25 * 2 * .pi
    let endAngle: CGFloat = startAngle + percent * 2 * .pi
    let path = UIBezierPath()
    path.move(to: center)
    path.addArc(withCenter: center, radius: w/2, startAngle: startAngle, endAngle: endAngle, clockwise: true)
    path.close()

    circleLayer.path = path.cgPath
}

然后你就可以:

代码语言:javascript
复制
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    configureCircleLayer()
    startCircleAnimation()
}

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)

    displayLink?.invalidate()   // to avoid displaylink keeping a reference to dismissed view during animation
}

这会产生:

票数 16
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45307902

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档