Make clockwise disappearing circle with PaintCode

2019-03-06 01:19发布

问题:

I'm trying to make a clockwise disappearing circle on top of my logo with Paintcode. So far I made variables for start and end angles and I don't know how to make the step by step process for clockwise moving.

This is what I made in PaintCode:

And this is the function from the program:

func drawCanvas1(frame: CGRect = CGRect(x: 0, y: 0, width: 502, height: 480), startAngle: CGFloat = 360, endAngle: CGFloat = -360) {
    //// General Declarations
    let context = UIGraphicsGetCurrentContext()!

    //// Color Declarations
    let fillColor = UIColor(red: 0.093, green: 0.382, blue: 0.372, alpha: 1.000)
    let fillColor2 = UIColor(red: 0.967, green: 0.968, blue: 0.960, alpha: 1.000)

    //// Group 2
    //// Group 3
    context.saveGState()
    context.beginTransparencyLayer(auxiliaryInfo: nil)

    //// Clip Clip
    let clipPath = UIBezierPath(rect: CGRect(x: frame.minX + 129.03, y: frame.minY + 113.85, width: 238.25, height: 202.1))
    clipPath.addClip()


    //// Bezier Drawing
    let bezierPath = UIBezierPath()
    bezierPath.move(to: CGPoint(x: frame.minX + 257.2, y: frame.minY + 261.51))
    bezierPath.addCurve(to: CGPoint(x: frame.minX + 212.54, y: frame.minY + 216.85), controlPoint1: CGPoint(x: frame.minX + 232.53, y: frame.minY + 261.51), controlPoint2: CGPoint(x: frame.minX + 212.54, y: frame.minY + 241.52))
    bezierPath.addCurve(to: CGPoint(x: frame.minX + 257.2, y: frame.minY + 172.19), controlPoint1: CGPoint(x: frame.minX + 212.54, y: frame.minY + 192.18), controlPoint2: CGPoint(x: frame.minX + 232.53, y: frame.minY + 172.19))
    bezierPath.addCurve(to: CGPoint(x: frame.minX + 301.87, y: frame.minY + 216.85), controlPoint1: CGPoint(x: frame.minX + 281.87, y: frame.minY + 172.19), controlPoint2: CGPoint(x: frame.minX + 301.87, y: frame.minY + 192.18))
    bezierPath.addCurve(to: CGPoint(x: frame.minX + 257.2, y: frame.minY + 261.51), controlPoint1: CGPoint(x: frame.minX + 301.87, y: frame.minY + 241.52), controlPoint2: CGPoint(x: frame.minX + 281.87, y: frame.minY + 261.51))
    bezierPath.close()
    bezierPath.move(to: CGPoint(x: frame.minX + 332.37, y: frame.minY + 180.76))
    bezierPath.addCurve(to: CGPoint(x: frame.minX + 300.63, y: frame.minY + 170.19), controlPoint1: CGPoint(x: frame.minX + 326.52, y: frame.minY + 169.08), controlPoint2: CGPoint(x: frame.minX + 312.31, y: frame.minY + 164.34))
    bezierPath.addLine(to: CGPoint(x: frame.minX + 296.53, y: frame.minY + 172.1))
    bezierPath.addCurve(to: CGPoint(x: frame.minX + 257.32, y: frame.minY + 157.34), controlPoint1: CGPoint(x: frame.minX + 286.06, y: frame.minY + 162.92), controlPoint2: CGPoint(x: frame.minX + 272.35, y: frame.minY + 157.34))
    bezierPath.addCurve(to: CGPoint(x: frame.minX + 235.67, y: frame.minY + 161.42), controlPoint1: CGPoint(x: frame.minX + 249.68, y: frame.minY + 157.34), controlPoint2: CGPoint(x: frame.minX + 242.39, y: frame.minY + 158.8))
    bezierPath.addLine(to: CGPoint(x: frame.minX + 235.68, y: frame.minY + 152.59))
    bezierPath.addLine(to: CGPoint(x: frame.minX + 225.81, y: frame.minY + 150.61))
    bezierPath.addLine(to: CGPoint(x: frame.minX + 220.89, y: frame.minY + 159.38))
    bezierPath.addLine(to: CGPoint(x: frame.minX + 227.76, y: frame.minY + 165.21))
    bezierPath.addCurve(to: CGPoint(x: frame.minX + 197.8, y: frame.minY + 216.87), controlPoint1: CGPoint(x: frame.minX + 209.86, y: frame.minY + 175.48), controlPoint2: CGPoint(x: frame.minX + 197.8, y: frame.minY + 194.76))
    bezierPath.addCurve(to: CGPoint(x: frame.minX + 198.56, y: frame.minY + 226.38), controlPoint1: CGPoint(x: frame.minX + 197.8, y: frame.minY + 220.11), controlPoint2: CGPoint(x: frame.minX + 198.06, y: frame.minY + 223.28))
    bezierPath.addCurve(to: CGPoint(x: frame.minX + 198.57, y: frame.minY + 226.49), controlPoint1: CGPoint(x: frame.minX + 198.57, y: frame.minY + 226.42), controlPoint2: CGPoint(x: frame.minX + 198.57, y: frame.minY + 226.45))
    bezierPath.addCurve(to: CGPoint(x: frame.minX + 157.51, y: frame.minY + 215.5), controlPoint1: CGPoint(x: frame.minX + 171.6, y: frame.minY + 217.72), controlPoint2: CGPoint(x: frame.minX + 166.18, y: frame.minY + 216.58))
    bezierPath.addLine(to: CGPoint(x: frame.minX + 152.17, y: frame.minY + 223.39))
    bezierPath.addCurve(to: CGPoint(x: frame.minX + 230.93, y: frame.minY + 270.22), controlPoint1: CGPoint(x: frame.minX + 179.07, y: frame.minY + 232.4), controlPoint2: CGPoint(x: frame.minX + 208.29, y: frame.minY + 259.24))
    bezierPath.addCurve(to: CGPoint(x: frame.minX + 234.66, y: frame.minY + 271.92), controlPoint1: CGPoint(x: frame.minX + 232.15, y: frame.minY + 270.83), controlPoint2: CGPoint(x: frame.minX + 233.39, y: frame.minY + 271.39))
    bezierPath.addCurve(to: CGPoint(x: frame.minX + 234.9, y: frame.minY + 272.03), controlPoint1: CGPoint(x: frame.minX + 234.74, y: frame.minY + 271.95), controlPoint2: CGPoint(x: frame.minX + 234.83, y: frame.minY + 271.99))
    bezierPath.addCurve(to: CGPoint(x: frame.minX + 239.12, y: frame.minY + 273.55), controlPoint1: CGPoint(x: frame.minX + 236.42, y: frame.minY + 272.65), controlPoint2: CGPoint(x: frame.minX + 237.81, y: frame.minY + 273.26))
    bezierPath.addCurve(to: CGPoint(x: frame.minX + 257.32, y: frame.minY + 276.39), controlPoint1: CGPoint(x: frame.minX + 244.86, y: frame.minY + 275.39), controlPoint2: CGPoint(x: frame.minX + 250.97, y: frame.minY + 276.39))
    bezierPath.addCurve(to: CGPoint(x: frame.minX + 316.85, y: frame.minY + 216.87), controlPoint1: CGPoint(x: frame.minX + 290.2, y: frame.minY + 276.39), controlPoint2: CGPoint(x: frame.minX + 316.85, y: frame.minY + 249.74))
    bezierPath.addCurve(to: CGPoint(x: frame.minX + 316.82, y: frame.minY + 215.74), controlPoint1: CGPoint(x: frame.minX + 316.85, y: frame.minY + 216.49), controlPoint2: CGPoint(x: frame.minX + 316.83, y: frame.minY + 216.12))
    bezierPath.addCurve(to: CGPoint(x: frame.minX + 322.77, y: frame.minY + 211.99), controlPoint1: CGPoint(x: frame.minX + 319.45, y: frame.minY + 214.08), controlPoint2: CGPoint(x: frame.minX + 322.58, y: frame.minY + 212.09))
    bezierPath.addCurve(to: CGPoint(x: frame.minX + 332.37, y: frame.minY + 180.76), controlPoint1: CGPoint(x: frame.minX + 333.73, y: frame.minY + 205.85), controlPoint2: CGPoint(x: frame.minX + 338.04, y: frame.minY + 192.11))
    bezierPath.close()
    fillColor.setFill()
    bezierPath.fill()


    //// Bezier 2 Drawing
    let bezier2Path = UIBezierPath()
    bezier2Path.move(to: CGPoint(x: frame.minX + 352.29, y: frame.minY + 128.81))
    bezier2Path.addLine(to: CGPoint(x: frame.minX + 352.29, y: frame.minY + 236.94))
    bezier2Path.addCurve(to: CGPoint(x: frame.minX + 288.22, y: frame.minY + 301), controlPoint1: CGPoint(x: frame.minX + 352.29, y: frame.minY + 272.26), controlPoint2: CGPoint(x: frame.minX + 323.54, y: frame.minY + 301))
    bezier2Path.addLine(to: CGPoint(x: frame.minX + 143.97, y: frame.minY + 301))
    bezier2Path.addLine(to: CGPoint(x: frame.minX + 143.97, y: frame.minY + 192.88))
    bezier2Path.addCurve(to: CGPoint(x: frame.minX + 208.05, y: frame.minY + 128.81), controlPoint1: CGPoint(x: frame.minX + 143.98, y: frame.minY + 157.55), controlPoint2: CGPoint(x: frame.minX + 172.72, y: frame.minY + 128.81))
    bezier2Path.addLine(to: CGPoint(x: frame.minX + 352.29, y: frame.minY + 128.81))
    bezier2Path.close()
    bezier2Path.move(to: CGPoint(x: frame.minX + 129.01, y: frame.minY + 191.33))
    bezier2Path.addLine(to: CGPoint(x: frame.minX + 129.01, y: frame.minY + 315.99))
    bezier2Path.addLine(to: CGPoint(x: frame.minX + 290.58, y: frame.minY + 315.99))
    bezier2Path.addCurve(to: CGPoint(x: frame.minX + 367.26, y: frame.minY + 238.49), controlPoint1: CGPoint(x: frame.minX + 332.86, y: frame.minY + 315.99), controlPoint2: CGPoint(x: frame.minX + 367.26, y: frame.minY + 281.22))
    bezier2Path.addLine(to: CGPoint(x: frame.minX + 367.26, y: frame.minY + 113.83))
    bezier2Path.addLine(to: CGPoint(x: frame.minX + 205.68, y: frame.minY + 113.83))
    bezier2Path.addCurve(to: CGPoint(x: frame.minX + 129.01, y: frame.minY + 191.33), controlPoint1: CGPoint(x: frame.minX + 163.41, y: frame.minY + 113.83), controlPoint2: CGPoint(x: frame.minX + 129.02, y: frame.minY + 148.6))
    bezier2Path.close()
    fillColor.setFill()
    bezier2Path.fill()


    context.endTransparencyLayer()
    context.restoreGState()


    //// Bezier 3 Drawing
    let bezier3Path = UIBezierPath()
    bezier3Path.move(to: CGPoint(x: frame.minX + 319.98, y: frame.minY + 201.25))
    bezier3Path.addCurve(to: CGPoint(x: frame.minX + 315.44, y: frame.minY + 204.02), controlPoint1: CGPoint(x: frame.minX + 319.85, y: frame.minY + 201.36), controlPoint2: CGPoint(x: frame.minX + 317.35, y: frame.minY + 202.87))
    bezier3Path.addCurve(to: CGPoint(x: frame.minX + 304.48, y: frame.minY + 180.56), controlPoint1: CGPoint(x: frame.minX + 313.53, y: frame.minY + 195.33), controlPoint2: CGPoint(x: frame.minX + 309.73, y: frame.minY + 187.36))
    bezier3Path.addCurve(to: CGPoint(x: frame.minX + 307.83, y: frame.minY + 178.87), controlPoint1: CGPoint(x: frame.minX + 306.06, y: frame.minY + 179.74), controlPoint2: CGPoint(x: frame.minX + 307.69, y: frame.minY + 178.91))
    bezier3Path.addCurve(to: CGPoint(x: frame.minX + 323.01, y: frame.minY + 185.61), controlPoint1: CGPoint(x: frame.minX + 313.73, y: frame.minY + 177.24), controlPoint2: CGPoint(x: frame.minX + 320.15, y: frame.minY + 179.93))
    bezier3Path.addCurve(to: CGPoint(x: frame.minX + 319.98, y: frame.minY + 201.25), controlPoint1: CGPoint(x: frame.minX + 325.71, y: frame.minY + 191.03), controlPoint2: CGPoint(x: frame.minX + 324.3, y: frame.minY + 197.42))
    bezier3Path.close()
    fillColor2.setFill()
    bezier3Path.fill()


    //// Bezier 4 Drawing
    let bezier4Path = UIBezierPath()
    bezier4Path.move(to: CGPoint(x: frame.minX + 223.93, y: frame.minY + 202.61))
    bezier4Path.addCurve(to: CGPoint(x: frame.minX + 253.02, y: frame.minY + 252.78), controlPoint1: CGPoint(x: frame.minX + 218.34, y: frame.minY + 227.69), controlPoint2: CGPoint(x: frame.minX + 232.45, y: frame.minY + 248.93))
    bezier4Path.addCurve(to: CGPoint(x: frame.minX + 223.93, y: frame.minY + 202.61), controlPoint1: CGPoint(x: frame.minX + 258.75, y: frame.minY + 236.32), controlPoint2: CGPoint(x: frame.minX + 253.13, y: frame.minY + 210.93))
    bezier4Path.close()
    fillColor.setFill()
    bezier4Path.fill()


    //// Bezier 5 Drawing
    let bezier5Path = UIBezierPath()
    bezier5Path.move(to: CGPoint(x: frame.minX + 287.08, y: frame.minY + 222.2))
    bezier5Path.addCurve(to: CGPoint(x: frame.minX + 260.13, y: frame.minY + 252.78), controlPoint1: CGPoint(x: frame.minX + 287.48, y: frame.minY + 240.26), controlPoint2: CGPoint(x: frame.minX + 274.83, y: frame.minY + 252.96))
    bezier5Path.addCurve(to: CGPoint(x: frame.minX + 287.08, y: frame.minY + 222.2), controlPoint1: CGPoint(x: frame.minX + 258.44, y: frame.minY + 240.65), controlPoint2: CGPoint(x: frame.minX + 265.81, y: frame.minY + 223.92))
    bezier5Path.close()
    fillColor.setFill()
    bezier5Path.fill()




    //// Oval Drawing
    let ovalRect = CGRect(x: frame.minX, y: frame.minY, width: 502, height: 480)
    let ovalPath = UIBezierPath()
    ovalPath.addArc(withCenter: CGPoint.zero, radius: ovalRect.width / 2, startAngle: -startAngle * CGFloat.pi/180, endAngle: -endAngle * CGFloat.pi/180, clockwise: true)
    ovalPath.addLine(to: CGPoint.zero)
    ovalPath.close()

    var ovalTransform = CGAffineTransform(translationX: ovalRect.midX, y: ovalRect.midY)
    ovalTransform = ovalTransform.scaledBy(x: 1, y: ovalRect.height / ovalRect.width)
    ovalPath.apply(ovalTransform)

    UIColor.gray.setFill()
    ovalPath.fill()
}

