Calling UIGestureRecognizer from UITouch object - ios

I am trying to associate a Gesture with UITouch object because I need to pass data through a target action Method.
I have something implemented but it is inefficient,it get 100% of the CPU processing, it is probably because it is coded incorrectly.
There is my instance method for the UITouch event which call the UIPangesture
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
return YES;
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc]
initWithTarget:self action:#selector(handlePan:)];
[self addGestureRecognizer: panGesture];
}
There is my instance method for the UIPAnGestureRecognizer
- (IBAction)handlePan:(UIPanGestureRecognizer *)event
{
if (event.numberOfTouches==1)
{
CGPoint lastPoint = [event locationOfTouch: event.numberOfTouches - 1 inView: event.view];
CGRect bounds = self.bounds;
CGPoint center = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
CGPoint delta = CGPointMake(lastPoint.x - center.x, lastPoint.y - center.y);
CGFloat angle = (delta.y == 0 ? delta.x >= 0 ? 0 : M_PI : atan2(delta.y, delta.x));
angle = fmod(angle, M_PI * 2.0);
angle += angle >= 0 ? 0 : M_PI * 2.0;
UIColor *color = [UIColor colorWithHue: angle / (M_PI * 2.0) saturation:1. brightness:1. alpha:1];
if ([color getRed: &r green: &g blue:&b alpha: &a]){
rValue = r;
gValue = g;
bValue = b;
[self setNeedsDisplay];
[self sendActionsForControlEvents:UIControlEventValueChanged];
[self updateLabels];
}
}
}
And there there the methods which call the action method to obtain the variables from the color picker
- (void)setup:(CsoundObj *)csoundObj
{
NSLog(#"Color value - R : %f G : %f : B %f", rValue, gValue, bValue);
channelPtrR = [csoundObj getInputChannelPtr:#"mix" channelType:CSOUND_CONTROL_CHANNEL];
channelPtrG = [csoundObj getInputChannelPtr:#"pitch" channelType:CSOUND_CONTROL_CHANNEL];
channelPtrB = [csoundObj getInputChannelPtr:#"c" channelType:CSOUND_CONTROL_CHANNEL];
cachedValueR = rValue;
cachedValueG = gValue;
cachedValueB = bValue;
self.cacheDirty = YES;
[self addTarget:self action:#selector(updateValueCache:) forControlEvents:UIControlEventValueChanged];
}
- (void)updateValueCache:(id)sender
{
cachedValueR = ((CustomView*)sender).rValue;
cachedValueG = ((CustomView*)sender).gValue;
cachedValueB = ((CustomView*)sender).bValue;
self.cacheDirty = YES;
}
- (void)updateValuesToCsound
{
if (self.cacheDirty) {
*channelPtrR = cachedValueR;
*channelPtrG = cachedValueG;
*channelPtrB = cachedValueB;
self.cacheDirty = NO;
}
}
- (void)updateValuesFromCsound
{
}
- (void)cleanup
{
[self removeTarget:self action:#selector(updateValueCache:) forControlEvents:UIControlEventValueChanged];
}
I know the problem it is on the instance method for the UITouch event which call the UIPangesture, any other way to do it more efficiently?

I think that you are calling this section of code way too often:
CGPoint lastPoint = [event locationOfTouch: event.numberOfTouches - 1 inView: event.view];
CGRect bounds = self.bounds;
CGPoint center = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
CGPoint delta = CGPointMake(lastPoint.x - center.x, lastPoint.y - center.y);
CGFloat angle = (delta.y == 0 ? delta.x >= 0 ? 0 : M_PI : atan2(delta.y, delta.x));
angle = fmod(angle, M_PI * 2.0);
angle += angle >= 0 ? 0 : M_PI * 2.0;
UIColor *color = [UIColor colorWithHue: angle / (M_PI * 2.0) saturation:1. brightness:1. alpha:1];
if ([color getRed: &r green: &g blue:&b alpha: &a]){
rValue = r;
gValue = g;
bValue = b;
[self setNeedsDisplay];
[self sendActionsForControlEvents:UIControlEventValueChanged];
[self updateLabels];
}
You are likely calling it when there is no change, or little change. There are two things you could do to reduce the number of calls. You can test for a significant change in the angle like this:
//class property
#property (nonatomic, assign) CGFloat lastAngle;
//In your code
CGFloat deltaAngle = fabs(self.lastAngle - angle)
if (deltaAngle > 0.5) { //select whatever delta works best
angle += angle >= 0 ? 0 : M_PI * 2.0;
UIColor *color = [UIColor colorWithHue: angle / (M_PI * 2.0) saturation:1. brightness:1. alpha:1];
if ([color getRed: &r green: &g blue:&b alpha: &a]){
rValue = r;
gValue = g;
bValue = b;
[self sendActionsForControlEvents:UIControlEventValueChanged];
[self updateLabels];
[self setNeedsDisplay];
}
}
Or you can test for time since the last update like this:
//class property
#property (nonatomic, retain) NSDate *lastTime;
//set it when the view loads
self.lastTime = [NSDate date];
//In your code
NSTimeInterval interval = [[NSDate date] timeIntervalSinceDate:lastdate];
self.lastTime = [NSDate date];
if (interval > 0.05) { //select whatever time interval works best
angle += angle >= 0 ? 0 : M_PI * 2.0;
UIColor *color = [UIColor colorWithHue: angle / (M_PI * 2.0) saturation:1. brightness:1. alpha:1];
if ([color getRed: &r green: &g blue:&b alpha: &a]){
rValue = r;
gValue = g;
bValue = b;
[self sendActionsForControlEvents:UIControlEventValueChanged];
[self updateLabels];
[self setNeedsDisplay];
}
}
Also, are you sure you need the [self setNeedsDisplay] ?

Related

Fill in the translucent color insider the circular slider

I would like to implement the custom circular slider for IOS. I refer to the EFCircularSlider to create my own one. When it comes to the customization such as filling in the translucent color insider the circular slider , i have found that this line is not working. Would you please tell me are there any other alternatives?
CGContextSetFillColorWithColor(ctx, [UIColor greenColor].CGColor );
The below is my code (EFCircularSlider.m)
#import "EFCircularSlider.h"
#import <QuartzCore/QuartzCore.h>
#import <CoreImage/CoreImage.h>
#define kDefaultFontSize 14.0f;
#define ToRad(deg) ( (M_PI * (deg)) / 180.0 )
#define ToDeg(rad) ( (180.0 * (rad)) / M_PI )
#define SQR(x) ( (x) * (x) )
#interface EFCircularSlider (private)
#property (readonly, nonatomic) CGFloat radius;
#end
#implementation EFCircularSlider {
int angle;
int fixedAngle;
NSMutableDictionary* labelsWithPercents;
NSArray* labelsEvenSpacing;
}
- (void)defaults {
// Defaults
_maximumValue = 100.0f;
_minimumValue = 0.0f;
_currentValue = 0.0f;
_lineWidth = 5;
_lineRadiusDisplacement = 0;
_unfilledColor = [UIColor lightGrayColor];
_filledColor = [UIColor blueColor];
_handleColor = _filledColor;
_labelFont = [UIFont systemFontOfSize:10.0f];
_snapToLabels = NO;
_handleType = EFSemiTransparentWhiteCircle;
_labelColor = [UIColor redColor];
_labelDisplacement = 2;
self.backgroundColor = [UIColor clearColor];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self defaults];
[self setFrame:frame];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self=[super initWithCoder:aDecoder])){
[self defaults];
}
return self;
}
#pragma mark - Setter/Getter
- (void)setFrame:(CGRect)frame {
[super setFrame:frame];
angle = [self angleFromValue];
}
- (CGFloat)radius {
//radius = self.frame.size.height/2 - [self circleDiameter]/2;
return self.frame.size.height/2 - _lineWidth/2 - ([self circleDiameter]-_lineWidth) - _lineRadiusDisplacement;
}
- (void)setCurrentValue:(float)currentValue {
_currentValue=currentValue;
if(_currentValue>_maximumValue) _currentValue=_maximumValue;
else if(_currentValue<_minimumValue) _currentValue=_minimumValue;
angle = [self angleFromValue];
[self setNeedsLayout];
[self setNeedsDisplay];
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
#pragma mark - drawing methods
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGContextRef ctx = UIGraphicsGetCurrentContext();
//Draw the unfilled circle
//CGContextAddArc(ctx, self.frame.size.width/2, self.frame.size.height/2, self.radius, 0, M_PI *2, 0);
CGContextAddArc(ctx, self.frame.size.width/2, self.frame.size.height/2, self.radius, 0, M_PI *2, 0);
[_unfilledColor setStroke];
CGContextSetLineWidth(ctx, _lineWidth);
CGContextSetLineCap(ctx, kCGLineCapButt);
CGContextDrawPath(ctx, kCGPathStroke);
//Draw the filled circle
if((_handleType == EFDoubleCircleWithClosedCenter || _handleType == EFDoubleCircleWithOpenCenter) && fixedAngle > 5) {
CGContextAddArc(ctx, self.frame.size.width/2 , self.frame.size.height/2, self.radius, 3*M_PI/2, 3*M_PI/2-ToRad(angle+3), 0);
} else {
CGContextAddArc(ctx, self.frame.size.width/2 , self.frame.size.height/2, self.radius, 3*M_PI/2, 3*M_PI/2-ToRad(angle), 0);
}
[_filledColor setStroke];
CGContextSetLineWidth(ctx, _lineWidth);
CGContextSetLineCap(ctx, kCGLineCapButt);
CGContextDrawPath(ctx, kCGPathStroke);
UIView *colourView = [[UIView alloc] initWithFrame:rect];
colourView.opaque = NO;
colourView.alpha = .7f;
//colourView.backgroundColor = [UIColor colorWithRed:0.13f green:0.14f blue:0.15f alpha:1.00f];
//CGContextSetStrokeColorWithColor(ctx, [UIColor greenColor].CGColor);
CGContextSetFillColorWithColor(ctx, [UIColor greenColor].CGColor );
// CGContextFillRect(ctx, (CGRect){ {0,0}, colourView.size} );
//Add the labels (if necessary)
if(labelsEvenSpacing != nil) {
[self drawLabels:ctx];
}
//The draggable part
[self drawHandle:ctx];
}
-(void) drawHandle:(CGContextRef)ctx{
CGContextSaveGState(ctx);
CGPoint handleCenter = [self pointFromAngle: angle];
if(_handleType == EFSemiTransparentWhiteCircle) {
[[UIColor colorWithWhite:0.3 alpha:0.7] set];
CGContextFillEllipseInRect(ctx, CGRectMake(handleCenter.x, handleCenter.y, _lineWidth, _lineWidth));
} else if(_handleType == EFSemiTransparentBlackCircle) {
[[UIColor colorWithWhite:0.0 alpha:0.7] set];
CGContextFillEllipseInRect(ctx, CGRectMake(handleCenter.x, handleCenter.y, _lineWidth, _lineWidth));
} else if(_handleType == EFDoubleCircleWithClosedCenter) {
[_handleColor set];
CGContextAddArc(ctx, handleCenter.x + (_lineWidth)/2, handleCenter.y + (_lineWidth)/2, _lineWidth, 0, M_PI *2, 0);
CGContextSetLineWidth(ctx, 7);
CGContextSetLineCap(ctx, kCGLineCapButt);
CGContextDrawPath(ctx, kCGPathStroke);
CGContextFillEllipseInRect(ctx, CGRectMake(handleCenter.x, handleCenter.y, _lineWidth-1, _lineWidth-1));
} else if(_handleType == EFDoubleCircleWithOpenCenter) {
[_handleColor set];
CGContextAddArc(ctx, handleCenter.x + (_lineWidth)/2, handleCenter.y + (_lineWidth)/2, _lineWidth/2 + 5, 0, M_PI *2, 0);
CGContextSetLineWidth(ctx, 4);
CGContextSetLineCap(ctx, kCGLineCapButt);
CGContextDrawPath(ctx, kCGPathStroke);
CGContextAddArc(ctx, handleCenter.x + _lineWidth/2, handleCenter.y + _lineWidth/2, _lineWidth/2, 0, M_PI *2, 0);
CGContextSetLineWidth(ctx, 2);
CGContextSetLineCap(ctx, kCGLineCapButt);
CGContextDrawPath(ctx, kCGPathStroke);
} else if(_handleType == EFBigCircle) {
[_handleColor set];
CGContextFillEllipseInRect(ctx, CGRectMake(handleCenter.x-2.5, handleCenter.y-2.5, _lineWidth+5, _lineWidth+5));
}
CGContextRestoreGState(ctx);
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
CGPoint p1 = [self centerPoint];
CGPoint p2 = point;
CGFloat xDist = (p2.x - p1.x);
CGFloat yDist = (p2.y - p1.y);
double distance = sqrt((xDist * xDist) + (yDist * yDist));
return distance < self.radius + 11;
}
-(void) drawLabels:(CGContextRef)ctx {
if(labelsEvenSpacing == nil || [labelsEvenSpacing count] == 0) {
return;
} else {
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0
NSDictionary *attributes = #{ NSFontAttributeName: _labelFont,
NSForegroundColorAttributeName: _labelColor
};
#endif
CGFloat fontSize = ceilf(_labelFont.pointSize);
NSInteger distanceToMove = -[self circleDiameter]/2 - fontSize/2 - _labelDisplacement;
for (int i=0; i<[labelsEvenSpacing count]; i++)
{
NSString *label = [labelsEvenSpacing objectAtIndex:[labelsEvenSpacing count] - i - 1];
CGFloat percentageAlongCircle = i/(float)[labelsEvenSpacing count];
CGFloat degreesForLabel = percentageAlongCircle * 360;
CGSize labelSize=CGSizeMake([self widthOfString:label withFont:_labelFont], [self heightOfString:label withFont:_labelFont]);
CGPoint closestPointOnCircleToLabel = [self pointFromAngle:degreesForLabel withObjectSize:labelSize];
CGRect labelLocation = CGRectMake(closestPointOnCircleToLabel.x, closestPointOnCircleToLabel.y, labelSize.width, labelSize.height);
CGPoint centerPoint = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
float radiansTowardsCenter = ToRad(AngleFromNorth(centerPoint, closestPointOnCircleToLabel, NO));
labelLocation.origin.x = (labelLocation.origin.x + distanceToMove * cos(radiansTowardsCenter));
labelLocation.origin.y = (labelLocation.origin.y + distanceToMove * sin(radiansTowardsCenter));
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0
[label drawInRect:labelLocation withAttributes:attributes];
#else
[_labelColor setFill];
[label drawInRect:labelLocation withFont:_labelFont];
#endif
}
}
}
#pragma mark - UIControl functions
-(BOOL) beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
[super beginTrackingWithTouch:touch withEvent:event];
return YES;
}
-(BOOL) continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
[super continueTrackingWithTouch:touch withEvent:event];
CGPoint lastPoint = [touch locationInView:self];
[self moveHandle:lastPoint];
[self sendActionsForControlEvents:UIControlEventValueChanged];
return YES;
}
-(void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event{
[super endTrackingWithTouch:touch withEvent:event];
if(_snapToLabels && labelsEvenSpacing != nil) {
CGFloat newAngle=0;
float minDist = 360;
for (int i=0; i<[labelsEvenSpacing count]; i++) {
CGFloat percentageAlongCircle = i/(float)[labelsEvenSpacing count];
CGFloat degreesForLabel = percentageAlongCircle * 360;
if(abs(fixedAngle - degreesForLabel) < minDist) {
newAngle=degreesForLabel ? 360 - degreesForLabel : 0;
minDist = abs(fixedAngle - degreesForLabel);
}
}
angle = newAngle;
_currentValue = [self valueFromAngle];
[self setNeedsDisplay];
}
}
-(void)moveHandle:(CGPoint)point {
CGPoint centerPoint;
centerPoint = [self centerPoint];
int currentAngle = floor(AngleFromNorth(centerPoint, point, NO));
angle = 360 - 90 - currentAngle;
_currentValue = [self valueFromAngle];
[self setNeedsDisplay];
}
- (CGPoint)centerPoint {
return CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
}
#pragma mark - helper functions
-(CGPoint)pointFromAngle:(int)angleInt{
//Define the Circle center
CGPoint centerPoint = CGPointMake(self.frame.size.width/2 - _lineWidth/2, self.frame.size.height/2 - _lineWidth/2);
//Define The point position on the circumference
CGPoint result;
result.y = round(centerPoint.y + self.radius * sin(ToRad(-angleInt-90))) ;
result.x = round(centerPoint.x + self.radius * cos(ToRad(-angleInt-90)));
return result;
}
-(CGPoint)pointFromAngle:(int)angleInt withObjectSize:(CGSize)size{
//Define the Circle center
CGPoint centerPoint = CGPointMake(self.frame.size.width/2 - size.width/2, self.frame.size.height/2 - size.height/2);
//Define The point position on the circumference
CGPoint result;
result.y = round(centerPoint.y + self.radius * sin(ToRad(-angleInt-90))) ;
result.x = round(centerPoint.x + self.radius * cos(ToRad(-angleInt-90)));
return result;
}
- (CGFloat)circleDiameter {
if(_handleType == EFSemiTransparentWhiteCircle) {
return _lineWidth;
} else if(_handleType == EFSemiTransparentBlackCircle) {
return _lineWidth;
} else if(_handleType == EFDoubleCircleWithClosedCenter) {
return _lineWidth * 2 + 3.5;
} else if(_handleType == EFDoubleCircleWithOpenCenter) {
return _lineWidth + 2.5 + 2;
} else if(_handleType == EFBigCircle) {
return _lineWidth + 2.5;
}
return 0;
}
static inline float AngleFromNorth(CGPoint p1, CGPoint p2, BOOL flipped) {
CGPoint v = CGPointMake(p2.x-p1.x,p2.y-p1.y);
float vmag = sqrt(SQR(v.x) + SQR(v.y)), result = 0;
v.x /= vmag;
v.y /= vmag;
double radians = atan2(v.y,v.x);
result = ToDeg(radians);
return (result >=0 ? result : result + 360.0);
}
-(float) valueFromAngle {
if(angle < 0) {
_currentValue = -angle;
} else {
_currentValue = 270 - angle + 90;
}
fixedAngle = _currentValue;
return (_currentValue*(_maximumValue - _minimumValue))/360.0f;
}
- (float)angleFromValue {
angle = 360 - (360.0f*_currentValue/_maximumValue);
if(angle==360) angle=0;
return angle;
}
- (CGFloat) widthOfString:(NSString *)string withFont:(UIFont*)font {
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, nil];
return [[[NSAttributedString alloc] initWithString:string attributes:attributes] size].width;
}
- (CGFloat) heightOfString:(NSString *)string withFont:(UIFont*)font {
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, nil];
return [[[NSAttributedString alloc] initWithString:string attributes:attributes] size].height;
}
#pragma mark - public methods
-(void)setInnerMarkingLabels:(NSArray*)labels{
labelsEvenSpacing = labels;
[self setNeedsDisplay];
}
#end
EFCircularSlider.h
#import <UIKit/UIKit.h>
#interface EFCircularSlider : UIControl
typedef NS_ENUM(NSInteger, EFHandleType) {
EFSemiTransparentWhiteCircle,
EFSemiTransparentBlackCircle,
EFDoubleCircleWithOpenCenter,
EFDoubleCircleWithClosedCenter,
EFBigCircle
};
#property (nonatomic) float minimumValue;
#property (nonatomic) float maximumValue;
#property (nonatomic) float currentValue;
#property (nonatomic) int lineWidth;
#property (nonatomic) int lineRadiusDisplacement;
#property (nonatomic, strong) UIColor* filledColor;
#property (nonatomic, strong) UIColor* unfilledColor;
#property (nonatomic, strong) UIColor* handleColor;
#property (nonatomic) EFHandleType handleType;
#property (nonatomic, strong) UIFont* labelFont;
#property (nonatomic, strong) UIColor* labelColor;
#property (nonatomic, assign) NSInteger labelDisplacement;
#property (nonatomic) BOOL snapToLabels;
-(void)setInnerMarkingLabels:(NSArray*)labels;
#end
You can only do 1 CGContextDrawPath with the implicit path, and use kCGPathFill to fill it.
Also, if you want the translucent background color to not interfere with the overlaying arc:
draw the background first
use an actual transparent color (alpha < 1.0)
Do this:
CGContextAddArc(ctx, self.frame.size.width/2,
self.frame.size.height/2,
self.radius, 0, M_PI *2, 0);
CGContextClosePath(ctx);
CGContextSetFillColorWithColor(ctx,
[UIColor colorWithRed:0
green:.5 // dark green
blue:0
alpha:.25] // translucent
.CGColor );
CGContextDrawPath(ctx, kCGPathFill);
...before you do that (redefine CGContextAddArc)
CGContextAddArc(ctx, self.frame.size.width/2,
self.frame.size.height/2,
self.radius, 0, M_PI *2, 0);
CGContextClosePath(ctx);
[_unfilledColor setStroke];
CGContextSetLineWidth(ctx, _lineWidth);
CGContextSetLineCap(ctx, kCGLineCapButt);
CGContextDrawPath(ctx, kCGPathStroke);
PS. Nice control. Make sources public when anti-aliased and ready!

