I'm trying to achieve something similar to the attached image, where the circle is animated depending on your level progress and then a label is attached to both the end of the animated path to show the gained experience and also the undrawn part of the circle to show the remaining experience. I have the circle animating as wanted but am having trouble coming up with a solution to the labels to appear in the right spot.
I've tried setting the position of the label to the path.currentPoint but that always seems to be the start of the drawn path and not the end.
Any pointers on how to achieve this would be great!
I had been working on your question, first of all to achieve this you must animate the path the real path, not only the strokeEnd, if you animate only the strokeEnd your path.currentPoint will always return the circle endPoint of the path, in order to animate the path you need to make a KeyFramed Animation animating the “path” as keyPath and passing an array of paths from current angle to desired one, then in order to set the correct position for your label you need to get de currentPoint of all this paths values and making another keyFramed animation with “position” keyPath and passing as values all this points collected from paths array
This is a basic example working
The code is in GitHub in this Repo
You have a lot of work to do yet but, this can be an starting point for your final solution
Hope this helps, best regards
Related
I have a CGPath, and on that path I detect touches. The path is basically a line or a curve, but it is not filled. The idea is that the user strokes the path, so wherever the user has stroked along the path I'd like to draw the path with a different stroke/color etc.
First, to find if the touches are inside the path, and to give the user some room for error, I'm using CGPathCreateCopyByStrokingPath, and CGPointInsidePath. This works fine.
Next, I need to find how far along the path the user has stroked. That is today's question.
When that is done, and I have an answer like 33%, then I'd be a simple assignment to strokeEnd on the path. (two copies, one complete path, and one on top with strokeEnd set to whatever percentage of the complete path the user has stroked with his finger so far).
Any ideas how to find how far along the path the user has stroked his finger?
I have worked on this quite a while, and would like to share my answer (I'm the one who asked the question btw).
I want to detect touches along a path (e.g a Bezier Curve). Problem is that it can only tell you if a touch hit the path, but not where (using CGPathContainsPoint).
To avoid having the user hit exactly on the path, create a fatter version with CGPathCreateCopyByStrokingPath), so that's the one to check for touch on.
The main problem still is, where along the path did the touch hit. What I did was to create a dashed path copy with CGPathCreateCopyByDashingPath with minimal spacing. This path is never actually displayed. Then I got every part of it with the CGPathApply saving every path element in an array.
When a touch is detected, and is within the "fat path", then I just loop through the array of path-dash-elements, checking the distance (just regular Pythagoras) to the start-point of each path-dash-element.
If the touch-point is closest to the 14'th element, and there are e.g. 140 elements total, that means we're 10% along the way.
Now that we have the percentage. What I want now is to paint the path as far as the touch. I tried to use the suggested strokeEnd property on a path. That didn't match my calculated percentage at all. Actually, it seemed to me to be quite inaccurate. Or maybe my calculation is.
Anyway, since we already have all the path-elements, and know which one to stop at (the 14th element), what I did was to build a new path using the path-elements. Since I didn't want a dashed path (that was just a trick to find out where the touch was on the long continuous path), I just skipped all the moveTo path-elements (except the very first). That will create a continous path.
CGPathApply - by request
// MARK: - Stuff for CGPathApply
typealias MyPathApplier = #convention(block) (UnsafePointer<CGPathElement>) -> Void
// http://stackoverflow.com/questions/24274913/equivalent-of-or-alternative-to-cgpathapply-in-swift
// Note: You must declare MyPathApplier as #convention(block), because
// if you don't, you get "fatal error: can't unsafeBitCast between
// types of different sizes" at runtime, on Mac OS X at least.
func myPathApply(path: CGPath!, block: MyPathApplier) {
let callback: #convention(c) (UnsafeMutablePointer<Void>, UnsafePointer<CGPathElement>) -> Void = { (info, element) in
let block = unsafeBitCast(info, MyPathApplier.self)
block(element)
}
CGPathApply(path, unsafeBitCast(block, UnsafeMutablePointer<Void>.self), unsafeBitCast(callback, CGPathApplierFunction.self))
}
I have a utility class GHPathUtilies.m which includes a method:
+(CGFloat) totalLengthOfCGPath:(CGPathRef)pathRef
This is all written in Objective-C, but presumably you could use the same technique to walk along the path elements, using CGPathApply, until you are near enough, or have passed the target point. The general idea is to break the curves between each element into small line segments and keep a running sum of the length of each segment.
Of course if the path doubles back on itself, you'll have to figure out which point is the touch point.
I would like to implement karaoke-like progress highlight for iOS.
I know I could use NSAttributedString and highlight the text character by character. However, I would like the highlight to progress pixel by pixel, not character by character.
Any ideas?
P.S. No need for sample code, just point me to the right direction.
Here is an example:
I can't think of any automatic way to do that. There would be several problems to solve. It would be pretty hard, I think.
The hardest would probably be figuring out the pixel position of each word so you can pace the coloring to match the timing within the music. With text and attributed layout, you could probably get the text engine to give you the boundaries of each word and then apply the color attribute to each word as it's spoken/sung. You'd have to have data about the time offset for the beginning and end of each word's being sung.
You might have to use Core Text to get layout information about the bounding rectangles of each word.
Once you get that you could build a path (UIBezierPath or CGPath; they're pretty interchangeable) that follows the flow of the text, and then install that path in to a shape layer. You could then make the text transparent, make the shape layer a colored background that shows through, and animate the shape layer's strokeStart and/or strokeEnd properties to make it fill the text. You might need to do it word by word with a short animation that interpolates between one word and the next to get the timing right.
You probably want to have a look at Core Text, which is the lower level framework used for laying out text, using this you can obtain necessary paths that you need to render said effect (I suggest starting from answers similar to this)
There are plenty of answers for alternative, perhaps simpler answers, for example character by character or word-by-word, which may be easier to implement.
I'm trying to animate an object along a specific segment of a path using CAKeyframeAnimation. Is this possible?
Think of a path as parameterized between 0 and 1, which is what an animation will typically run on (from the start of the curve to the end of the curve). Is there a way I can run an animation, say, from .25 to .75 of this path?
I know I can offset an animation with the 'offset' parameter, but this only adjusts the start position and still loops around to the end of the curve. It also doesn't correctly obey the CAMediaTiming function, if set.
I'm trying to customize a Coreplot graph in many ways I can and the next thing I would like to do is place the X Axis Labels (one that is custom as well) at the bottom of the graph, independent of the X axis' position (whether it's scrolled up or down).
To make it clear, it is similar to giving the labels an offset value of something like 50.0. But offset is not the property I'm looking for since it fixes the labels location relative to the X axis.
Any way this can be done? Or do I have to skip the axisLabels property and place and layer or something manually at the bottom of the graph?
EDIT: Alright, I managed to place an axis on the bottom with CPTConstraints. But it's not on the bottommost. If a plot point is on those levels, the plot line overlaps the labels. I tired padding of the graph but of course, it moves the whole graph, hence the issue persists.
Thanks in advance
Make a second x-axis. Have the first one draw the axis line, tick marks, etc., as normal but no labels. Label the second one and set all of the line style properties to nil so it doesn't draw any of the lines.
Turns out that aside from the graph, the plotAreaFrame property of the CPTGrpah also has paddings. If you give more paddings to plotAreaFrame than that of the graph, the plot will be drawn in a smaller frame and the rest of the graph area will be for you to add what you want (i.e., a second Axis).
Big thanks to #Eric, for trying to answer Every single CorePlot question as soon as possible.
CorePlot does have a lot of customisation than I thought.
I'm learning my way through UIBezierPaths by creating a table from scratch, and having individual cells filled with different colours.
This is a custom object I'm building which is contained in a subclassed UIView.
At the moment, I'm constructing this in this order:
'Cell' fill colours
Column lines
Row lines
Outer box (rounded rect)
As the picture suggests, I'm having trouble getting rid of the sharp corner of the cell fill outside the orange rounded rectangle.
Can anyone point me in the right direction to get rid of these?
Cheers! :)
At the beginning of your drawing code you should add the outer rounded rect path to the clipping path using its addClip method. That way nothing will get drawn that is outside this path.