How to draw arc through 3 points - ios

How to draw an arc as shown in the image as suppose i have a UIView as the middle point of the line which is movable.
i had store line's start point in CGPoint name lineStartPoint and end point as lineEndPoint.
the moving object can be accessed through CGPoint named movingPoint.
Thanks in advance for help.

Here is an example how to calculate the required parameters for the bezierPathWithArcCenter:radius:startAngle:endAngle:clockwise: method.
- (void)viewDidLoad {
[super viewDidLoad];
// for example
CGPoint lineStartPoint = {100,100};
CGPoint lineEndPoint = {100,200};
CGPoint movingPoint = {30,150};
CGFloat eps = 1e-5;
CGVector v1 = {movingPoint.x-lineEndPoint.x, movingPoint.y-lineEndPoint.y};
CGFloat dist1 = sqrt(v1.dx*v1.dx + v1.dy*v1.dy);
v1.dx = v1.dx/dist1;
v1.dy = v1.dy/dist1;
CGVector v2 = {movingPoint.x-lineStartPoint.x, movingPoint.y-lineStartPoint.y};
CGFloat dist2 = sqrt(v2.dx*v2.dx + v2.dy*v2.dy);
v2.dx = v2.dx/dist2;
v2.dy = v2.dy/dist2;
CGFloat det = v1.dx*v2.dy - v1.dy*v2.dx;
if (fabs(det) < eps) {
// the three points are collinear
// TODO: draw a line from lineStartPoint to lineEndPoint
return;
}
CGPoint mid1 = {(movingPoint.x+lineEndPoint.x)/2, (movingPoint.y+lineEndPoint.y)/2};
CGPoint mid2 = {(movingPoint.x+lineStartPoint.x)/2, (movingPoint.y+lineStartPoint.y)/2};
CGFloat b1 = v1.dx*mid1.x + v1.dy*mid1.y;
CGFloat b2 = v2.dx*mid2.x + v2.dy*mid2.y;
CGFloat centerX = v2.dy/det*b1 - v1.dy/det*b2;
CGFloat centerY = -v2.dx/det*b1 + v1.dx/det*b2;
CGPoint center = {centerX, centerY};
CGFloat radius = sqrtf((movingPoint.x-center.x)*(movingPoint.x-center.x) + (movingPoint.y-center.y)*(movingPoint.y-center.y));
CGFloat startAngle = atan2f(lineStartPoint.y-center.y, lineStartPoint.x-center.x);
CGFloat movingAngle = atan2f(movingPoint.y-center.y, movingPoint.x-center.x);
CGFloat endAngle = atan2f(lineEndPoint.y-center.y, lineEndPoint.x-center.x);
BOOL isClockwise;
if ((endAngle>startAngle && startAngle<movingAngle && movingAngle<endAngle) ||
(endAngle<startAngle && !(endAngle<movingAngle && movingAngle<startAngle))) {
isClockwise = YES;
} else {
isClockwise = NO;
}
//Show results
CAShapeLayer* startPointLayer = [[CAShapeLayer alloc] init];
startPointLayer.path = [UIBezierPath bezierPathWithArcCenter:lineStartPoint radius:2 startAngle:0 endAngle:2*M_PI clockwise:YES].CGPath;
[self.view.layer addSublayer:startPointLayer];
CAShapeLayer* endPointLayer = [[CAShapeLayer alloc] init];
endPointLayer.path = [UIBezierPath bezierPathWithArcCenter:lineEndPoint radius:2 startAngle:0 endAngle:2*M_PI clockwise:YES].CGPath;
[self.view.layer addSublayer:endPointLayer];
CAShapeLayer* movingPointLayer = [[CAShapeLayer alloc] init];
movingPointLayer.path = [UIBezierPath bezierPathWithArcCenter:movingPoint radius:2 startAngle:0 endAngle:2*M_PI clockwise:YES].CGPath;
[self.view.layer addSublayer:movingPointLayer];
CAShapeLayer* arcLayer = [[CAShapeLayer alloc] init];
[arcLayer setFillColor:[UIColor clearColor].CGColor];
[arcLayer setStrokeColor:[UIColor blueColor].CGColor];
arcLayer.path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:isClockwise].CGPath;
[self.view.layer addSublayer:arcLayer];
}

Related

How can we avoid the extra bezier curve line?

