Animation with UIBezierPath - ios

I searched a lot for this topic, all answers said that i have to use CAShapeLayer or Layers and ... . the problem is i don't really know how to use them, and i know a solution to my problem; i'd just like to know if there's any way to do this without CAShapeLayer.
I'm trying to make a Star shaped rating view. there's a star, and a rectangle inside it from bottom to top indicating percentage of the rated star.
here is my .h file:
IB_DESIGNABLE
#interface FSStarRatingView : UIView
#property (assign, nonatomic) IBInspectable BOOL transparentInside;
#property (assign, nonatomic) IBInspectable CGFloat innerRadius; // between 0-1
#property (assign, nonatomic) IBInspectable CGFloat percentage; // between 0-1
#property (strong, nonatomic) IBInspectable UIColor *fillColor;
#property (strong, nonatomic) IBInspectable UIColor *unfilledColor; // works if transparentInside is YES
#property (strong, nonatomic) IBInspectable UIColor *strokeColor;
- (void)setPercentage:(CGFloat)percentage withAnimationDuration:(CGFloat)duration;
and here is my .m file
- (UIBezierPath *)starShapedBezierPath {
CGPoint points[10];
// points[0] = CGPointMake(self.center.x, 0);
CGFloat outterRadius = self.frame.size.width / 2;
CGFloat startDegree = 3.0 * M_PI_2;
CGPoint center = CGPointMake(outterRadius, outterRadius * 2.0/1.85);
for (int i = 0; i < 10; i+=2) {
points[i] = CGPointMake(center.x + outterRadius * cos(startDegree),
center.y + outterRadius * sin(startDegree));
startDegree += 0.4 * M_PI;
}
CGFloat innerRadius = self.innerRadius * outterRadius;
startDegree = M_PI_2;
for (int i = 5; i < 15; i+=2) {
points[i%10] = CGPointMake(center.x + innerRadius * cos(startDegree),
center.y + innerRadius * sin (startDegree));
startDegree += 0.4 * M_PI;
}
UIBezierPath *star = [UIBezierPath bezierPath];
[star moveToPoint:points[0]];
for (int i = 1; i < 10; i++)
[star addLineToPoint:points[i]];
[star closePath];
return star;
}
- (void)setPercentage:(CGFloat)percentage {
_percentage = percentage;
[self setNeedsDisplay];
}
- (void)setPercentage:(CGFloat)percentage withAnimationDuration:(CGFloat)duration{
[UIView
animateWithDuration:duration
animations:^{
self.percentage = percentage;
}];
}
- (void)drawRect:(CGRect)rect {
UIBezierPath *star = [self starShapedBezierPath];
star.lineWidth = 1.0;
[self.strokeColor setStroke];
[star stroke];
[star addClip];
CGFloat topBottomOffset = 0.15 * self.frame.size.width / 4.0;
CGFloat availableHeight = self.frame.size.height - 2.0 * topBottomOffset;
CGFloat usedHeight = availableHeight * self.percentage;
if (! self.transparentInside) {
UIBezierPath *unusedPercentageRect = [UIBezierPath bezierPathWithRect:CGRectMake(0, topBottomOffset, self.frame.size.width, availableHeight - usedHeight)];
[self.unfilledColor setFill];
[unusedPercentageRect fill];
}
UIBezierPath *usedPercentageRect = [UIBezierPath bezierPathWithRect:CGRectMake(0, topBottomOffset + availableHeight - usedHeight, self.frame.size.width, usedHeight)];
[self.fillColor setFill];
[usedPercentageRect fill];
[star addClip];
}
I know it is possible to do it with 2 subviews and animating their frame with animation, just wondering if there's a way to do it directly with Bezierpaths...
thanks

Related

IB_DESIGNABLE tintColor issues