回答1:

I wrote a post on how to create clock wipe animations here on SO a long time ago:

How do you achieve a "clock wipe"/ radial wipe effect in iOS?

The code is in Objective-C, but the technique is the same in Swift. it involves using a CAShapeLayer as a mask layer on your image's layer, and using a CABasicAnimation.

You have to set up the shape layer to contain a circle who's radius is 1/4 of the size of the image square, and who's stroke thickness is 1/2 the image square. Then you use a CABasicAnimation to animate the strokeEnd property of the shape layer.

It won't look right unless the view you are masking is square.

The result looks like this:



回答2:

You are like 50% complete. :) Now you need to create a class to hold your circular view and manipulate the end angle.

But before that, in PaintCode, hook up an angle type variable to the circle End Angle property. Like so:

Notice: in my example I'm using an image as the circle's Fill. Not sure if that is the approach you envisioned, but there you go. If you are going to follow this path, make sure the image you used in PaintCode is also in Xcode under Assets.xcassets (using the same name referenced in the StyleKit class).

Now back to that class. This could be a regular UIView, with a reference to the angle type property; as well as functions to increment it, start and stop the animation:

@IBDesignable class CircleView: UIView {

    // MARK: - Properties

    var angle: CGFloat = 0 {
        didSet {
            setNeedsDisplay()
        }
    }

