2

I'm writing an app that could make good use of the Apple Watch's fitness tracker design, here:

enter image description here

So far, I've created the basic outline which is just a CAShapeLayer with a CGPath of an ellipse. I use strokeStart and strokeEnd to animate the progress. My problem comes when applying a gradient to the outline. How do I apply a gradient like above to the stroke of a CGPath?

Adam Carter
  • 4,741
  • 5
  • 42
  • 103
  • Hey Adam, Did you try this CoreGraphics work on Apple Watch App? I am having trouble implementing CG functionalities in WatchKit app. – Hawk-Eye Apr 06 '15 at 07:13
  • Yes and no. I found to animate you can just animate the stroke start/stroke end of a CAShapeLayer, and to get the gradient you draw the path to the context and add a gradient to the context (see other questions of mine on here) but the problem is that you can't animate the context's content because when it renders it's just a bitmap. So I'm not sure how to combine the two separate things in to one – Adam Carter Apr 06 '15 at 11:42
  • Hey @AdamCarter, how you use CAShapelayer in Watchkit. QuartzCore framework is missing in WatchOS – Manish Sep 10 '19 at 05:03

2 Answers2

4

The cleanest way to do this without having to drop down to Core Graphics or GL is to create a layer containing the angle gradient that you want the ring filled with, mask it with a CAShapeLayer containing your circular path (with the appropriate line width and cap settings), then, as you’re currently doing, use the shape layer’s strokeEnd property to set the “fill” percentage. Note that there isn’t a built-in way to create an angle gradient—you can use one of the suggestions in this answer for that.

edit: Also, you’ll need a pair of semicircular “cap” images, one at each end of the ring—as the fill percentage gets close to 100%, the region at the top will reveal the discontinuity between the start and end color. In your example image above, you’d need a red semicircle oriented like this ( at the start, and a pink one oriented like this ) with a translation/rotation transform tracking the end.

additional edit: Also also, since the end-cap semicircle will be moving along the gradient, you’ll need it to change color, interpolating from the start color to the end color as the fill amount goes from 0% to 100%. Best way to do that is with a shape layer with a semicircular path, since you can set the fillColor of that without having to redraw image contents.

Community
  • 1
  • 1
Noah Witherspoon
  • 57,021
  • 16
  • 130
  • 131
  • This is what I did. I ended up adding a linear gradient but that stopped the implicit animations and explicit animations in CA (here: http://stackoverflow.com/questions/29056332/cashapelayer-s-strokeend-doesn-t-animate) so for now removed the gradient created in Quartz to leave just a block colour. – Adam Carter Mar 17 '15 at 17:25
  • Not sure what you mean by adding it—if you’re filling the gradient in a layer’s `-drawRect:` then yeah it might be too slow to animate, but if you create an image with the gradient beforehand and just use that image as the masked layer’s contents then it should work better. – Noah Witherspoon Mar 17 '15 at 17:33
  • What I meant is that I was drawing an arc straight to the graphics context. So instead of a full arc and managing its size based on the stroke end property, I drew a non CAShapeLayer arc which obviously cannot be animated as its not a party of the ca layer – Adam Carter Mar 17 '15 at 21:14
0

We did this for an iOS app.. but quickly stopped as it gets bogged down quickly. I think Apple is using images.. as they do in the Lister example

Jonny
  • 1