Can we avoid the extra bezier curve line after the last point in Bezier graph? Is there any property for that?
We need just end-end curve graph, no extra curve. Please share the solutions/suggestion. Update: Please check the code below,
- (void)drawRect:(CGRect)rect {
UIBezierPath *line = [UIBezierPath bezierPath];
NSValue *value = points[0];
CGPoint p1 = [value CGPointValue];
[line moveToPoint:p1];if (points.count == 2) {
value = points[1];
CGPoint p2 = [value CGPointValue];
[line addLineToPoint:p2];
return line;
}
for (NSUInteger i = 1; i < points.count; i++) {
value = points[i];
CGPoint p2 = [value CGPointValue];
CGPoint midPoint = midPointForPoints(p1, p2);
[line addQuadCurveToPoint:midPoint controlPoint:controlPointForPoints(midPoint, p1)];
[line addQuadCurveToPoint:p2 controlPoint:controlPointForPoints(midPoint, p2)];
p1 = p2;
}
CAShapeLayer *pathLayer = [CAShapeLayer layer];
pathLayer.frame = self.bounds;
pathLayer.path = line.CGPath;
pathLayer.strokeColor = self.color.CGColor;
pathLayer.fillColor = nil;
pathLayer.opacity = self.lineAlpha;
pathLayer.lineWidth = self.lineWidth;
pathLayer.lineJoin = kCALineJoinBevel;
pathLayer.lineCap = kCALineCapRound;
[self.layer addSublayer:pathLayer];}
static CGPoint midPointForPoints(CGPoint p1, CGPoint p2) {
return CGPointMake((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);}
Thanks!

How to resize all subviews/sublayers when superview is resized?

This question relates to the project: https://github.com/FindForm/FFAngularPointilism
Currently, all the behavior is dictated by the dimensions of the original UIImage. Despite resizing, shrinking, and rotating, the drawing of the "double triangle squares" remains the same. The triangular blur is unchanged regardless of the bounds of the UIImageView to which it is applied.
In my UIImageView subclass, I initialize with a UIImage and call the following in the designated initializer:
- (void)loadMatrix
{
num = 12;
CGFloat width = self.bounds.size.width;
CGFloat height = self.bounds.size.height;
CGFloat theBiggestSideLength = MAX(width, height);
_array = [NSMutableArray array];
_array2 = [NSMutableArray array];
for(int i = 0; i < theBiggestSideLength; i += num)
{
[self.array addObject:[NSMutableArray array]];
[self.array2 addObject:[NSMutableArray array]];
}
for(int i = 0; i < self.array.count; i++)
{
for(int j = 0; j < self.array.count; j++)
{
[self.array[i] addObject:[self getRGBAsFromImage:self.image
atX:j * 2 * num
andY:i * 2 * num]];
int xIndex = ((j * 2 * num) - (num / 2.0));
int yIndex = ((i * 2 * num) + (num / 2.0));
xIndex %= (int)width * 2;
yIndex %= (int)width * 2;
NSLog(#"%d", xIndex);
[self.array2[i] addObject:[self getRGBAsFromImage:self.image
atX:xIndex
andY:yIndex]];
}
}
_imageGrayscaleView = [[UIImageView alloc] initWithImage:[self convertToGreyscale:self.image]];
_finalbwimage = self.imageGrayscaleView.image;
_imageGrayscaleView.frame = self.bounds;
[self insertSubview:_imageGrayscaleView atIndex:0];
_viewMask = [[UIView alloc] initWithFrame:self.frame];
_viewMask.center = CGPointMake(_viewMask.center.x,
_viewMask.center.y + _viewMask.frame.size.height / 2.0f);
_shapeLayer = [[CAShapeLayer alloc] init];
CGRect maskRect = CGRectZero;
CGPathRef path = CGPathCreateWithRect(maskRect, NULL);
_shapeLayer.path = path;
CGPathRelease(path);
self.imageGrayscaleView.layer.mask = _shapeLayer;
}
To draw these tiles, I add a timer and call the following method each interval. Num and row are updating as expected.:
- (void)drawTile
{
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(pixel * num, row * num, num, num)];
view.backgroundColor = [self.array[row] objectAtIndex:pixel];
[self addSubview:view];
[self.ksubviews addObject:view];
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(view.frame.origin.x , view.frame.origin.y + (view.frame.size.height))];
[path addLineToPoint:CGPointMake(view.frame.origin.x , view.frame.origin.y)];
[path addLineToPoint:CGPointMake(view.frame.origin.x + view.frame.size.width, view.frame.origin.y + view.frame.size.height)];
[path closePath];
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = path.CGPath;
shapeLayer.strokeColor = [[UIColor blackColor] CGColor];
shapeLayer.fillColor = [((UIColor *)[self.array2[row] objectAtIndex:pixel]) CGColor];
shapeLayer.lineWidth = 0;
[self.layer addSublayer:shapeLayer];
[self.ksublayers addObject:shapeLayer];
}
The following is the correct behavior:
When this image is resized via the nib, or its bounds set programmatically, the animatable triangular squares are unchanged.
You make all the subviews changes on the method Draw Rect inside your class, and every time you make a change, to the superview, you call the method setNeedDisplay.
I didn't understand you question very well, but i hope this helps!