    // MARK: - Private Properties

    private var animationTimer = Timer()

    // MARK: - Lifecycle

    override func draw(_ rect: CGRect) {
        StyleKit.drawCircleShape(frame: rect, resizing: .aspectFill, angle: angle)
    }
}

// MARK: - Interface

extension CircleView {

    func startAnimating() {
        animationTimer = Timer.scheduledTimer(
            timeInterval: 0.01,
            target: self,
            selector: #selector(updateAngle),
            userInfo: nil,
            repeats: true
        )
        animationTimer.fire()
    }

    func stopAnimating() {
        animationTimer.invalidate()
    }

    @objc func updateAngle() {
        if angle == 360 { angle = 0 }
        angle += 1
    }
}

This of course is just an example to give you an idea. Your implementation may vary.

Finally, you could use a ViewController to start / stop the animation as in:

class CircleViewController: UIViewController {

    // MARK: - Private Properties

    @IBOutlet private weak var circleView: CircleView! {
        didSet {
            circleView.startAnimating()
        }
    }

    // MARK: - Lifecycle

    override func viewDidDisappear(_ animated: Bool) {
        circleView.stopAnimating()
    }
}

Final result:

That's it. You can find the project here (it's the Fifth test):
https://github.com/backslash-f/paintcode-tests

Ah, another thing to note: as soon as I started using an image to fill my circular shape, Interface Builder gave me some weird errors: IB Designables: Failed to render and update auto layout status for... That's something to investigate. Not sure if it's reproducible tho.

Cheers!