IT

원 그리기 애니메이션

lottoking 2020. 8. 21. 08:05
반응형

원 그리기 애니메이션


원의 그림을 애니메이션으로 만드는 방법을 찾고 있습니다. 나는 원을 만들 수 있었지만 그것은 모두 함께 그립니다.

CircleView수업 은 다음과 가변 .

import UIKit

class CircleView: UIView {
  override init(frame: CGRect) {
    super.init(frame: frame)
    self.backgroundColor = UIColor.clearColor()
  }

  required init(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }


  override func drawRect(rect: CGRect) {
    // Get the Graphics Context
    var context = UIGraphicsGetCurrentContext();

    // Set the circle outerline-width
    CGContextSetLineWidth(context, 5.0);

    // Set the circle outerline-colour
    UIColor.redColor().set()

    // Create Circle
    CGContextAddArc(context, (frame.size.width)/2, frame.size.height/2, (frame.size.width - 10)/2, 0.0, CGFloat(M_PI * 2.0), 1)

    // Draw
    CGContextStrokePath(context);
  }
}

그리고 다음은 뷰 컨트롤러의 뷰 계층 구조에 추가하는 방법입니다.

func addCircleView() {
    let diceRoll = CGFloat(Int(arc4random_uniform(7))*50)
    var circleWidth = CGFloat(200)
    var circleHeight = circleWidth
    // Create a new CircleView
    var circleView = CircleView(frame: CGRectMake(diceRoll, 0, circleWidth, circleHeight))

    view.addSubview(circleView)
}

원의 그림을 1 초 이상 애니메이션하는 방법이 있습니까?

예를 들어, 애니메이션의 일부는이 이미지에서 파란색 선처럼 보일 것입니다.

부분 애니메이션


이를 수행하는 가장 쉬운 방법은 핵심 애니메이션의 힘을 사용하여 대부분의 작업을 수행하는 것입니다. 이를 위해 원 그리기 코드를 drawRect함수에서 . 그런 다음을 사용하여 속성을 에서 . 여기서 마법의 큰 부분을 차지합니다. 문서에서 :CAShapeLayerCABasicAnimationCAShapeLayerstrokeEnd0.01.0strokeEnd

strokeStart 속성과 결합 된이 속성은 채팅 경로의 하위 영역을 정의합니다. 이 속성의 값은 시작 속성이 시작점을 정의하는 동안 문장을 완료 할 경로를 따라 최강 지점을 나타냅니다. 0.0 값은 경로의 시작을 실수 1.0 값은 경로의 끝을 나타냅니다. 그 사이의 값은 경로 길이를 따라 선형으로 해석됩니다.

로 설정 strokeEnd하면 0.0아무것도 그려지지 않습니다. 로 설정하면 1.0완전한 원이 그려집니다. 로 설정하면 0.5반원이 그려집니다. 기타

그래서 시작 가능한 만들 수 있습니다 CAShapeLayer당신의 CircleViewinit에 해당 레이어를 기능을 추가 뷰의 sublayers제거해야 할 drawRect레이어 이제 원을 그리기되기 때문에 기능) :

let circleLayer: CAShapeLayer!

override init(frame: CGRect) {
    super.init(frame: frame)
    self.backgroundColor = UIColor.clearColor()

    // Use UIBezierPath as an easy way to create the CGPath for the layer.
    // The path should be the entire circle.
    let circlePath = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2.0, y: frame.size.height / 2.0), radius: (frame.size.width - 10)/2, startAngle: 0.0, endAngle: CGFloat(M_PI * 2.0), clockwise: true)

    // Setup the CAShapeLayer with the path, colors, and line width
    circleLayer = CAShapeLayer()
    circleLayer.path = circlePath.CGPath
    circleLayer.fillColor = UIColor.clearColor().CGColor
    circleLayer.strokeColor = UIColor.redColor().CGColor
    circleLayer.lineWidth = 5.0;

    // Don't draw the circle initially
    circleLayer.strokeEnd = 0.0

    // Add the circleLayer to the view's layer's sublayers
    layer.addSublayer(circleLayer)
}

참고 :circleLayer.strokeEnd = 0.0원이 바로 그려지지 않도록 설정 하고 있습니다.