iOS drag shape pinned to center

Taking a next step from this post Drag UIView around Shape Comprised of CGMutablePaths , I am trying to add a line which is on one end pinned to the center of the pathLayer_ and the other end gets dragged along with the circular object (circleLayer in handleView_) on the path (figure-8).
The line has its own layer andview and path initiated in (void)initHandleView.
First: I am not able to get the line to go through the center of the pathLayer_:
- (void)viewDidLoad {
[super viewDidLoad];
[self initPathLayer];
[self initHandleView];
[self initHandlePanGestureRecognizer];
}
- (void)initPathLayer {
pathLayer_ = [CAShapeLayer layer];
pathLayer_.lineWidth = 1;
pathLayer_.fillColor = nil;
pathLayer_.strokeColor = [UIColor lightGrayColor].CGColor;
pathLayer_.lineCap = kCALineCapButt;
pathLayer_.lineJoin = kCALineJoinRound;
pathLayer_.frame = self.view.bounds;
pathLayerCenter_ = CGPointMake(CGRectGetMidX(pathLayer_.frame), CGRectGetMidY(pathLayer_.frame));
[self.view.layer addSublayer:pathLayer_];
}
- (void)initHandleView {
handlePathPointIndex_ = 0;
CGRect rect = CGRectMake(0, 0, 30, 30);
CAShapeLayer *circleLayer = [CAShapeLayer layer];
circleLayer.fillColor = [UIColor redColor].CGColor;
circleLayer.strokeColor = [UIColor redColor].CGColor;
circleLayer.lineWidth = 2;
UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(rect, circleLayer.lineWidth, circleLayer.lineWidth)];
circleLayer.frame = rect;
circleLayer.path = circlePath.CGPath;
handleView_ = [[UIView alloc] initWithFrame:rect];
CGRect lineRect = CGRectMake(0,0,CGRectGetMaxX([[UIScreen mainScreen] bounds]), CGRectGetMaxY([[UIScreen mainScreen] bounds]));
CGPoint center = CGPointMake(CGRectGetMidX(pathLayer_.frame), CGRectGetMidY(pathLayer_.frame));
lineView_ = [[UIView alloc] initWithFrame:lineRect];
CAShapeLayer *lineLayer = [CAShapeLayer layer];
lineLayer.fillColor = [UIColor blueColor].CGColor;
lineLayer.strokeColor = [UIColor blueColor].CGColor;
lineLayer.lineWidth = 2;
lineLayer.frame = self.view.bounds;
UIBezierPath *linePath = [UIBezierPath bezierPath];
CGPoint circlePoint = CGPointMake(handleView_.center.x, handleView_.center.y);
[linePath moveToPoint:center]; //Center
[linePath addLineToPoint:circlePoint];
[linePath moveToPoint:circlePoint]; //Handle
lineLayer.path = linePath.CGPath;
[handleView_.layer insertSublayer:circleLayer above:handleView_.layer];
[handleView_.layer insertSublayer:lineLayer above:handleView_.layer];
// handleView_.layer.masksToBounds = YES;
float direction = DEGREES_TO_RADIANS(10);
CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(direction);
[handleView_ setTransform:rotationTransform];
[self.view addSubview:lineView_];
[self.view addSubview:handleView_];
}
And second: I am not sure I do the right thing with the PanGesturerecognizer:
- (void)handleWasPanned:(UIPanGestureRecognizer *)recognizer {
switch (recognizer.state) {
case UIGestureRecognizerStateBegan: {
desiredHandleCenter_ = handleView_.center;
break;
}
case UIGestureRecognizerStateChanged:
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled: {
CGPoint translation = [recognizer translationInView:self.view];
desiredHandleCenter_.x += translation.x;
desiredHandleCenter_.y += translation.y;
[self moveHandleTowardPointAndRotateLine:desiredHandleCenter_];
break;
}
default:
break;
}
[recognizer setTranslation:CGPointZero inView:self.view];
}
- (void)moveHandleTowardPointAndRotateLine:(CGPoint)point {
CGFloat earlierDistance = [self distanceToPoint:point ifHandleMovesByOffset:-1];
CGFloat currentDistance = [self distanceToPoint:point ifHandleMovesByOffset:0];
CGFloat laterDistance = [self distanceToPoint:point ifHandleMovesByOffset:1];
if (currentDistance <= earlierDistance && currentDistance <= laterDistance)
return;
NSInteger step;
CGFloat distance;
if (earlierDistance < laterDistance) {
step = -1;
distance = earlierDistance;
} else {
step = 1;
distance = laterDistance;
}
NSInteger offset = step;
while (true) {
NSInteger nextOffset = offset + step;
CGFloat nextDistance = [self distanceToPoint:point ifHandleMovesByOffset:nextOffset];
if (nextDistance >= distance)
break;
distance = nextDistance;
offset = nextOffset;
}
handlePathPointIndex_ += offset;
// Make one end of the line move with handle (point) while the other is pinned to center of pathLayer_ (ie. in figure8 its the cross point, in a circle it's center)
//CGFloat rot = [self getTouchAngle:point];
CGFloat rot = atan2f((point.x - pathLayerCenter_.x), -(point.y - pathLayerCenter_.y));
handleView_.layer.transform = CATransform3DMakeRotation(rot, 0., 0., 1.);
[self layoutHandleView];
}
I'm just going to ignore your code and tell you how I'd modify my original answer to add the line you want.
First, I'm going to use a dedicated shape layer for the line, so I'll add an instance variable to reference the line layer:
#implementation ViewController {
CAShapeLayer *lineLayer_; // NEW!!!
UIBezierPath *path_;
CAShapeLayer *pathLayer_;
etc.
I need to initialize the line layer when I load the view:
- (void)viewDidLoad {
[super viewDidLoad];
[self initPathLayer];
[self initHandleView];
[self initHandlePanGestureRecognizer];
[self initLineLayer]; // NEW!!!
}
- (void)initLineLayer {
lineLayer_ = [CAShapeLayer layer];
lineLayer_.lineWidth = 1;
lineLayer_.fillColor = nil;
lineLayer_.strokeColor = [UIColor redColor].CGColor;
lineLayer_.lineCap = kCALineCapRound;
lineLayer_.lineJoin = kCALineJoinRound;
[self.view.layer addSublayer:lineLayer_];
}
I need to lay out the line layer when I lay out my top-level view (when it's first shown and on rotations):
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self createPath];
[self createPathPoints];
[self layoutPathLayer];
[self layoutHandleView];
[self layoutLineLayer]; // NEW!!!
}
- (void)layoutLineLayer {
lineLayer_.frame = self.view.bounds;
CGRect bounds = self.view.bounds;
CGPoint start = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
CGPoint end = pathPoints_[handlePathPointIndex_];
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:start];
[path addLineToPoint:end];
lineLayer_.path = path.CGPath;
}
Finally, I need to lay out the line layer again (to update its path) whenever I update the handle:
- (void)moveHandleTowardPoint:(CGPoint)point {
// I have omitted most of this method body for brevity.
// Just copy it from the original answer.
...
...
handlePathPointIndex_ += offset;
[self layoutHandleView];
[self layoutLineLayer]; // NEW!!!
}
Result:

Map CLLocationCoordinate2D (lat,long) to iphone/ipad screen coordinate

I'm drawing a straight line from point A to point B on a map from coordinates using MKOverlayView
This goes well but I need to repeat that same line with UIBezierPath now.
Can I transform somehow the map coordinate values to screen coordinate values?
This is what I have so far
#define EARTH_RADIUS 6371
#implementation ViewController
#synthesize mapView = _mapView;
#synthesize routeLine = _routeLine;
#synthesize routeLineView = _routeLineView;
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
// create the overlay
[self loadRoute];
// add the overlay to the map
if (nil != self.routeLine) {
[self.mapView addOverlay:self.routeLine];
}
// zoom in on the route.
[self zoomInOnRoute];
}
// creates the route (MKPolyline) overlay
-(void) loadRoute
{
NSString* fileContents = #"41.0311,21.3403 42.0012,21.4167";
NSArray* pointStrings = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
// while we create the route points, we will also be calculating the bounding box of our route
// so we can easily zoom in on it.
MKMapPoint northEastPoint;
MKMapPoint southWestPoint;
// create a c array of points.
MKMapPoint* pointArr = malloc(sizeof(CLLocationCoordinate2D) * pointStrings.count);
NSMutableArray *cgPoint = [NSMutableArray array];
for(int idx = 0; idx < pointStrings.count; idx++)
{
// break the string down even further to latitude and longitude fields.
NSString* currentPointString = [pointStrings objectAtIndex:idx];
NSArray* latLonArr = [currentPointString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#","]];
CLLocationDegrees latitude = [[latLonArr objectAtIndex:0] doubleValue];
CLLocationDegrees longitude = [[latLonArr objectAtIndex:1] doubleValue];
// create our coordinate and add it to the correct spot in the array
CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(latitude, longitude);
CGPoint latLong = CGPointFromString([NSString stringWithFormat:#"{%#}",currentPointString]);
[cgPoint addObject:[NSValue valueWithCGPoint:latLong]];
MKMapPoint point = MKMapPointForCoordinate(coordinate);
// if it is the first point, just use them, since we have nothing to compare to yet.
if (idx == 0) {
northEastPoint = point;
southWestPoint = point;
}
else
{
if (point.x > northEastPoint.x)
northEastPoint.x = point.x;
if(point.y > northEastPoint.y)
northEastPoint.y = point.y;
if (point.x < southWestPoint.x)
southWestPoint.x = point.x;
if (point.y < southWestPoint.y)
southWestPoint.y = point.y;
}
pointArr[idx] = point;
}
CGPoint point = [self convertLatLongCoord:[[cgPoint objectAtIndex:0] CGPointValue]];
CGPoint point1 = [self convertLatLongCoord:[[cgPoint objectAtIndex:1] CGPointValue]];
// create the polyline based on the array of points.
self.routeLine = [MKPolyline polylineWithPoints:pointArr count:pointStrings.count];
_routeRect = MKMapRectMake(southWestPoint.x, southWestPoint.y, northEastPoint.x - southWestPoint.x, northEastPoint.y - southWestPoint.y);
// clear the memory allocated earlier for the points
free(pointArr);
UIView *invisibleView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
invisibleView.backgroundColor=[UIColor clearColor];
[self.view addSubview:invisibleView];
// [self drawLine];
[invisibleView setUserInteractionEnabled:NO];
UIBezierPath *linePath = [UIBezierPath bezierPath];
[linePath moveToPoint:point1];
[linePath addLineToPoint:point];
//shape layer for the line
CAShapeLayer *line = [CAShapeLayer layer];
line.path = [linePath CGPath];
line.strokeColor = [[UIColor whiteColor] CGColor];
line.lineWidth = 5;
[invisibleView.layer addSublayer:line];
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration =8.0;
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
[line addAnimation:pathAnimation forKey:#"strokeEnd"];
}
- (CGPoint)convertLatLongCoord:(CGPoint)latLong {
CGSize screenSize = [UIScreen mainScreen].applicationFrame.size;
CGFloat SCALE = MIN(screenSize.width, screenSize.height) / (2.0 * EARTH_RADIUS);
CGFloat OFFSET = MIN(screenSize.width, screenSize.height) / 2.0;
CGFloat x = EARTH_RADIUS * cos(latLong.x) * cos(latLong.y) * SCALE + OFFSET;
CGFloat y = EARTH_RADIUS * cos(latLong.x) * sin(latLong.y) * SCALE + OFFSET;
return CGPointMake(x, y);
}
-(void) zoomInOnRoute
{
[self.mapView setVisibleMapRect:_routeRect];
//[self.mapView setUserInteractionEnabled:NO];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
}
#pragma mark MKMapViewDelegate
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
MKOverlayView* overlayView = nil;
if(overlay == self.routeLine)
{
//if we have not yet created an overlay view for this overlay, create it now.
if(nil == self.routeLineView)
{
self.routeLineView = [[MKPolylineView alloc] initWithPolyline:self.routeLine];
self.routeLineView.fillColor = [UIColor whiteColor];
self.routeLineView.strokeColor = [UIColor blueColor];
self.routeLineView.lineWidth = 0.5;
}
overlayView = self.routeLineView;
}
return overlayView;
}
And this is how it looks:
I need that white line to go over the blue one, any help will be much appreciated

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.

Resources