I'm getting into IB_DESIGNABLE, and I've stumbled over an issue.
When I set the tintColor of my custom view with IB, it is rendered in the right way in the IB.
But when I run it on the device it is displayed with default tintColor.
#pragma mark - UIView
- (void)drawRect:(CGRect)rect {
[self drawCircleRadius:MIN(rect.size.width / 2, rect.size.height / 2) - self.lineWidth / 2.f
rect:rect
startAngle:self.startAngleRadians
endAngle:self.endAngleRadians
lineWidth:self.lineWidth];
}
#pragma mark - private methods
- (void)drawCircleRadius:(CGFloat)radius
rect:(CGRect)rect
startAngle:(CGFloat)startAngle
endAngle:(CGFloat)endAngel
lineWidth:(CGFloat)lineWidth {
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[self.tintColor setStroke];
[bezierPath addArcWithCenter:CGPointMake(rect.size.width / 2, rect.size.height / 2)
radius:radius
startAngle:startAngle
endAngle:endAngel
clockwise:YES];
bezierPath.lineWidth = lineWidth;
[bezierPath stroke];
}
What the difference? Why is it displayed with the default tint color in the device, and correctly displayed in IB?
UPDATE:
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface PKCircleView : UIView
#property (nonatomic, assign) IBInspectable CGFloat startAngleRadians;
#property (nonatomic, assign) IBInspectable CGFloat endAngleRadians;
#property (nonatomic, assign) IBInspectable CGFloat lineWidth;
#end
The issue was on that line
self.tintColor = [UIColor defaultDwonloadButtonBlueColor];
:
static PKCircleView *CommonInit(PKCircleView *self) {
if (self != nil) {
self.backgroundColor = [UIColor clearColor];
self.startAngleRadians = M_PI * 1.5;
self.endAngleRadians = self.startAngleRadians + (M_PI * 2);
self.lineWidth = 1.f;
self.tintColor = [UIColor defaultDwonloadButtonBlueColor];
}
return self;
}
#implementation PKCircleView
#pragma mark - initialization
- (id)initWithCoder:(NSCoder *)decoder {
return CommonInit([super initWithCoder:decoder]);
}
- (instancetype)initWithFrame:(CGRect)frame {
return CommonInit([super initWithFrame:frame]);
}
it seems like setTintColor from IB is called before init... methods.

iOS Custom Annotation: A view below the annotation pin