이제 원 애니메이션을 트리거하기 위해 호출 할 수있는 함수를 추가해 보겠습니다.

func animateCircle(duration: NSTimeInterval) {
    // We want to animate the strokeEnd property of the circleLayer
    let animation = CABasicAnimation(keyPath: "strokeEnd")

    // Set the animation duration appropriately
    animation.duration = duration

    // Animate from 0 (no circle) to 1 (full circle)
    animation.fromValue = 0
    animation.toValue = 1

    // Do a linear animation (i.e. the speed of the animation stays the same)
    animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)

    // Set the circleLayer's strokeEnd property to 1.0 now so that it's the
    // right value when the animation ends.
    circleLayer.strokeEnd = 1.0

    // Do the actual animation
    circleLayer.addAnimation(animation, forKey: "animateCircle")
}

그런 다음, 우리가해야 할 일은 당신의 변화 addCircleView는 당신이 추가 애니메이션 트리거 기능을 CircleView보충 할 것입니다 superview:

func addCircleView() {
    let diceRoll = CGFloat(Int(arc4random_uniform(7))*50)
     var circleWidth = CGFloat(200)
     var circleHeight = circleWidth

        // Create a new CircleView
     var circleView = CircleView(frame: CGRectMake(diceRoll, 0, circleWidth, circleHeight))

     view.addSubview(circleView)

     // Animate the drawing of the circle over the course of 1 second
     circleView.animateCircle(1.0)
}

합쳐진 모든 것은 다음과 같은 생각입니다.

원 애니메이션

참고 : 이렇게 반복되지 않고 애니메이션 된 후에는 완전한 원으로 유지됩니다.


Swift 3.0에 대한 Mikes 답변 업데이트

var circleLayer: CAShapeLayer!

override init(frame: CGRect) {
    super.init(frame: frame)
    self.backgroundColor = UIColor.clear

    // Use UIBezierPath as an easy way to create the CGPath for the layer.
    // The path should be the entire circle.
    let circlePath = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2.0, y: frame.size.height / 2.0), radius: (frame.size.width - 10)/2, startAngle: 0.0, endAngle: CGFloat(M_PI * 2.0), clockwise: true)

    // Setup the CAShapeLayer with the path, colors, and line width
    circleLayer = CAShapeLayer()
    circleLayer.path = circlePath.cgPath
    circleLayer.fillColor = UIColor.clear.cgColor
    circleLayer.strokeColor = UIColor.red.cgColor
    circleLayer.lineWidth = 5.0;

    // Don't draw the circle initially
    circleLayer.strokeEnd = 0.0

    // Add the circleLayer to the view's layer's sublayers
    layer.addSublayer(circleLayer)
} 

required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
}

func animateCircle(duration: TimeInterval) {
    // We want to animate the strokeEnd property of the circleLayer
    let animation = CABasicAnimation(keyPath: "strokeEnd")

    // Set the animation duration appropriately
    animation.duration = duration

    // Animate from 0 (no circle) to 1 (full circle)
    animation.fromValue = 0
    animation.toValue = 1

    // Do a linear animation (i.e The speed of the animation stays the same)
    animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)

    // Set the circleLayer's strokeEnd property to 1.0 now so that it's the
    // Right value when the animation ends
    circleLayer.strokeEnd = 1.0

    // Do the actual animation
    circleLayer.add(animation, forKey: "animateCircle")
}

함수를 호출 비용 :

func addCircleView() {
    let diceRoll = CGFloat(Int(arc4random_uniform(7))*50)
    var circleWidth = CGFloat(200)
    var circleHeight = circleWidth

    // Create a new CircleView
    let circleView = CircleView(frame: CGRect(x: diceRoll, y: 0, width: circleWidth, height: circleHeight))
    //let test = CircleView(frame: CGRect(x: diceRoll, y: 0, width: circleWidth, height: circleHeight))

    view.addSubview(circleView)

    // Animate the drawing of the circle over the course of 1 second
    circleView.animateCircle(duration: 1.0)
}


Mike의 대답은 훌륭합니다! 또 다른 멋지고 간단한 방법은 setNeedsDisplay ()와 결합 된 drawRect를 사용하는 것입니다. 느슨해 보이지만 말씀드립니다 :-)여기에 이미지 설명 입력

