with the help i am able to draw circle with the coordinated using:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 5.0);
CGContextSetStrokeColorWithColor(context,[UIColor grayColor].CGColor);
/**** Get values from reponse and fill it in this array. ******/
NSArray *objectCoords = [NSArray arrayWithObjects: #"{{20,80},{5,5}}", #"{{120,60},{5,5}}", #"{{60,84},{5,5}}", #"{{80,88},{5,5}}", #"{{100,93},{5,5}}", #"{{20,20},{5,5}}", #"{{160,70},{5,5}}", #"{{128,68},{5,5}}", #"{{90,60},{5,5}}", #"{{110,80},{5,5}}", nil];
for (NSString* objectCoord in objectCoords) {
CGRect coord = CGRectFromString(objectCoord);
// Draw using your coord
CGContextAddEllipseInRect(context, coord);
}
Now what i am trying to achieve is draw lines between the points(circle),as shown in the attached image.I know we can draw a line between 2 points, but here in this case, the lines needs to be drawn between one to multiple point/circle .Please suggest me to achieve this result.
You can just draw multiple lines. First, come up with a model to represent between which points you want to draw lines. For example, you could have an array of lines, where each line is defined, itself, as an array with the indices of two points, the starting point and the ending point.
For example, if you want to draw lines from point 1 to 3, 4, and 5, and from point 3 to 4 and 5, and between 4 and 5, you could do something like:
CGContextSetLineWidth(context, 1.0);
NSArray *lines = #[#[#(1), #(3)],
#[#(1), #(4)],
#[#(1), #(5)],
#[#(3), #(4)],
#[#(3), #(5)],
#[#(4), #(5)]];
for (NSArray *points in lines) {
NSInteger startIndex = [points[0] integerValue];
NSInteger endIndex = [points[1] integerValue];
CGRect startRect = CGRectFromString(objectCoords[startIndex]);
CGRect endRect = CGRectFromString(objectCoords[endIndex]);
CGContextMoveToPoint(context, CGRectGetMidX(startRect), CGRectGetMidY(startRect));
CGContextAddLineToPoint(context, CGRectGetMidX(endRect), CGRectGetMidY(endRect));
}
CGContextDrawPath(context, kCGPathStroke);
There are tons of different ways of doing it, but just come up with some model that represents where you want to draw the lines, and then iterate through that model to draw all of the individual lines.
If you wanted to have every point draw lines to the three closest points (which is not what your picture does, but it's what you asked for in a subsequent comment), you can:
build array of indices, indices in your objectCoords array; and
now iterate through each point in objectCoords:
build new array of indices, sortedIndices, sorted by the distance that the point represented by that index is from the current object in objectCoords; and
draw the three closest lines.
Thus:
// build array of indices (0, 1, 2, ...)
NSMutableArray *indices = [NSMutableArray array];
[objectCoords enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[indices addObject:#(idx)];
}];
// now go through all of the points in objectCoords
[objectCoords enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) {
// build new array of indices sorted by distance from the current point
NSArray *sortedIndices = [indices sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
CGFloat distance1 = [self distanceFromPointAtIndex:idx
toPointAtIndex:[obj1 integerValue]
usingObjectCoords:objectCoords];
CGFloat distance2 = [self distanceFromPointAtIndex:idx
toPointAtIndex:[obj2 integerValue]
usingObjectCoords:objectCoords];
if (distance1 < distance2)
return NSOrderedAscending;
else if (distance1 > distance2)
return NSOrderedDescending;
return NSOrderedSame;
}];
// now draw lines to the three closest indices
// (skipping 0, because that's probably the current point)
for (NSInteger i = 1; i < 4 && i < [sortedIndices count]; i++) {
NSInteger index = [sortedIndices[i] integerValue];
CGRect startRect = CGRectFromString(objectCoords[idx]);
CGRect endRect = CGRectFromString(objectCoords[index]);
CGContextMoveToPoint(context, CGRectGetMidX(startRect), CGRectGetMidY(startRect));
CGContextAddLineToPoint(context, CGRectGetMidX(endRect), CGRectGetMidY(endRect));
}
}];
CGContextSetLineWidth(context, 1);
CGContextDrawPath(context, kCGPathStroke);
And this uses the following method to calculate the distance between two points:
- (CGFloat)distanceFromPointAtIndex:(NSInteger)index1 toPointAtIndex:(NSInteger)index2 usingObjectCoords:(NSArray *)objectCoords
{
CGRect rect1 = CGRectFromString(objectCoords[index1]);
CGRect rect2 = CGRectFromString(objectCoords[index2]);
return hypotf(CGRectGetMidX(rect1) - CGRectGetMidX(rect2), CGRectGetMidY(rect1) - CGRectGetMidY(rect2));
}
Using the objectCoords in your original example, that yields:
This is a little unrelated to your original question, but rather than an array of strings, like so:
NSArray *objectCoords = #[#"{{20,80},{5,5}}",
#"{{120,60},{5,5}}",
#"{{60,84},{5,5}}",
#"{{80,88},{5,5}}",
#"{{100,93},{5,5}}",
#"{{20,20},{5,5}}",
#"{{160,70},{5,5}}",
#"{{128,68},{5,5}}",
#"{{90,60},{5,5}}",
#"{{110,80},{5,5}}"];
I might suggest employing an array of NSValue objects:
NSArray *objectCoords = #[[NSValue valueWithCGRect:CGRectMake(20,80,5,5)],
[NSValue valueWithCGRect:CGRectMake(120,60,5,5)],
[NSValue valueWithCGRect:CGRectMake(60,84,5,5)],
[NSValue valueWithCGRect:CGRectMake(80,88,5,5)],
[NSValue valueWithCGRect:CGRectMake(100,93,5,5)],
[NSValue valueWithCGRect:CGRectMake(20,20,5,5)],
[NSValue valueWithCGRect:CGRectMake(160,70,5,5)],
[NSValue valueWithCGRect:CGRectMake(128,68,5,5)],
[NSValue valueWithCGRect:CGRectMake(90,60,5,5)],
[NSValue valueWithCGRect:CGRectMake(110,80,5,5)]];
Then, when you're extracting the CGRect value, instead of:
CGRect rect = CGRectFromString(objectCoords[index]);
You would do:
CGRect rect = [objectCoords[index] CGRectValue];
I know this looks more cumbersome, but using NSValue is going to be more efficient (which is useful when doing a lot or repeated calculations of distances).
Even better, you might want to define your own model object that more intuitively defines a point that you want to chart, e.g.:
#interface ChartPoint : NSObject
#property (nonatomic) CGPoint center;
#property (nonatomic) CGFloat radius;
#end
and
#implementation ChartPoint
+ (instancetype) chartPointWithCenter:(CGPoint)center radius:(CGFloat)radius
{
ChartPoint *chartPoint = [[ChartPoint alloc] init];
chartPoint.center = center;
chartPoint.radius = radius;
return chartPoint;
}
- (CGFloat)distanceToPoint:(ChartPoint *)otherPoint
{
return hypotf(self.center.x - otherPoint.center.x, self.center.y - otherPoint.center.y);
}
#end
And then you can create an array of them like so:
NSArray *objectCoords = #[[ChartPoint chartPointWithCenter:CGPointMake(20,80) radius:5],
[ChartPoint chartPointWithCenter:CGPointMake(120,60) radius:5],
[ChartPoint chartPointWithCenter:CGPointMake(60,84) radius:5],
[ChartPoint chartPointWithCenter:CGPointMake(80,88) radius:5],
[ChartPoint chartPointWithCenter:CGPointMake(100,93) radius:5],
[ChartPoint chartPointWithCenter:CGPointMake(20,20) radius:5],
[ChartPoint chartPointWithCenter:CGPointMake(160,70) radius:5],
[ChartPoint chartPointWithCenter:CGPointMake(128,68) radius:5],
[ChartPoint chartPointWithCenter:CGPointMake(90,60) radius:5],
[ChartPoint chartPointWithCenter:CGPointMake(110,80) radius:5]];
But this
avoids inefficient CGRectFromString;
avoids needing to do those repeated CGRectGetMidX and CGRectGetMidY calls to determine the center of the CGRect; and, most importantly,
more accurately represents what your objects really are.
Obviously, when you want to draw your points, instead of doing:
NSString *string = objectCoords[idx];
CGRect *rect = CGRectFromString(string);
CGContextAddEllipseInRect(context, rect);
You'd do:
ChartPoint *point = objectCoords[idx];
CGContextAddArc(context, point.center.x, point.center.y, point.radius, 0, M_PI * 2.0, YES);
In my app I want to move a little UIImageView with inside a .png; this is a little insect and I want to simulate his flight. At example I want that this png do when it move an inverted eight as the infinite simbol ∞
You may use CoreAnimation. You can subclass a view, create a subview for the insect, and then assign an animation to it, following a defined path.
Your UIImageView could be animated. If it's a fly, you can do a few frames for wing moves:
NSArray *images = [NSArray arrayWithObjects:..., nil];
insect.animationImages = images;
insect.animationDuration = ??;
insect.animationRepeatCount = 0;
[insect startAnimating];
Then set an init frame for the insect:
insect.frame = CGRectMake(-120, 310, [[images objectAtIndex:0] size].width, [[images objectAtIndex:0] size].height);
And then define the path:
CGMutablePathRef aPath;
CGFloat arcTop = insect.center.y - 50;
aPath = CGPathCreateMutable();
CGPathMoveToPoint(aPath, NULL, insect.center.x, insect.center.y);
CGPathAddCurveToPoint(aPath, NULL, insect.center.x, arcTop, 240, -100, 490, 360);
CAKeyframeAnimation* arcAnimation = [CAKeyframeAnimation animationWithKeyPath: #"position"];
arcAnimation.repeatCount = HUGE_VALF;
[arcAnimation setDuration: 4.5];
[arcAnimation setAutoreverses: NO];
arcAnimation.removedOnCompletion = NO;
arcAnimation.fillMode = kCAFillModeBoth;
[arcAnimation setPath: aPath];
CFRelease(aPath);
[insect.layer addAnimation: arcAnimation forKey: #"position"];
I leave how to do the infinite loop path up to you :)
Hope it helps!
Normally, if you were to be moving things around, I'd suggest using [UIView animate...]. However, you want something to move on a complex, curvy path. So instead, I'd suggest coming up with an equation that gives the (x,y) for the insect as a function of time, and then start an NSTimer with a fairly small time interval, and every time you get an update, move the insect (perhaps using [UIView animate...]).
Another way to go is to use a 2-d animation framework such as cocos2d - then, you can get an 'update' call linked to the frame refresh rate, inside of which you update the position of your insect using the same equation as from above.
I'm developing a game in which I want my image to reduce in size gradually. I'm reducing the frame size gradually in my code when it works fine. [I've already used CGAffineTransform and it doesn't suit my requirement.]
-(void)function
{
ravanImage1.frame=CGRectMake(150,((ravanImage1.frame.origin.y)-5),q,z);
if(ravanImage1.center.y>=300&&ravanImage1.center.y<=370)
{
q=60;
z=60;
ravanImage1.frame=CGRectMake(150,((ravanImage1.frame.origin.y)-5),q,z);
}
if(ravanImage1.center.y>=230&&ravanImage1.center.y<=299)
{
q=40;
z=40;
ravanImage1.frame=CGRectMake(150,((ravanImage1.frame.origin.y)-5),q,z);
}
if(ravanImage1.center.y>=150&&ravanImage1.center.y<=229)
{
q=20;
z=20;
ravanImage1.frame=CGRectMake(150,((ravanImage1.frame.origin.y)-5),q,z);
}
}
But when I apply a while loop for the same code specifying wheather at what point to stop reducing the frame("while that point isn't reached"), it doesn't show the image frame reduction little by little as it shows it otherwise, but directly places the image at the end point with proper frame.
I want it to get displyed the way it gets without the while loop i.e. reduction little by little. Yes, while debugging it steps through all the steps properly.
Can anybody please help me?
As others have pointed out, manually adjusting the frame of your view will give you terrible performance. If you really don't want to use a standard UIView animation block for changing your view, you can specify bounds size values to animate through using a CAKeyframeAnimation applied to your view's layer:
CAKeyframeAnimation * customSizeAnimation = [CAKeyframeAnimation animationWithKeyPath:#"bounds.size"];
NSArray *sizeValues = [NSArray arrayWithObjects:[NSValue valueWithCGSize:size1], [NSValue valueWithCGSize:size2], [NSValue valueWithCGSize:size3], nil];
[customSizeAnimation setValues:frameValues];
NSArray *times = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0f], [NSNumber numberWithFloat:0.5f], [NSNumber numberWithFloat:1.0f], nil];
[customSizeAnimation setKeyTimes:times];
customSizeAnimation.fillMode = kCAFillModeForwards;
customSizeAnimation.removedOnCompletion = NO;
[view.layer addAnimation: customSizeAnimation forKey:#"customSizeAnimation"];
This animation will start at size1, pass through size2 at the midway point in the animation, and end at size3. You can have an arbitrary number of key frames and times for your animation, so you should be able to achieve the effect you desire
EDIT (1/5/2010): Removed kCAAnimationPaced as a calculationMode, which would cause the key times to be ignored. Also, I forgot that frame was a derived property, so you need to animate something like the bounds size instead.
The reason it does that is because it executes the while loop very quickly. I think the best thing to do is put some sort of a delay timer after each step of the while loop, then you'll see each step and it won't just 'jump' to it's final state.
[self setTimer: [NSTimer scheduledTimerWithTimeInterval: 3.5
target: self
selector: #selector (function_name)
userInfo: nil
repeats: YES]];
try using this.
This' my move function in which I'm trying to change the size of my imageView. If you can point out any error, I'll be really grateful..
-(void)move
{
UIImageView *imageViewForAnimation = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"ravan.jpg"]];
imageViewForAnimation.alpha = 1.0f;
CGSize size1=CGSizeMake(60,60);
CGSize size2=CGSizeMake(40,40);
CGSize size3=CGSizeMake(20,20);
CAKeyframeAnimation *customFrameAnimation = [CAKeyframeAnimation animationWithKeyPath:#"frame"];
NSArray *sizeValues = [NSArray arrayWithObjects:[NSValue valueWithCGSize:size1], [NSValue valueWithCGSize:size2], [NSValue valueWithCGSize:size3], nil];
[customFrameAnimation setValues:sizeValues];
customFrameAnimation.duration=10.0;
NSArray *times = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0f], [NSNumber numberWithFloat:0.5f], [NSNumber numberWithFloat:1.0f], nil];
[customFrameAnimation setKeyTimes:times];
customFrameAnimation.fillMode = kCAFillModeForwards;
customFrameAnimation.removedOnCompletion = NO;
[imageViewForAnimation.layer addAnimation:customFrameAnimation forKey:#"customFrameAnimation"];
[self.view addSubview:imageViewForAnimation];
}