I need to replace the default annotation view with my custom annotation view.
I need the do following things:
Custom Annotation view with an image view embedded in it.
A view below it which contains a label in it.
For more clarification see the image:
In the above image I need to place an image view in the white space which you can see in the image in circular form, next I also need to add a view which contains a label on which I can set any text like me, friends, etc...
So, for this I searched number of questions on stack overflow but didn't got my answer. I don't want it on call out, I just want it simply as annotation when map is rendered. I have tried to make a custom class for this but not getting any idea how to deal with this.
Any help will be highly appreciated
You could just create your own annotation view:
#import MapKit;
#interface CustomAnnotationView : MKAnnotationView
#end
#interface CustomAnnotationView ()
#property (nonatomic) CGSize textSize;
#property (nonatomic) CGSize textBubbleSize;
#property (nonatomic, weak) UILabel *label;
#property (nonatomic) CGFloat lineWidth;
#property (nonatomic) CGFloat pinRadius;
#property (nonatomic) CGFloat pinHeight;
#property (nonatomic, strong) UIBezierPath *pinPath;
#property (nonatomic, strong) UIBezierPath *textBubblePath;
#end
#implementation CustomAnnotationView
- (instancetype)initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if (self) {
self.lineWidth = 1.0;
self.pinHeight = 40;
self.pinRadius = 15;
UILabel *label = [[UILabel alloc] init];
label.textAlignment = NSTextAlignmentCenter;
label.font = [UIFont preferredFontForTextStyle:UIFontTextStyleCallout];
label.textColor = [UIColor whiteColor];
[self addSubview:label];
self.label = label;
[self adjustLabelWidth:annotation];
self.opaque = false;
}
return self;
}
- (void)setAnnotation:(id<MKAnnotation>)annotation {
[super setAnnotation:annotation];
if (annotation) [self adjustLabelWidth:annotation];
}
- (void)adjustLabelWidth:(id<MKAnnotation>)annotation {
NSString *title = [annotation title];
NSDictionary *attributes = #{NSFontAttributeName : self.label.font};
self.textSize = [title sizeWithAttributes:attributes];
CGFloat delta = self.textSize.height * (1.0 - sinf(M_PI_4)) * 0.55;
self.textBubbleSize = CGSizeMake(self.textSize.width + delta * 2, self.textSize.height + delta * 2);
self.label.frame = CGRectMake(0, self.pinHeight, self.textBubbleSize.width, self.textBubbleSize.height);
self.label.text = title;
self.frame = CGRectMake(0, 0, self.textBubbleSize.width, self.pinHeight + self.textBubbleSize.height);
self.centerOffset = CGPointMake(0, self.frame.size.height / 2.0 - self.pinHeight);
}
- (void)drawRect:(CGRect)rect {
CGFloat radius = self.pinRadius - self.lineWidth / 2.0;
CGPoint startPoint = CGPointMake(self.textBubbleSize.width / 2.0, self.pinHeight);
CGPoint center = CGPointMake(self.textBubbleSize.width / 2, self.pinRadius);
CGPoint nextPoint;
// pin
self.pinPath = [UIBezierPath bezierPath];
[self.pinPath moveToPoint:startPoint];
nextPoint = CGPointMake(self.textBubbleSize.width / 2 - radius, self.pinRadius);
[self.pinPath addCurveToPoint:nextPoint
controlPoint1:CGPointMake(startPoint.x, startPoint.y - (startPoint.y - nextPoint.y) / 2.0)
controlPoint2:CGPointMake(nextPoint.x, nextPoint.y + (startPoint.y - nextPoint.y) / 2.0)];
[self.pinPath addArcWithCenter:center radius:radius startAngle:M_PI endAngle:0 clockwise:TRUE];
nextPoint = startPoint;
startPoint = self.pinPath.currentPoint;
[self.pinPath addCurveToPoint:nextPoint
controlPoint1:CGPointMake(startPoint.x, startPoint.y - (startPoint.y - nextPoint.y) / 2.0)
controlPoint2:CGPointMake(nextPoint.x, nextPoint.y + (startPoint.y - nextPoint.y) / 2.0)];
[[UIColor blackColor] setStroke];
[[UIColor colorWithRed:0.0 green:0.5 blue:1.0 alpha:0.8] setFill];
self.pinPath.lineWidth = self.lineWidth;
[self.pinPath fill];
[self.pinPath stroke];
[self.pinPath closePath];
// bubble around label
if ([self.annotation.title length] > 0) {
self.textBubblePath = [UIBezierPath bezierPath];
CGRect bubbleRect = CGRectInset(CGRectMake(0, self.pinHeight, self.textBubbleSize.width, self.textBubbleSize.height), self.lineWidth / 2, self.lineWidth / 2);
self.textBubblePath = [UIBezierPath bezierPathWithRoundedRect:bubbleRect
cornerRadius:bubbleRect.size.height / 2];
self.textBubblePath.lineWidth = self.lineWidth;
[self.textBubblePath fill];
[self.textBubblePath stroke];
} else {
self.textBubblePath = nil;
}
// center white dot
self.pinPath = [UIBezierPath bezierPathWithArcCenter:center radius:radius / 3.0 startAngle:0 endAngle:M_PI * 2.0 clockwise:TRUE];
self.pinPath.lineWidth = self.lineWidth;
[[UIColor whiteColor] setFill];
[self.pinPath fill];
}
- (UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event {
if ([self.pinPath containsPoint:point] || [self.textBubblePath containsPoint:point])
return self;
return nil;
}
#end
That yields something like:
Clearly, you can customize this to your heart's content, but it illustrates the basic idea: Write a MKAnnotationView subclass that overrides initWithAnnotation:reuseIdentifier: and implement your own drawRect.

How to draw a circle according to the offset when drop down the tableView

I know how to draw the circle according to the offset.
If i drop down the tableView too fast to see the process of the draw,so i want to make it slower.How to make it? Thanks.
This is my circle.m
property progress is the offset (0.0~1.0).
- (void)drawRect:(CGRect)rect {
[WMFontColor888888 setStroke];
CGFloat startAngle = - M_PI * 80 / 180;
CGFloat step = 0.0;
step = 33 * M_PI/18 * self.progress;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2) radius:self.bounds.size.width / 2 - 3 startAngle:startAngle endAngle:startAngle + step clockwise:YES];
path.lineWidth = 1.5;
path.lineCapStyle = kCGLineCapRound;
path.lineJoinStyle = kCGLineJoinRound;
[path stroke];
}
If you dig into the layer level, you can subclass CAShapeLayer and use -[CAShapeLayer strokeStart] and -[CAShapeLayer strokeEnd]. Then, you can simply move your CG code into -drawInContext:
Example:
#implementation MyView
- (Class)layerClass
{
return [MyCircleLayer class];
}
- (void)setProgress:(CGFloat)progress
{
MyCircleLayer *layer = (id)self.layer;
[layer setProgress:progress];
}
#end
#interface MyCircleLayer : CAShapeLayer
#property (nonatomic, assign) CGFloat progress;
#end
#implementation MyCircleLayer
// Vary strokeStart/strokeEnd based on where or how you want to animate the drawing
- (void)setProgress:(CGFloat)progress
{
_progress = progress;
/**
Constantly updating strokeStart/strokeEnd should queue up redraws and
should draw fluidly, but this could be delayed with an interval or via layer animation
*/
[self setStrokeEnd:progress];
}
- (void)drawInContext:(CGContextRef)ctx
{
[WMFontColor888888 setStroke];
CGFloat startAngle = - M_PI * 80 / 180;
CGFloat step = 0.0;
// Draw the full circle
...
[path stroke];
}

