0

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:

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()
}
jscs
  • 63,694
  • 13
  • 151
  • 195
  • Clockwise disappearing? You mean like this? http://i.stack.imgur.com/stadH.gif. If so that's called a "Clock Wipe" animation and I have no idea how to do it in PaintCode. Can PaintCode even generate animations? – Duncan C Jan 27 '19 at 15:01
  • You need to edit your question to provide a clearer idea of what you're trying to do, and not just post several hundred lines of code. – Duncan C Jan 27 '19 at 15:03
  • So I want to make it like your gif. Yes, Paintcode can generate animations. – Konstantin Kostadinov Jan 27 '19 at 15:15
  • 1
    Even without paintcode animation, I have to just make a circle on top of the logo and make it with clock wipe animation? – Konstantin Kostadinov Jan 27 '19 at 15:20
  • You said: "Even without paintcode animation I have to... and make it with clock wipe animation?" What does that mean? You mean you want to be able to do an animation, with or without paintCode? – Duncan C Jan 27 '19 at 16:26
  • By the way you, in PaintCode 3.x you don't need that Frame anymore (and all those constraints configurations). It's much easier nowadays... Take a look at my answer and check the repo. In it, there's the PaintCode project file. You can check the absence of Frames. :-) – backslash-f Jan 27 '19 at 18:32
  • Next time please [edit your closed question](https://stackoverflow.com/questions/54388383/paintcode-disappearing-circle-on-top-of-logo) rather than reposting. – jscs Jan 27 '19 at 19:20
  • I'm new in this community and I tried but couldn't delete it :/ – Konstantin Kostadinov Jan 28 '19 at 23:50

2 Answers2

1

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:

PaintCode

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:

Example

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!

backslash-f
  • 7,923
  • 7
  • 52
  • 80
  • Super mate, thanks for sharing this. I literally love you . But how can I stop the rotation and make the circle/the image that go a circle shape disappear after the rotation ? – Konstantin Kostadinov Jan 28 '19 at 23:25
  • Well, we know that when the degrees reach 360, the animation is done. We could use that in our favor and delegate the dismissal to the presenter (in this case the CircleViewController). I pushed the delegation example, please pull. Now I know you are new to this community, and your question is beginning to diverge... It's better to keep our questions small and straightforward. (Also, accept the answer as it addresses your original issue: https://stackoverflow.com/help/someone-answers) – backslash-f Jan 29 '19 at 08:13
0

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:

Clock Wipe animation GIF

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • Do you mind posting your Swift code as an answer? I haven't had time to convert my old Objective-C code to Swift, and it would be nice to have it updated. – Duncan C Jan 29 '19 at 14:46
  • I made it in Objective-C but u can convert it to Swift with "Swiftify for Xcode" rather I don't know how to used just heard of it. Still I want everything in my app to be in swift so I made it with the Paintcode like "backslash-f". – Konstantin Kostadinov Jan 29 '19 at 14:51