Setting CPU Difficulty for Pong

Second time on here hoping that someone can guide me in the right direction.
I'm trying to implement a Pong game within an app for iOS devices (iPhone/iPod/iPad) and it works just fine, except for the fact that I can't beat the CPU, let alone score a point. Here's the implementation .m code:
#import "BTM_pongGame.h"
#import <QuartzCore/QuartzCore.h>
#define STEP_DURATION 0.05
#define RESET_BALL_ANIMATION_DURATION 0.5f
#define MARGIN_WHERE_BALL_IS_LEAVING 40
//#define CPU_SKILL 20
#interface BTM_pongGame ()
#end
#implementation BTM_pongGame
#synthesize cpuSkill, ballShape;
- (void)viewDidLoad
{
[super viewDidLoad];
cpuSkill = [BT_strings getJsonPropertyValue:self.screenData.jsonVars nameOfProperty:#"cpuSkill" defaultValue:#"20"];
ballShape = [BT_strings getJsonPropertyValue:self.screenData.jsonVars nameOfProperty:#"ballShape" defaultValue:#"1"];
if ([ballShape isEqual: #"1"]) {
self.ball.layer.cornerRadius = self.ball.frame.size.width / 2;
} else {
//self.ball.layer.cornerRadius = self.ball.frame.size.width / 2;
}
}
- (void) wordsPerLineInfoShowAlert {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Choose Game"
message:#"Multiplayer or Single Player? You decide! Choose below."
delegate:self
cancelButtonTitle:#"Single Player"
otherButtonTitles:#"Go Back",#"Multi Player", nil];
[alert show];
}
- (void)alertView: (UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"Single Player"]) {
[self resetPlayerScoreLabels];
[self updatePlayerScoreLabels];
self.mode = kGameSinglePlayer;
[self moveComputerPaddle];
//Disable panning of computer paddle
[self.playerOne.superview setGestureRecognizers:#[]];
[self resetBoardForNewRound];
} else if ([title isEqualToString:#"Multi Player"]) {
[self resetPlayerScoreLabels];
[self updatePlayerScoreLabels];
self.mode = kGameMultiPlayer;
[self resetBoardForNewRound];
} else if ( [title isEqualToString:#"Go Back"]){
[self navLeftTap];
}
}
// thanks to MrsVanBeveren for coding the reset score stuff
-(void)resetPlayerScoreLabels
{
playerOneScore = 0;
playerTwoScore = 0;
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self wordsPerLineInfoShowAlert];
[self startGameTimer];
}
-(void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[_gameTimer invalidate];
}
-(void)quitPressed:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void)startGameTimer
{
[_gameTimer invalidate];
_gameTimer = [NSTimer scheduledTimerWithTimeInterval:STEP_DURATION target:self selector:#selector(step:) userInfo:nil repeats:YES];
}
#pragma mark Game setup
/*
* Some maths to calculate a new starting direction
* By choosing mid left/right part of the unit circle
* we avoid getting straight up/down directions
*/
-(void)resetBallDirection
{
float randomUnity = arc4random_uniform(100)/100.0;
int horizontalDirection = (arc4random() % 2 ? 1 : -1);
float angle = M_PI_4 + randomUnity * M_PI_2;
float direction = horizontalDirection * angle;
dirX = sin(direction);
dirY = cos(direction);
}
-(void)resetBoardForNewRound
{
speed = self.view.frame.size.width / 50.0;
[self resetBallDirection];
[_gameTimer invalidate];
[UIView animateWithDuration:RESET_BALL_ANIMATION_DURATION animations:^{
self.ball.center = CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0);
} completion:^(BOOL finished){
[self startGameTimer];
}];
[self enlargeAnimation:self.ball];
}
-(void)updatePlayerScoreLabels
{
self.playerOneScoreLabel.text = [NSString stringWithFormat:#"%d",playerOneScore];
self.playerTwoScoreLabel.text = [NSString stringWithFormat:#"%d",playerTwoScore];
}
#pragma mark Paddle handling
- (IBAction)paddlePanned:(UIPanGestureRecognizer*)recognizer {
UIView *paddleWrapper = recognizer.view;
UIView *paddle = [[paddleWrapper subviews] lastObject];
switch([recognizer state]) {
case UIGestureRecognizerStateBegan: {
paddle.backgroundColor = UIColor.whiteColor;
}
break;
case UIGestureRecognizerStateChanged: {
CGPoint position = [recognizer locationInView:self.view];
CGFloat haldPaddleHeight = paddleWrapper.frame.size.height / 2.0;
CGPoint newCenter = paddleWrapper.center;
newCenter.y = position.y;
newCenter.y = MAX(haldPaddleHeight, newCenter.y);
newCenter.y = MIN(self.view.bounds.size.height - haldPaddleHeight, newCenter.y);
paddleWrapper.center = newCenter;
}
break;
case UIGestureRecognizerStateEnded: {
paddle.backgroundColor = UIColor.grayColor;
}
break;
default:
break;
}
}
#pragma mark Game loop
/*
* Game loop
* - Moves the ball and checks for obstacles
*/
- (void)step:(NSTimer*)timer
{
speed += 0.05;
[self checkForBallLevingSide];
CGPoint newCenter = self.ball.center;
CGFloat ballRadius = self.ball.frame.size.height / 2.0;
newCenter.x += dirX * speed;
newCenter.y += dirY * speed;
CGFloat upperEdge = ballRadius;
CGFloat bottomEdge = self.view.bounds.size.height - ballRadius;
// Bounce ball of top/bottom walls
if (newCenter.y <= upperEdge) {
dirY = ABS(dirY);
newCenter.y = upperEdge;
} else if (newCenter.y >= bottomEdge) {
dirY = -ABS(dirY);
newCenter.y = bottomEdge;
}
[UIView animateWithDuration:STEP_DURATION animations:^{
self.ball.center = newCenter;
}];
}
-(BOOL)didBallHitPaddle:(UIView *)paddle withinLimit:(CGFloat)limit
{
if (CGRectIntersectsRect(paddle.frame, self.ball.frame)){
[self deflectBallFromPaddle:paddle];
CGRect ballFrame = self.ball.frame;
ballFrame.origin.x = limit;
self.ball.frame = ballFrame;
return YES;
} else
return NO;
}
-(void)checkForBallLevingSide
{
float limitLeft = MARGIN_WHERE_BALL_IS_LEAVING;
float limitRight = self.view.bounds.size.width - self.ball.frame.size.width - MARGIN_WHERE_BALL_IS_LEAVING;
CGRect ballFrame = self.ball.frame;
CGFloat ballX = ballFrame.origin.x;
if (ballX < limitLeft) {
if (![self didBallHitPaddle:self.playerOne.superview withinLimit:limitLeft])
[self playerDidMiss:kPlayerOne];
}else if (ballX > limitRight) {
if (![self didBallHitPaddle:self.playerTwo.superview withinLimit:limitRight])
[self playerDidMiss:kPlayerTwo];
}
}
/*
* Calculates new dirX and dirY after the bounce.
* The longer from the paddle's middle the bigger the result angle gets. (Pong style)
*/
-(void)deflectBallFromPaddle:(UIView *)paddle
{
dirX *= -1;
CGFloat diff = self.ball.center.y - paddle.center.y;
float p = diff / paddle.frame.size.height * 2.0f;
dirY += p * 0.5;
}
-(void)playerDidMiss:(kPlayer)player
{
if (player == kPlayerOne) {
playerTwoScore++;
[self victoryShake:self.playerTwo];
[self enlargeAnimation:self.playerTwoScoreLabel];
}
else if (player == kPlayerTwo) {
playerOneScore++;
[self victoryShake:self.playerOne];
[self enlargeAnimation:self.playerOneScoreLabel];
}
[self resetBoardForNewRound];
[self updatePlayerScoreLabels];
}
-(void)moveComputerPaddle
{
UIView *paddle = self.playerOne.superview;
CGPoint cpuCenter = paddle.center;
CGFloat diff = self.ball.center.y - paddle.center.y;
CGFloat movement = MIN((int)cpuSkill, ABS(diff));
movement *= diff > 0 ? 1 : -1;
cpuCenter.y += movement;
[UIView animateWithDuration:0.1 animations:^{
self.playerOne.superview.center = cpuCenter;
} completion:^(BOOL b) {
[self moveComputerPaddle];
}];
}
#pragma mark Animation
-(void)victoryShake:(UIView *)view
{
CAKeyframeAnimation * anim = [ CAKeyframeAnimation animationWithKeyPath:#"transform" ] ;
anim.values = [ NSArray arrayWithObjects:
[ NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0.0f, -20.0f, 0.0f) ],
[ NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0.0f, 20.0f, 0.0f) ],
nil ] ;
anim.autoreverses = YES ;
anim.repeatCount = 4.0f ;
anim.duration = 0.07f ;
[view.layer addAnimation:anim forKey:nil];
}
-(void)enlargeAnimation:(UIView *)view
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"transform"];
animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.0, 1.0, 1.0)];
animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(3.0, 3.0, 1.0)];
[animation setDuration:RESET_BALL_ANIMATION_DURATION * 0.5];
animation.repeatCount = 1.0f ;
[animation setAutoreverses:YES];
[view.layer addAnimation:animation forKey:nil];
}
#end
I've simply tried changing the number where it states #define CPU_SkILL and again #"cpuSkill" defaultValue:#"20"];, but I've had no luck with changing the number in any direction.
Any ideas?
Thank you in advance.
The cpuSkill variable controls the movement of the CPU-controlled paddle:
CGFloat movement = MIN((int)cpuSkill, ABS(diff));
So maybe the cpuSkill is higher than intended in every situation. Try much lower values or make some random little adjustment to movement after the calculation.
Also, you should ABS() the cpuSkill to avoid perfect moving when negative moving.