Why isn't my curved text centering itself?

I have a UIView subclass called dpCurvedLabel. It uses CATextLayers to curved text around an arc. It works fine, except that I can't get it perfectly centered in the layers parent view. I want the center point for the arc to be at the very center of the view (even if the view is smaller) so all the text characters are the same distance from the center.
I can get it CLOSE but it's always a at least few pixels off. The amount it's 'off' seems to be effected by the frame size I give each CATextLayer. There's something wrong with the math, but I can't figure out what. My code:
// instantiate a dpCurvedLabel in a super view
dpCurvedLabel *curvedLabel = [[dpCurvedLabel alloc]
initWithFrame:CGRectMake(0, 0, 200, 200) arcSize:360 radius:50
text:#"curve curve curve " font:[UIFont fontWithName:#"HelveticaNeue" size:18]
textColor:[UIColor whiteColor]];
// You can animate a rotation to see a more pronounced effect
// [curvedLabel rotateContinously];
[self addSubview:curvedLabel];
dpCurvedLabel.h
#import <UIKit/UIKit.h>
#interface dpCurvedLabel : UIView
#property CGFloat arcSize;
#property CGFloat radius;
#property (strong) NSString *text;
#property (strong) UIFont *font;
#property (strong) UIColor *textColor;
- (id)initWithFrame:(CGRect)frame arcSize:(CGFloat)arcSize radius:(CGFloat)radius
text:(NSString *)text font:(UIFont *)font
textColor:(UIColor *)textColor;
- (void)rotateContinously;
+ (void)makeCurvedText:(CALayer *)layer arcSize:(CGFloat)arcSize radius:(CGFloat)radius
text:(NSString *)text font:(UIFont *)font textColor:(UIColor *)textColor;
#end
dpCurvedLabel.m
#import "dpCurvedLabel.h"
#implementation dpCurvedLabel
- (id)initWithFrame:(CGRect)frame arcSize:(CGFloat)arcSize radius:(CGFloat)radius text:(NSString *)text font:(UIFont *)font textColor:(UIColor *)textColor
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.opaque = NO;
self.clipsToBounds = NO;
self.arcSize = arcSize;
self.radius = radius;
self.text = text;
self.font = font;
self.textColor = textColor;
}
return self;
}
- (void)layoutSublayersOfLayer:(CALayer *)layer
{
[super layoutSublayersOfLayer:layer];
NSLog(#"laying out sublayers!");
[dpCurvedLabel makeCurvedText:layer arcSize:self.arcSize radius:self.radius text:self.text font:self.font textColor:self.textColor];
}
+ (void)makeCurvedText:(CALayer *)layer arcSize:(CGFloat)arcSize radius:(CGFloat)radius text:(NSString *)text font:(UIFont *)font textColor:(UIColor *)textColor
{
layer.sublayers = nil;
layer.masksToBounds = NO;
CGFloat arcStart = 0;
CGFloat shiftH = 0;
CGFloat shiftV = 0;
BOOL clockwise = YES;
BOOL debugMode = YES;
CGFloat xcenter = CGRectGetMidX(layer.bounds);
CGFloat ycenter = CGRectGetMidY(layer.bounds);
CGFloat angle = arcStart;
CGFloat angleStep = arcSize / text.length;
for ( NSUInteger i = 0; i < text.length; ++i )
{
NSRange range = { .location = i, .length = 1 };
NSString *c = [text substringWithRange:range];
CGFloat yoffset = sin( degreesToRadians(angle) ) * radius;
CGFloat xoffset = cos( degreesToRadians(angle) ) * radius;
CGFloat rotAngle = 90 - angle;
if ( clockwise )
{
yoffset = -yoffset;
rotAngle = -90 + angle;
}
CATextLayer* tl = [[CATextLayer alloc] init];
tl.masksToBounds = NO;
tl.wrapped = NO;
tl.truncationMode = kCATruncationNone;
if ( debugMode )
{
tl.borderWidth = 1;
tl.cornerRadius = 3;
tl.borderColor = [UIColor whiteColor].CGColor;
}
// Text layer frame determined here. Effects how arc is centered.
CGSize charSize = CGSizeMake(20, 20);
tl.frame = CGRectMake( shiftH + xcenter - xoffset, shiftV + ycenter + yoffset, charSize.width, charSize.height );
// *******
tl.font = (__bridge CFTypeRef)(font.fontName);
tl.fontSize = font.pointSize;
tl.foregroundColor = textColor.CGColor;
tl.string = c;
tl.alignmentMode = #"center";
tl.transform = CATransform3DMakeAffineTransform( CGAffineTransformMakeRotation( degreesToRadians(rotAngle) ) );
[layer addSublayer:tl];
angle += angleStep;
}
if ( debugMode )
{
layer.backgroundColor = RGBA(0x00, 0x00, 0x00, .6).CGColor;
}
}
- (void)rotateContinously
{
CABasicAnimation *rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI * 2.0 /* full rotation*/ * 1 * 1 ];
rotationAnimation.duration = 5;
rotationAnimation.cumulative = YES;
rotationAnimation.repeatCount = INT_MAX;
[self.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
}
#end
What's wrong with the math here? Why won't this text arc center itself?
The problem comes from the fact that you are setting the frame of each text layer instead of its position. This means that you are positioning the lower left corner to be at the location where the position should be. Do this instead:
tl.position = CGPointMake( shiftH + xcenter - xoffset, shiftV + ycenter + yoffset);
tl.bounds = CGRectMake(0,0,charSize.width,charSize.height);
And you will find your layers to be exactly where you want them.

IOS : Animate transformation from a line to a bezier curve

I would like to animate a straight line curving into a bezier curve (from "_" to "n"), is there a library somewhere that can help me to do it ?
I know how to draw a Bezier curve with UIBezierPath, I could redraw rapidly and progressively do the transformation, but if something already does that it would be cool :-)
I might do something with a CADisplayLink. For example, you could do this in your view controller using a CAShapeLayer, e.g.:
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#interface ViewController ()
#property (nonatomic) CFTimeInterval firstTimestamp;
#property (nonatomic, strong) CAShapeLayer *shapeLayer;
#property (nonatomic, strong) CADisplayLink *displayLink;
#property (nonatomic) NSUInteger loopCount;
#end
static CGFloat const kSeconds = 5.0;
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self addShapeLayer];
[self startDisplayLink];
}
- (void)addShapeLayer
{
self.shapeLayer = [CAShapeLayer layer];
self.shapeLayer.path = [[self pathAtInterval:0.0] CGPath];
self.shapeLayer.fillColor = [[UIColor clearColor] CGColor];
self.shapeLayer.lineWidth = 3.0;
self.shapeLayer.strokeColor = [[UIColor redColor] CGColor];
[self.view.layer addSublayer:self.shapeLayer];
}
- (void)startDisplayLink
{
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(handleDisplayLink:)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)stopDisplayLink
{
[self.displayLink invalidate];
self.displayLink = nil;
}
- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
if (!self.firstTimestamp)
self.firstTimestamp = displayLink.timestamp;
self.loopCount++;
NSTimeInterval elapsed = (displayLink.timestamp - self.firstTimestamp);
self.shapeLayer.path = [[self pathAtInterval:elapsed] CGPath];
if (elapsed >= kSeconds)
{
[self stopDisplayLink];
self.shapeLayer.path = [[self pathAtInterval:0] CGPath];
self.statusLabel.text = [NSString stringWithFormat:#"loopCount = %.1f frames/sec", self.loopCount / kSeconds];
}
}
- (UIBezierPath *)pathAtInterval:(NSTimeInterval) interval
{
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0, self.view.bounds.size.height / 2.0)];
CGFloat fractionOfSecond = interval - floor(interval);
CGFloat yOffset = self.view.bounds.size.height * sin(fractionOfSecond * M_PI * 2.0);
[path addCurveToPoint:CGPointMake(self.view.bounds.size.width, self.view.bounds.size.height / 2.0)
controlPoint1:CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0 - yOffset)
controlPoint2:CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0 + yOffset)];
return path;
}
#end
Alternatively, if you wanted to do it by subclassing UIView, you could do it like so:
#import "View.h"
#import <QuartzCore/QuartzCore.h>
#interface View ()
#property (nonatomic, strong) CADisplayLink *displayLink;
#property (nonatomic) CFTimeInterval firstTimestamp;
#property (nonatomic) CFTimeInterval displayLinkTimestamp;
#property (nonatomic) NSUInteger loopCount;
#end
static CGFloat const kSeconds = 5.25;
#implementation View
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self startDisplayLink];
}
return self;
}
- (void)startDisplayLink
{
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(handleDisplayLink:)];
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)stopDisplayLink
{
[self.displayLink invalidate];
self.displayLink = nil;
}
- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
if (!self.firstTimestamp)
self.firstTimestamp = displayLink.timestamp;
self.displayLinkTimestamp = displayLink.timestamp;
self.loopCount++;
[self setNeedsDisplayInRect:self.bounds];
NSTimeInterval elapsed = (displayLink.timestamp - self.firstTimestamp);
if (elapsed >= kSeconds)
{
[self stopDisplayLink];
self.displayLinkTimestamp = self.firstTimestamp + kSeconds;
[self setNeedsDisplayInRect:self.bounds];
self.statusLabel.text = [NSString stringWithFormat:#"loopCount = %.1f frames/sec", self.loopCount / kSeconds];
}
}
- (UIBezierPath *)pathAtInterval:(NSTimeInterval) interval
{
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0, self.bounds.size.height / 2.0)];
CGFloat fractionOfSecond = interval - floor(interval);
CGFloat yOffset = self.bounds.size.height * sin(fractionOfSecond * M_PI * 2.0);
[path addCurveToPoint:CGPointMake(self.bounds.size.width, self.bounds.size.height / 2.0)
controlPoint1:CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0 - yOffset)
controlPoint2:CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0 + yOffset)];
return path;
}
- (void)drawRect:(CGRect)rect
{
NSTimeInterval elapsed = (self.displayLinkTimestamp - self.firstTimestamp);
UIBezierPath *path = [self pathAtInterval:elapsed];
[[UIColor redColor] setStroke];
path.lineWidth = 3.0;
[path stroke];
}
#end
I test both the subclassed UIView as well as the view controller, and they both resulted in roughly 60 frames per second.

Resources