-90 °이고 270 °에서 끝나는 상단에서 시작하여 원을 그리려고합니다. 원의 중심은 (centerX, centerY)이며 주어진 반경입니다. CurrentAngle은 minAngle (-90)에서 maxAngle (270)로 이동하는 원 끝점의 현재 각도입니다.

// MARK: Properties
let centerX:CGFloat = 55
let centerY:CGFloat = 55
let radius:CGFloat = 50

var currentAngle:Float = -90
let minAngle:Float = -90
let maxAngle:Float = 270

drawRect에서 원이 표시되는 방식을 지정합니다.

override func drawRect(rect: CGRect) {

    let context = UIGraphicsGetCurrentContext()

    let path = CGPathCreateMutable()

    CGPathAddArc(path, nil, centerX, centerY, radius, CGFloat(GLKMathDegreesToRadians(minAngle)), CGFloat(GLKMathDegreesToRadians(currentAngle)), false)

    CGContextAddPath(context, path)
    CGContextSetStrokeColorWithColor(context, UIColor.blueColor().CGColor)
    CGContextSetLineWidth(context, 3)
    CGContextStrokePath(context)
}

문제는 currentAngle이 변경되지 않으므로 currentAngle = minAngle과 같이 원이 정적이며 표시되지 않습니다.

그런 다음 타이머를 만들고 해당 타이머가 실행될 때마다 currentAngle을 개선합니다. 수업 맨 위에 두 번의 화재 사이에 타이밍을 추가합니다.

let timeBetweenDraw:CFTimeInterval = 0.01

초기화에서 타이머를 추가하십시오.

NSTimer.scheduledTimerWithTimeInterval(timeBetweenDraw, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)

타이머가 실행될 때 호출 될 함수를 추가 할 수 있습니다.

func updateTimer() {

    if currentAngle < maxAngle {
        currentAngle += 1
    }
}

슬프게도 앱을 때 다시 그려야하는 시스템을 지정하지 않습니다. 이 setNeedsDisplay ()를 호출하여 수행합니다. 업데이트 된 타이머 기능은 다음과 가변합니다.

func updateTimer() {

    if currentAngle < maxAngle {
        currentAngle += 1
        setNeedsDisplay()
    }
}

_ _ _

필요한 모든 코드가 여기에 요약되어 있습니다.

import UIKit
import GLKit

class CircleClosing: UIView {

    // MARK: Properties
    let centerX:CGFloat = 55
    let centerY:CGFloat = 55
    let radius:CGFloat = 50

    var currentAngle:Float = -90

    let timeBetweenDraw:CFTimeInterval = 0.01