How to display custom circular progress bar with Anti-Clockwise animation?

I'm working with Quiz related App. Here will display the custom circular progress bar based the percentage value from correct answers divider by total questions and multiply by 100.
And get the resulted value as percentage and then the resulted value divided by 100 for get the float values, because the progress animation value is "0.0 to 1.0"
Here I use the library "DACircularProgressView".
Now the progress working with clockwise animation. But I need anti clockwise animation.
If you anybody know kindly give the siggestion. I really don't know how to change rotation animation in "DACircularProgressView".
//
// DACircularProgressView.h
// DACircularProgress
//
// Created by Daniel Amitay on 2/6/12.
// Copyright (c) 2012 Daniel Amitay. All rights reserved.
//
#import <UIKit/UIKit.h>
#interface DACircularProgressView : UIView
#property(nonatomic, strong) UIColor *trackTintColor UI_APPEARANCE_SELECTOR;
#property(nonatomic, strong) UIColor *progressTintColor UI_APPEARANCE_SELECTOR;
#property(nonatomic) NSInteger roundedCorners UI_APPEARANCE_SELECTOR; // Can not use BOOL with UI_APPEARANCE_SELECTOR :-(
#property(nonatomic) CGFloat thicknessRatio UI_APPEARANCE_SELECTOR;
#property(nonatomic) CGFloat progress;
#property(nonatomic) CGFloat indeterminateDuration UI_APPEARANCE_SELECTOR;
#property(nonatomic) NSInteger indeterminate UI_APPEARANCE_SELECTOR; // Can not use BOOL with UI_APPEARANCE_SELECTOR :-(
- (void)setProgress:(CGFloat)progress animated:(BOOL)animated;
#end
//
// DACircularProgressView.m
// DACircularProgress
//
// Created by Daniel Amitay on 2/6/12.
// Copyright (c) 2012 Daniel Amitay. All rights reserved.
//
#import "DACircularProgressView.h"
#import <QuartzCore/QuartzCore.h>
#interface DACircularProgressLayer : CALayer
#property(nonatomic, strong) UIColor *trackTintColor;
#property(nonatomic, strong) UIColor *progressTintColor;
#property(nonatomic) NSInteger roundedCorners;
#property(nonatomic) CGFloat thicknessRatio;
#property(nonatomic) CGFloat progress;
#end
#implementation DACircularProgressLayer
#dynamic trackTintColor;
#dynamic progressTintColor;
#dynamic roundedCorners;
#dynamic thicknessRatio;
#dynamic progress;
+ (BOOL)needsDisplayForKey:(NSString *)key
{
return [key isEqualToString:#"progress"] ? YES : [super needsDisplayForKey:key];
}
- (void)drawInContext:(CGContextRef)context
{
CGRect rect = self.bounds;
CGPoint centerPoint = CGPointMake(rect.size.height / 2, rect.size.width / 2);
CGFloat radius = MIN(rect.size.height, rect.size.width) / 2;
CGFloat progress = MIN(self.progress, 1.f - FLT_EPSILON);
CGFloat radians = (progress * 2 * M_PI) - M_PI_2;
CGContextSetFillColorWithColor(context, self.trackTintColor.CGColor);
CGMutablePathRef trackPath = CGPathCreateMutable();
CGPathMoveToPoint(trackPath, NULL, centerPoint.x, centerPoint.y);
CGPathAddArc(trackPath, NULL, centerPoint.x, centerPoint.y, radius, 3 * M_PI_2, -M_PI_2, NO);
CGPathCloseSubpath(trackPath);
CGContextAddPath(context, trackPath);
CGContextFillPath(context);
CGPathRelease(trackPath);
if (progress > 0.f)
{
CGContextSetFillColorWithColor(context, self.progressTintColor.CGColor);
CGMutablePathRef progressPath = CGPathCreateMutable();
CGPathMoveToPoint(progressPath, NULL, centerPoint.x, centerPoint.y);
CGPathAddArc(progressPath, NULL, centerPoint.x, centerPoint.y, radius, 3 * M_PI_2, radians, NO);
CGPathCloseSubpath(progressPath);
CGContextAddPath(context, progressPath);
CGContextFillPath(context);
CGPathRelease(progressPath);
}
if (progress > 0.f && self.roundedCorners)
{
CGFloat pathWidth = radius * self.thicknessRatio;
CGFloat xOffset = radius * (1.f + ((1 - (self.thicknessRatio / 2.f)) * cosf(radians)));
CGFloat yOffset = radius * (1.f + ((1 - (self.thicknessRatio / 2.f)) * sinf(radians)));
CGPoint endPoint = CGPointMake(xOffset, yOffset);
CGContextAddEllipseInRect(context, CGRectMake(centerPoint.x - pathWidth / 2, 0, pathWidth, pathWidth));
CGContextFillPath(context);
CGContextAddEllipseInRect(context, CGRectMake(endPoint.x - pathWidth / 2, endPoint.y - pathWidth / 2, pathWidth, pathWidth));
CGContextFillPath(context);
}
CGContextSetBlendMode(context, kCGBlendModeClear);
CGFloat innerRadius = radius * (1.f - self.thicknessRatio);
CGPoint newCenterPoint = CGPointMake(centerPoint.x - innerRadius, centerPoint.y - innerRadius);
CGContextAddEllipseInRect(context, CGRectMake(newCenterPoint.x, newCenterPoint.y, innerRadius * 2, innerRadius * 2));
CGContextFillPath(context);
}
#end
#implementation DACircularProgressView
+ (void) initialize
{
if (self != [DACircularProgressView class])
return;
id appearance = [self appearance];
[appearance setTrackTintColor:[[UIColor whiteColor] colorWithAlphaComponent:0.3f]];
[appearance setProgressTintColor:[UIColor whiteColor]];
[appearance setThicknessRatio:0.3f];
[appearance setRoundedCorners:NO];
[appearance setIndeterminateDuration:2.0f];
[appearance setIndeterminate:NO];
}
+ (Class)layerClass
{
return [DACircularProgressLayer class];
}
- (DACircularProgressLayer *)circularProgressLayer
{
return (DACircularProgressLayer *)self.layer;
}
- (id)init
{
return [self initWithFrame:CGRectMake(0.0f, 0.0f, 40.0f, 40.0f)];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.backgroundColor = [UIColor clearColor];
}
return self;
}
- (void)didMoveToWindow
{
self.circularProgressLayer.contentsScale = [UIScreen mainScreen].scale;
}
#pragma mark - Progress
-(CGFloat)progress
{
return self.circularProgressLayer.progress;
}
- (void)setProgress:(CGFloat)progress
{
[self setProgress:progress animated:NO];
}
- (void)setProgress:(CGFloat)progress animated:(BOOL)animated
{
CGFloat pinnedProgress = MIN(MAX(progress, 0.f), 1.f);
if (animated)
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"progress"];
animation.duration = fabsf(self.progress - pinnedProgress); // Same duration as UIProgressView animation
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.fromValue = [NSNumber numberWithFloat:self.progress];
animation.toValue = [NSNumber numberWithFloat:pinnedProgress];
[self.circularProgressLayer addAnimation:animation forKey:#"progress"];
// [self.circularProgressLayer setFrame:CGRectMake(3, 3, 40, 40)];
}
else
{
[self.circularProgressLayer setNeedsDisplay];
}
self.circularProgressLayer.progress = pinnedProgress;
}
#pragma mark - UIAppearance methods
- (UIColor *)trackTintColor
{
return self.circularProgressLayer.trackTintColor;
}
- (void)setTrackTintColor:(UIColor *)trackTintColor
{
self.circularProgressLayer.trackTintColor = trackTintColor;
[self.circularProgressLayer setNeedsDisplay];
}
- (UIColor *)progressTintColor
{
return self.circularProgressLayer.progressTintColor;
}
- (void)setProgressTintColor:(UIColor *)progressTintColor
{
self.circularProgressLayer.progressTintColor = progressTintColor;
[self.circularProgressLayer setNeedsDisplay];
}
- (NSInteger)roundedCorners
{
return self.roundedCorners;
}
-(void)setRoundedCorners:(NSInteger)roundedCorners
{
self.circularProgressLayer.roundedCorners = roundedCorners;
[self.circularProgressLayer setNeedsDisplay];
}
-(CGFloat)thicknessRatio
{
return self.circularProgressLayer.thicknessRatio;
}
- (void)setThicknessRatio:(CGFloat)thicknessRatio
{
self.circularProgressLayer.thicknessRatio = MIN(MAX(thicknessRatio, 0.f), 1.f);
[self.circularProgressLayer setNeedsDisplay];
}
- (NSInteger)indeterminate
{
CAAnimation *spinAnimation = [self.layer animationForKey:#"indeterminateAnimation"];
return spinAnimation;
}
- (void)setIndeterminate:(NSInteger)indeterminate
{
if (indeterminate && !self.indeterminate)
{
CABasicAnimation *spinAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
spinAnimation.byValue = [NSNumber numberWithFloat:2.0f*M_PI];
spinAnimation.duration = self.indeterminateDuration;
spinAnimation.repeatCount = HUGE_VALF;
[self.layer addAnimation:spinAnimation forKey:#"indeterminateAnimation"];
}
else
{
[self.layer removeAnimationForKey:#"indeterminateAnimation"];
}
}
#end
In my own class,
self.largeProgressView = [[DACircularProgressView alloc] initWithFrame:CGRectMake(10.0f, 85.0f, 78.0f, 78.0f)];
self.largeProgressView.roundedCorners = NO;
self.largeProgressView.trackTintColor = THIK_GRAY_COLOR;
self.largeProgressView.progressTintColor = LIGHT_GREEN_COLOR;
self.largeProgressView.thicknessRatio = 0.2f;
[self.largeProgressView setBackgroundColor:[UIColor clearColor]];
[resultatsCategoryView addSubview:self.largeProgressView];
total = [TotalQuestionsCount floatValue];
current = [CorrectAnswersCount floatValue];
percentageCompleted = current / total * 100;
percentageCompleted = percentageCompleted / 100;
//NSLog(#"percentageCompleted = %f",percentageCompleted);
for (DACircularProgressView *progressView in [NSArray arrayWithObjects:self.largeProgressView, nil])
{
CGFloat progress = percentageCompleted;
//NSLog(#"progress = %f",progress);
[progressView setProgress:progress animated:YES];
if (progressView.progress >= 1.0f && [self.timer isValid])
{
[progressView setProgress:0.f animated:YES];
}
}
use below code i hav changed a bit replace above .m file by below .m file
hope this helps u
#import "DACircularProgressView.h"
#import <QuartzCore/QuartzCore.h>
#interface DACircularProgressLayer : CALayer
#property(nonatomic, strong) UIColor *trackTintColor;
#property(nonatomic, strong) UIColor *progressTintColor;
#property(nonatomic) NSInteger roundedCorners;
#property(nonatomic) CGFloat thicknessRatio;
#property(nonatomic) CGFloat progress;
#end
#implementation DACircularProgressLayer
#dynamic trackTintColor;
#dynamic progressTintColor;
#dynamic roundedCorners;
#dynamic thicknessRatio;
#dynamic progress;
+ (BOOL)needsDisplayForKey:(NSString *)key
{
return [key isEqualToString:#"progress"] ? YES : [super needsDisplayForKey:key];
}
- (void)drawInContext:(CGContextRef)context
{
CGRect rect = self.bounds;
CGPoint centerPoint = CGPointMake(rect.size.height / 2.0f, rect.size.width / 2.0f);
CGFloat radius = MIN(rect.size.height, rect.size.width) / 2.0f;
CGFloat progress = MIN(self.progress, 1.0f - FLT_EPSILON);
CGFloat radians = (progress * 2.0f * -M_PI) - M_PI_2;
CGContextSetFillColorWithColor(context, self.trackTintColor.CGColor);
CGMutablePathRef trackPath = CGPathCreateMutable();
CGPathMoveToPoint(trackPath, NULL, centerPoint.x, centerPoint.y);
CGPathAddArc(trackPath, NULL, centerPoint.x, centerPoint.y, radius, 3.0f * -M_PI_2, M_PI_2, NO);
CGPathCloseSubpath(trackPath);
CGContextAddPath(context, trackPath);
CGContextFillPath(context);
CGPathRelease(trackPath);
if (progress > 0.0f)
{
CGContextSetFillColorWithColor(context, self.progressTintColor.CGColor);
CGMutablePathRef progressPath = CGPathCreateMutable();
CGPathMoveToPoint(progressPath, NULL, centerPoint.x, centerPoint.y);
CGPathAddArc(progressPath, NULL, centerPoint.x, centerPoint.y, radius, 3.0f * M_PI_2, radians, NO);
CGPathCloseSubpath(progressPath);
CGContextAddPath(context, progressPath);
CGContextFillPath(context);
CGPathRelease(progressPath);
}
if (progress > 0.0f && self.roundedCorners)
{
CGFloat pathWidth = radius * self.thicknessRatio;
CGFloat xOffset = radius * (1.0f + ((1.0f - (self.thicknessRatio / 2.0f)) * cosf(radians)));
CGFloat yOffset = radius * (1.0f + ((1.0f - (self.thicknessRatio / 2.0f)) * sinf(radians)));
CGPoint endPoint = CGPointMake(xOffset, yOffset);
CGContextAddEllipseInRect(context, CGRectMake(centerPoint.x - pathWidth / 2.0f, 0.0f, pathWidth, pathWidth));
CGContextFillPath(context);
CGContextAddEllipseInRect(context, CGRectMake(endPoint.x - pathWidth / 2.0f, endPoint.y - pathWidth / 2.0f, pathWidth, pathWidth));
CGContextFillPath(context);
}
CGContextSetBlendMode(context, kCGBlendModeClear);
CGFloat innerRadius = radius * (1.0f - self.thicknessRatio);
CGPoint newCenterPoint = CGPointMake(centerPoint.x - innerRadius, centerPoint.y - innerRadius);
CGContextAddEllipseInRect(context, CGRectMake(newCenterPoint.x, newCenterPoint.y, innerRadius * 2.0f, innerRadius * 2.0f));
CGContextFillPath(context);
}
#end
#interface DACircularProgressView ()
#end
#implementation DACircularProgressView
+ (void) initialize
{
if (self != [DACircularProgressView class])
return;
id appearance = [self appearance];
[appearance setTrackTintColor:[[UIColor whiteColor] colorWithAlphaComponent:0.3f]];
[appearance setProgressTintColor:[UIColor whiteColor]];
[appearance setBackgroundColor:[UIColor clearColor]];
[appearance setThicknessRatio:0.3f];
[appearance setRoundedCorners:NO];
[appearance setIndeterminateDuration:5.0f];
[appearance setIndeterminate:NO];
}
+ (Class)layerClass
{
return [DACircularProgressLayer class];
}
- (DACircularProgressLayer *)circularProgressLayer
{
return (DACircularProgressLayer *)self.layer;
}
- (id)init
{
return [super initWithFrame:CGRectMake(0.0f, 0.0f, 40.0f, 40.0f)];
}
- (void)didMoveToWindow
{
CGFloat windowContentsScale = self.window.screen.scale;
self.circularProgressLayer.contentsScale = windowContentsScale;
}
#pragma mark - Progress
- (CGFloat)progress
{
return self.circularProgressLayer.progress;
}
- (void)setProgress:(CGFloat)progress
{
[self setProgress:progress animated:NO];
}
- (void)setProgress:(CGFloat)progress animated:(BOOL)animated
{
[self.layer removeAnimationForKey:#"indeterminateAnimation"];
[self.circularProgressLayer removeAnimationForKey:#"progress"];
CGFloat pinnedProgress = MIN(MAX(progress, 0.0f), 1.0f);
if (animated)
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"progress"];
// animation.duration = fabsf(self.progress - pinnedProgress); // Same duration as UIProgressView animation
animation.duration = 10.0f;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
// animation.fromValue = [NSNumber numberWithFloat:self.progress];
// animation.toValue = [NSNumber numberWithFloat:pinnedProgress];
animation.fromValue = [NSNumber numberWithFloat:pinnedProgress];
animation.toValue = [NSNumber numberWithFloat:self.progress];
[self.circularProgressLayer addAnimation:animation forKey:#"progress"];
}
else
{
[self.circularProgressLayer setNeedsDisplay];
}
self.circularProgressLayer.progress = pinnedProgress;
}
#pragma mark - UIAppearance methods
- (UIColor *)trackTintColor
{
return self.circularProgressLayer.trackTintColor;
}
- (void)setTrackTintColor:(UIColor *)trackTintColor
{
self.circularProgressLayer.trackTintColor = trackTintColor;
[self.circularProgressLayer setNeedsDisplay];
}
- (UIColor *)progressTintColor
{
return self.circularProgressLayer.progressTintColor;
}
- (void)setProgressTintColor:(UIColor *)progressTintColor
{
self.circularProgressLayer.progressTintColor = progressTintColor;
[self.circularProgressLayer setNeedsDisplay];
}
- (NSInteger)roundedCorners
{
return self.roundedCorners;
}
- (void)setRoundedCorners:(NSInteger)roundedCorners
{
self.circularProgressLayer.roundedCorners = roundedCorners;
[self.circularProgressLayer setNeedsDisplay];
}
- (CGFloat)thicknessRatio
{
return self.circularProgressLayer.thicknessRatio;
}
- (void)setThicknessRatio:(CGFloat)thicknessRatio
{
self.circularProgressLayer.thicknessRatio = MIN(MAX(thicknessRatio, 0.f), 1.f);
[self.circularProgressLayer setNeedsDisplay];
}
- (NSInteger)indeterminate
{
CAAnimation *spinAnimation = [self.layer animationForKey:#"indeterminateAnimation"];
return (spinAnimation == nil ? 0 : 1);
}
- (void)setIndeterminate:(NSInteger)indeterminate
{
if (indeterminate && !self.indeterminate)
{
CABasicAnimation *spinAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
spinAnimation.byValue = [NSNumber numberWithFloat:indeterminate > 0 ? -2.0f*M_PI : 2.0f*M_PI];
spinAnimation.duration = self.indeterminateDuration;
spinAnimation.repeatCount = HUGE_VALF;
[self.layer addAnimation:spinAnimation forKey:#"indeterminateAnimation"];
}
else
{
[self.layer removeAnimationForKey:#"indeterminateAnimation"];
}
}
#end
i modified the example project, the output of the project is somthing like below
i dont think the above result is cock-wise rotation, the video is truncated at the end it will rotating in anti clock wise direction.. perfectly please check it, once again i re-posted the code. check it
open source u can download the project hear but animating clock wise modified to animate anti-clock wise in DACircularProgressView.m
I don't know if someone still have problems with this, but here is solution that moves animation from Empty to Full in both directions.
Code:
class CirclingVC: UIViewController {
let trackLayer = CAShapeLayer()
let shapeLayer = CAShapeLayer()
override func viewDidLoad() {
super.viewDidLoad()
// Create Path on which Shape will fill
let trackPath = UIBezierPath(arcCenter: view.center,
radius: 100,
startAngle: -.pi/2,
endAngle: 2 * .pi,
clockwise: true)
trackLayer.path = trackPath.cgPath
trackLayer.strokeColor = UIColor.lightGray.cgColor
trackLayer.lineWidth = 10
trackLayer.fillColor = UIColor.clear.cgColor
view.layer.addSublayer(trackLayer)
}
#IBAction func leftCirclingPressed(_ sender: UIButton) {
animateCircling(clockWise: false)
}
#IBAction func rightCirclingPressed(_ sender: UIButton) {
animateCircling(clockWise: true)
}
private func animateCircling(clockWise: Bool) {
// Create Shape that fills the circle
let shapePath = UIBezierPath(arcCenter: view.center,
radius: 100,
startAngle: clockWise ? (-.pi/2) : (3.5 * .pi),
endAngle: clockWise ? (2 * .pi) : (.pi),
clockwise: clockWise)
shapeLayer.path = shapePath.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.lineWidth = 10
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineCap = CAShapeLayerLineCap.round
shapeLayer.strokeEnd = 0
view.layer.addSublayer(shapeLayer)
// Animation
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.toValue = 1
animation.duration = 2
animation.timingFunction = CAMediaTimingFunction(name: .linear)
animation.fillMode = .forwards
animation.isRemovedOnCompletion = true
shapeLayer.add(animation, forKey: "circling")
}
}
Results:
GIF

Adding an indicator at the current touch position?

I am developing a color selector and using a circular gradient and a panGesture to pick the color.
Can I add a crosshair or something else to the current touch position?
I need the crosshair to be visible but it should not interfere with the gradient.
This is the code that adds the gradient :
- (void)viewDidLoad
{
[super viewDidLoad];
CGSize size = CGSizeMake(self.view.bounds.size.width, ((self.view.bounds.size.height)/2));
UIGraphicsBeginImageContextWithOptions(CGSizeMake(size.width, size.height), YES, 0.0);
[[UIColor whiteColor] setFill];
UIRectFill(CGRectMake(0, 0,size.width,size.height));
int sectors = 180;
float radius = MIN(size.width, size.height)/2;
float angle = 2 * M_PI/sectors;
UIBezierPath *bezierPath;
for ( int i = 0; i < sectors; i++)
{
CGPoint center = CGPointMake(((size.width)/2), ((size.height)/2));
bezierPath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:i * angle endAngle:(i + 1) * angle clockwise:YES];
[bezierPath addLineToPoint:center];
[bezierPath closePath];
UIColor *color = [UIColor colorWithHue:((float)i)/sectors saturation:1. brightness:1. alpha:1];
[color setFill];
[color setStroke];
[bezierPath fill];
[bezierPath stroke];
}
img = UIGraphicsGetImageFromCurrentImageContext();
gradientView = [[UIImageView alloc]initWithImage:img];;
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(handlePan:)];
colorView = [[UIView alloc] init];
colorView.frame = CGRectMake(0, 00, 50, 50);
rText.textAlignment = NSTextAlignmentCenter;
gText.textAlignment = NSTextAlignmentCenter;
bText.textAlignment = NSTextAlignmentCenter;
saturationText.textAlignment = NSTextAlignmentCenter;
alphaText.textAlignment = NSTextAlignmentCenter;
brightnessText.textAlignment = NSTextAlignmentCenter;
rText.keyboardType = UIKeyboardTypeNumberPad;
gText.keyboardType = UIKeyboardTypeNumberPad;
bText.keyboardType = UIKeyboardTypeNumberPad;
saturationText.keyboardType = UIKeyboardTypeNumberPad;
alphaText.keyboardType = UIKeyboardTypeNumberPad;
brightnessText.keyboardType = UIKeyboardTypeNumberPad;
gradientView.frame = CGRectMake(0,0,size.width,size.height);
self.view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:gradientView];
gradientView.userInteractionEnabled = YES;
[gradientView addGestureRecognizer: panGesture];
[self.view addSubview:colorView];
}
The handlePan method :
- (IBAction)handlePan:(UIPanGestureRecognizer *)sender
{
if (sender.numberOfTouches)
{
CGSize size = CGSizeMake(self.view.bounds.size.width, (self.view.bounds.size.height)/2);
float radius = MIN(size.width, size.height)/2;
[alphaSlider addTarget:self action:#selector(changeOpacity:) forControlEvents:UIControlEventValueChanged];
[hellSlider addTarget:self action:#selector(changeBrightness:) forControlEvents:UIControlEventValueChanged];
[saturationSlider addTarget:self action:#selector(saturate:) forControlEvents:UIControlEventValueChanged];
[rSlider addTarget:self action:#selector(redSlider:) forControlEvents:UIControlEventValueChanged];
[gSlider addTarget:self action:#selector(greenSlider:) forControlEvents:UIControlEventValueChanged];
[bSlider addTarget:self action:#selector(blueSlider:) forControlEvents:UIControlEventValueChanged];
CGPoint lastPoint = [sender locationOfTouch: sender.numberOfTouches - 1 inView: gradientView];
CGPoint center = CGPointMake((size.width/2), (size.height /2));
CGPoint delta = CGPointMake(lastPoint.x - center.x, lastPoint.y - center.y);
CGFloat angle = (delta.y == 0 ? delta.x >= 0 ? 0 : M_PI : atan2(delta.y, delta.x));
angle = fmod(angle, M_PI * 2.0);
angle += angle >= 0 ? 0 : M_PI * 2.0;
if((lastPoint.x - center.x) + (lastPoint.y - center.y)/2 < radius)
{
UIColor *color = [UIColor colorWithHue: angle / (M_PI * 2.0) saturation:saturationSlider.value brightness:hellSlider.value alpha:alphaSlider.value];
if ([color getRed: &r green: &g blue:&b alpha: &a])
{
NSLog(#"Color value - R : %g G : %g : B %g", r*255, g*255, b*255);
}
float red = r;
float green = g;
float blue = b;
rText.text = [NSString stringWithFormat:#"%.0f",rSlider.value];
gText.text = [NSString stringWithFormat:#"%.0f",green*255];
bText.text = [NSString stringWithFormat:#"%.0f",blue*255];
colorView.backgroundColor = [UIColor colorWithRed:(rText.text.floatValue/255) green:(gText.text.floatValue/255) blue:(bText.text.floatValue/255) alpha:alphaSlider.value];
rSlider.value = red*255;
gSlider.value = green*255;
bSlider.value = blue*255;
alphaText.text = [NSString stringWithFormat:#"%.2f",alphaSlider.value];
brightnessText.text = [NSString stringWithFormat:#"%.2f",hellSlider.value];
saturationText.text = [NSString stringWithFormat:#"%.2f",saturationSlider.value];
}
}
}
Question is solved, thank you.
Well, you definitely 'can' add a crosshair at your current touch position.
I can tell you one of the many possible ways of doing this.
Implement the
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer
action handler.
Load an image of a crosshair into an UIImage (say overlayImage):
overlayImage =[UIImage imageNamed:#"crosshair.png"];
and set it into an UIView (say overlayView).
[This can be done in your viewDidLoad method itself.]
Now, add the following code to your handlePan method:
CGPoint translation = [recognizer translationInView:colorView]; //whichever view you want.
_overlayView.center = CGPointMake(_overlayView.center.x + translation.x,
_overlayView.center.y + translation.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:colorView];
if (recognizer.state == UIGestureRecognizerStateEnded) {
CGPoint velocity = [recognizer velocityInView:colorView];
CGFloat magnitude = sqrtf((velocity.x * velocity.x) + (velocity.y * velocity.y));
CGFloat slideMult = magnitude / 800;
// NSLog(#"magnitude: %f, slideMult: %f", magnitude, slideMult);
float slideFactor = 0.1 * slideMult; // Increase for more of a slide
CGPoint finalPoint = CGPointMake(_overlayView.center.x + (velocity.x * slideFactor),
_overlayView.center.y + (velocity.y * slideFactor));
finalPoint.x = MIN(MAX(finalPoint.x, 0), colorView.bounds.size.width);
finalPoint.y = MIN(MAX(finalPoint.y, 0), colorView.bounds.size.height);
[UIView animateWithDuration:slideFactor*2 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
_overlayView.center = finalPoint;
} completion:nil];
That should do the trick.
You may also try doing this by implementing the touchesBegan and touchesMoved method.

Limiting a pan gesture recognizer to a specific circle

I am using a circular gradient, drawn in an image view, as a color wheel for my color picker. I want to limit the pan gesture recognizer's shouldReceiveTouch: to the gradient's position so that the rest of the view is not affected by the pan gesture recognizer.
How do I do specify circular bounds matching the gradient's radius?
EDIT: I have tried the following code without effect :
if (sender.numberOfTouches)
{
CGSize size = CGSizeMake(self.view.bounds.size.width, self.view.bounds.size.height/3);
float radius = MIN(size.width, size.height)/2;
[alphaSlider addTarget:self action:#selector(changeOpacity:) forControlEvents:UIControlEventValueChanged];
[hellSlider addTarget:self action:#selector(changeBrightness:) forControlEvents:UIControlEventValueChanged];
[saturationSlider addTarget:self action:#selector(saturate:) forControlEvents:UIControlEventValueChanged];
[rSlider addTarget:self action:#selector(redSlider:) forControlEvents:UIControlEventValueChanged];
[gSlider addTarget:self action:#selector(greenSlider:) forControlEvents:UIControlEventValueChanged];
[bSlider addTarget:self action:#selector(blueSlider:) forControlEvents:UIControlEventValueChanged];
CGPoint lastPoint = [sender locationOfTouch: sender.numberOfTouches - 1 inView: gradientView];
CGPoint center = CGPointMake((size.width/2), (size.height /2));
CGPoint delta = CGPointMake(lastPoint.x - center.x, lastPoint.y - center.y);
CGFloat angle = (delta.y == 0 ? delta.x >= 0 ? 0 : M_PI : atan2(delta.y, delta.x));
angle = fmod(angle, M_PI * 2.0);
angle += angle >= 0 ? 0 : M_PI * 2.0;
if((lastPoint.x - center.x) + (lastPoint.y - center.y)/2 < radius)
{
UIColor *color = [UIColor colorWithHue: angle / (M_PI * 2.0) saturation:saturationSlider.value brightness:hellSlider.value alpha:alphaSlider.value];
if ([color getRed: &r green: &g blue:&b alpha: &a])
{
NSLog(#"Color value - R : %g G : %g : B %g", r*255, g*255, b*255);
}
float red = r;
float green = g;
float blue = b;
rText.text = [NSString stringWithFormat:#"%.0f",rSlider.value];
gText.text = [NSString stringWithFormat:#"%.0f",green*255];
bText.text = [NSString stringWithFormat:#"%.0f",blue*255];
colorView.backgroundColor = [UIColor colorWithRed:(rText.text.floatValue/255) green:(gText.text.floatValue/255) blue:(bText.text.floatValue/255) alpha:alphaSlider.value];
rSlider.value = red*255;
gSlider.value = green*255;
bSlider.value = blue*255;
alphaText.text = [NSString stringWithFormat:#"%.2f",alphaSlider.value];
brightnessText.text = [NSString stringWithFormat:#"%.2f",hellSlider.value];
saturationText.text = [NSString stringWithFormat:#"%.2f",saturationSlider.value];
}
}
}
It seems to work on the right side of the gradient and quits receiving color updates, left and lower side are still updating. I need to limit the panGesture appropriately in order for it to stop interfering with my UISliders.
Any ideas?
You just need to hit-test the touch event to see if it's within the circle's area. To do this you'll need to know the center point of your circle, and its radius.
A general equation for doing this is covered on this existing question:
Equation for testing if a point is inside a circle

Resources