    // MARK: Init
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    func setup() {
        self.backgroundColor = UIColor.clearColor()
        NSTimer.scheduledTimerWithTimeInterval(timeBetweenDraw, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
    }

    // MARK: Drawing
    func updateTimer() {

        if currentAngle < 270 {
            currentAngle += 1
            setNeedsDisplay()
        }
    }

    override func drawRect(rect: CGRect) {

        let context = UIGraphicsGetCurrentContext()

        let path = CGPathCreateMutable()

        CGPathAddArc(path, nil, centerX, centerY, radius, -CGFloat(M_PI/2), CGFloat(GLKMathDegreesToRadians(currentAngle)), false)

        CGContextAddPath(context, path)
        CGContextSetStrokeColorWithColor(context, UIColor.blueColor().CGColor)
        CGContextSetLineWidth(context, 3)
        CGContextStrokePath(context)
    }
}

속도를 변경 비용 updateTimer 함수 또는이 함수가 호출되는 속도를 수정하면됩니다. 또한 서클이 완료되면 타이머를 무효화하고 싶을 수도 있습니다.

NB : 스토리 보드에 추가 비용을 추가하고 선택하고 Identity Inspector 로 이동 한 다음 ClassCircleClosing을 지정 하면 됩니다.

건배! bRo


완료 완료 완료하신 것을 스위프트 3.0에서 수행 한 Mike S의 솔루션과 또 다른 솔루션입니다.

func animateCircleFull(duration: TimeInterval) {
    CATransaction.begin()
    let animation = CABasicAnimation(keyPath: "strokeEnd")
    animation.duration = duration
    animation.fromValue = 0
    animation.toValue = 1
    animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
    circleLayer.strokeEnd = 1.0
    CATransaction.setCompletionBlock {
        print("animation complete")
    }
    // Do the actual animation
    circleLayer.add(animation, forKey: "animateCircle")
    CATransaction.commit()
}

완료 할 때 사용하면 동일한 함수를 재귀 적으로 호출하여 애니메이션을 다시 수행하여 애니메이션을 다시 사용할 수 있습니다. (매우 멋지지 않을 것임) 또는 조건이 계속 될 때까지 계속 연결되는 반전 함수를 누를 수 있습니다. 예 :

func animate(duration: TimeInterval){
    self.isAnimating = true
    self.animateCircleFull(duration: 1)
}

func endAnimate(){
    self.isAnimating = false
}

func animateCircleFull(duration: TimeInterval) {
    if self.isAnimating{
        CATransaction.begin()
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.duration = duration
        animation.fromValue = 0
        animation.toValue = 1
        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        circleLayer.strokeEnd = 1.0
        CATransaction.setCompletionBlock { 
            self.animateCircleEmpty(duration: duration)
        }
        // Do the actual animation
        circleLayer.add(animation, forKey: "animateCircle")
        CATransaction.commit()
    }
}

func animateCircleEmpty(duration: TimeInterval){
    if self.isAnimating{
        CATransaction.begin()
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.duration = duration
        animation.fromValue = 1
        animation.toValue = 0
        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        circleLayer.strokeEnd = 0
        CATransaction.setCompletionBlock {
            self.animateCircleFull(duration: duration)
        }
        // Do the actual animation
        circleLayer.add(animation, forKey: "animateCircle")
        CATransaction.commit()
    }
}

더 멋지게 만들기 다음과 같이 애니메이션의 방향을 위해 다음과 같이 있습니다.

 func setCircleClockwise(){
    let circlePath = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2.0, y: frame.size.height / 2.0), radius: (frame.size.width - 10)/2, startAngle: 0.0, endAngle: CGFloat(M_PI * 2.0), clockwise: true)
    self.circleLayer.removeFromSuperlayer()
    self.circleLayer = formatCirle(circlePath: circlePath)
    self.layer.addSublayer(self.circleLayer)
}

func setCircleCounterClockwise(){
    let circlePath = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2.0, y: frame.size.height / 2.0), radius: (frame.size.width - 10)/2, startAngle: 0.0, endAngle: CGFloat(M_PI * 2.0), clockwise: false)
    self.circleLayer.removeFromSuperlayer()
    self.circleLayer = formatCirle(circlePath: circlePath)
    self.layer.addSublayer(self.circleLayer)
}

func formatCirle(circlePath: UIBezierPath) -> CAShapeLayer{
    let circleShape = CAShapeLayer()
    circleShape.path = circlePath.cgPath
    circleShape.fillColor = UIColor.clear.cgColor
    circleShape.strokeColor = UIColor.red.cgColor
    circleShape.lineWidth = 10.0;
    circleShape.strokeEnd = 0.0
    return circleShape
}

func animate(duration: TimeInterval){
    self.isAnimating = true
    self.animateCircleFull(duration: 1)
}

func endAnimate(){
    self.isAnimating = false
}

func animateCircleFull(duration: TimeInterval) {
    if self.isAnimating{
        CATransaction.begin()
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.duration = duration
        animation.fromValue = 0
        animation.toValue = 1
        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        circleLayer.strokeEnd = 1.0
        CATransaction.setCompletionBlock {
            self.setCircleCounterClockwise()
            self.animateCircleEmpty(duration: duration)
        }
        // Do the actual animation
        circleLayer.add(animation, forKey: "animateCircle")
        CATransaction.commit()
    }
}

func animateCircleEmpty(duration: TimeInterval){
    if self.isAnimating{
        CATransaction.begin()
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.duration = duration
        animation.fromValue = 1
        animation.toValue = 0
        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        circleLayer.strokeEnd = 0
        CATransaction.setCompletionBlock {
            self.setCircleClockwise()
            self.animateCircleFull(duration: duration)
        }
        // Do the actual animation
        circleLayer.add(animation, forKey: "animateCircle")
        CATransaction.commit()
    }
}

참고 URL : https://stackoverflow.com/questions/26578023/animate-drawing-of-a-circle

반응형