Draw straight line on uiview - iphone

I'm writing a small word search game for the iPad.
i have a UIViewController and I tile the letters random in a UIView (lettersView). I set the label's tag in a 10x10 size matrix, using a UILabel for each letter.
Here is my problem: I don't know how to draw over letters (UILabels).
I have researched the matter and am confused which way to use (touch, opengl, quartz, hittest).
I just want to draw a straight line from the first point to the last point (touch down to touch up).
when user taps on the lettersView, I must find which letter was tapped. I can find the letter if I get the which UILabel was tapped first.
while the user moves their finger, it should draw the line, but while the user moves it must delete the old ones.
when the user touches up how can I get the last UILabel? i can check if its drawable, if I get the first and last uilabel.
(i found a small drawing sample written in ARC mode. but i cant convert it to non-ARC.)
i did all what you said. but i have some problems.
it draws the line on drawRect method. when i draw second line, it deletes the old one.
here is the codes:
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
touchStart = [[touches anyObject] locationInView:self];
float xPos=touchStart.x;
float yPos=touchStart.y;
startIndexCol = floor(xPos / letterWidth);
startIndexRow = floor(yPos / letterHeight);
xPos = (startIndexCol * letterWidth) + letterWidth/2;
yPos = (startIndexRow * letterHeight) + letterHeight/2;
touchStart = CGPointMake(xPos, yPos);
touchCurrent = touchStart;
[self setNeedsDisplay];
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
touchCurrent = [[touches anyObject] locationInView:self];
//CGRect frame = CGRectMake(touchStart.x, touchStart.y, touchCurrent.x, touchCurrent.y);
//[self setNeedsDisplayInRect:frame];
[self setNeedsDisplay];
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
touchCurrent = [[touches anyObject] locationInView:self];
//[self hitTest:touchCurrent withEvent:event];
float xPos=touchCurrent.x;
float yPos=touchCurrent.y;
endIndexCol = floor(xPos / letterWidth);
endIndexRow = floor(yPos / letterHeight);
xPos = (endIndexCol * letterWidth) + letterWidth/2;
yPos = (endIndexRow * letterHeight) + letterHeight/2;
touchCurrent = CGPointMake(xPos, yPos);
//CGRect frame = CGRectMake(touchStart.x, touchStart.y, touchCurrent.x, touchCurrent.y);
//[self setNeedsDisplayInRect:frame];
//check if it can be drawn
if ((startIndexCol == endIndexCol) && (startIndexRow == endIndexRow)) {
//clear, do nothing
touchStart = touchCurrent = (CGPoint) { -1, -1 };
startIndexCol = startIndexRow = endIndexCol = endIndexRow = -1;
}
else{
if (startIndexRow == endIndexRow) {//horizontal
if (endIndexCol > startIndexCol) {
[delegate checkWordInHorizontal:startIndexRow start:startIndexCol end:endIndexCol];
}
else{
[delegate checkWordInHorizontalBack:startIndexRow start:startIndexCol end:endIndexCol];
}
}
else if (startIndexCol == endIndexCol){ //vertical
if (endIndexRow > startIndexRow) {
[delegate checkWordInVertical:startIndexCol start:startIndexRow end:endIndexRow];
}
else{
[delegate checkWordInVerticalBack:startIndexCol start:startIndexRow end:endIndexRow];
}
}
}
[self setNeedsDisplay];
}
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
UIView *subview = [super hitTest:point withEvent:event];
if ([subview isEqual:self]) {
NSLog(#"point:%f - %f", point.x, point.y);
return self;
}
return nil;
}
-(void)drawLine{
NSLog(#"draw it");
drawIt=YES;
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
if (drawIt) {
NSLog(#"drawit");
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(ctx, 20.0);
CGContextSetRGBStrokeColor(ctx, 1.0, 2.0, 0, 0.5);
CGContextMoveToPoint(ctx, touchStart.x, touchStart.y);
CGContextAddLineToPoint(ctx, touchCurrent.x, touchCurrent.y);
CGContextStrokePath(ctx);
drawIt=NO;
touchStart = touchCurrent = (CGPoint) { -1, -1 };
startIndexCol = startIndexRow = endIndexCol = endIndexRow = -1;
}
[[UIColor redColor] setStroke];
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:touchStart];
[path addLineToPoint:touchCurrent];
[path setLineWidth:20.0];
[path strokeWithBlendMode:kCGBlendModeNormal alpha:0.5];
// [path stroke];
}

First, the UILabels are likely to cause you more trouble than good. For such as simple grid, it is likely easier to just draw the letters by hand in drawRect:, along with your connecting line. The overall structure would look like this:
In touchesBegan:withEvent:, you would make a note of the starting point in an ivar.
In touchesMoved:withEvent:, you would update the end-point in an ivar and call [self setNeedsDisplayInRect:]. Pass the rectangle defined by your two points.
In touchesEnded:withEvent:, you would make a hit-test, do whatever processing that entails, and then clear your starting and end points and call [self setNeedsDisplayInRect:].
By "make a hit-test," I mean calculate which letter is under the touch. Since you're laying these out in a grid, this should be very simple math.
In drawRect:, you would use [NSString drawAtPoint:withFont:] to draw each letter. You would then use UIBezierPath to draw the line (you could also use CGPathRef, but I'd personally use the UIKit object).

Related

How To select all the targets with touches rect in cocos2d

i want to select my targets using touches rect
am creating my unselected dots by coding like these:-
targets1 = [[NSMutableArray alloc] init];
for(int i=0;i<3;i++)
{
for (int y=0; y<3; y++) {
CCTexture2D *texture =
[[CCTextureCache sharedTextureCache] addImage:#"UnselectedDot.png"];
block = [CCSprite spriteWithTexture:texture rect:CGRectMake(0,0,82,82)];
CGFloat xoffset = ((block.contentSize.width)*10) + (((block.contentSize.height)-175)*y);
block.position = ccp( (i*82)+80,xoffset);
[bg1 addChild:block];
[targets1 addObject:block];
}
}
below is my sample output .
now i need to select all the dots by touches method. i written coding like these:-
- (void)update:(ccTime)dt {
// NSLog(#"%#",targets1);
for (CCSprite *sprite in targets1) {
CGRect dotrect = CGRectMake(sprite.position.x,
sprite.position.y-95,
sprite.contentSize.width,
sprite.contentSize.height);
CGFloat x = location.x;
CGFloat y = location.y;
CGFloat width = (location1.x - location.x);
CGFloat height = -(location1.y - location.y);
CGRect touchrect = CGRectMake (x, y, width,height);
NSLog(#"dotrect = %f,%f,%f,%f",dotrect.origin.x,dotrect.origin.y,dotrect.size.width,dotrect.size.height );
NSLog(#"touch rect = %f,%f,%f,%f,%f,%f",touchrect.origin.x,touchrect.origin.y,touchrect.size.width,touchrect.size.height,location1.x,location1.y);
if( CGRectContainsRect(dotrect, touchrect))
{ //collision detection
NSLog(#"am touched dot ");
}
}
}
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
NSLog(#"am touched began");
return YES;
}
- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {
location1 = [touch locationInView:[touch view]];
location1 = [[CCDirector sharedDirector] convertToGL:location1];
location1 = [self convertToNodeSpace:location1];
}
as per above coding my concept is :- using touches began and touches ended am making rectangle here.. inside these touchrect my unselected dot rect came means collsison detected.. then i can do my stuffs there. but its not colliding all.
am not getting were am making mistake.
Edit :1
now i got why coillision not working... actually insidemy touches rect having multiple rect.. so only ... here, am using rectcontainsrect.. tahts the problem.. any other method to rect having several rect for colllsion detection..
Try using:
CGPoint location=[touch locationInView:[touch view]];
location = [self convertTouchToNodeSpace:touch];
CGRectContainsPoint([dot boundingBox], location);
inside the CCTouch.. methods (as you wish depending on what you want to do).

Redrawing a path using CGContextStrokePath after initial load

I have a basic map view in my app which contains a set of user definable way points. When the view is loaded I draw a path connecting the way points. This works well.
However, when the user drags a way point around the view, I would like it to re-draw the paths and that is my problem. I dont know how to hacant get it to draw anything after the first time. Here's my code:
- (void)drawRect:(CGRect)rect { ////// works perfectly
context = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, 1, 0, 1, .7);
CGContextSetLineWidth(context, 20.0);
WaypointViewController *w = [arrayOfWaypoints objectAtIndex:0];
CGPoint startPoint = w.view.center;
CGContextMoveToPoint(context, startPoint.x, startPoint.y);
for (int i = 1; i<[arrayOfWaypoints count]; i++) {
WaypointViewController *w2 = [arrayOfWaypoints objectAtIndex:i];
CGPoint nextPoint = w2.view.center;
CGContextAddLineToPoint(context,nextPoint.x, nextPoint.y);
}
CGContextStrokePath(context);
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if (moving) {
UITouch *touch = [touches anyObject];
currentWaypoint.view.center = [touch locationInView:self];
[delegate setUserInteraction:NO];
[self drawInContext:context];
[NSThread detachNewThreadSelector:#selector(drawInContext:) toTarget:self withObject:nil];
}
}
- (void)drawInContext:(CGContextRef)context { ///gets called, but does nothing
CGContextSetRGBStrokeColor(context, 1, 0, 1, .7);
CGContextSetLineWidth(context, 20.0);
WaypointViewController *w = [arrayOfWaypoints objectAtIndex:0];
CGPoint startPoint = w.view.center;
CGContextMoveToPoint(context, startPoint.x, startPoint.y);
for (int i = 1; i<[arrayOfWaypoints count]; i++) {
WaypointViewController *w2 = [arrayOfWaypoints objectAtIndex:i];
CGPoint nextPoint = w2.view.center;
CGContextAddLineToPoint(context,nextPoint.x, nextPoint.y);
}
CGContextStrokePath(context);
}
You can not use the context that is active in drawRect: anywhere else.
Instead of calling [self drawInContext:context] call [self setNeedsDisplay]
The context that you are pointing to in your drawRect: might be freed or might still be active you don't know, but either way there is no way to get the bits you put into that context onto the screen.
Also, please read the docs on drawing on iOS here
You may not save the context for use after drawRect: returns. What you need to do is call [self setNeedsDisplay] (or setNeedsDisplayInRect:, if you can determine the bounding rectangle of the change), which will cause the system to call drawRect: again for you.
You can do all your drawing only in drawRect ...Hope this helps ... hence - (void)drawInContext:(CGContextRef)context will not be able to do any drawing. if after user's touch action , you need to do some redrawing then you need to call [self setNeedsDisplay] in your touchmethods.

code iphone image drawing lines

Hi everyone I'm french so scuse me for my english.So I want to make a game like flight control. When I draw line from an image like a plane I want that the plane follow that line .How can I do this please?
To draw the line, implement touchesBegan:, touchedMove:, touchedEnded, touchesCancelled: in your view or view controller and build a path (CGPathRef) using the touch points.
To make an an object move along the line, create a CAKeyframeAnimation, set the path and assign it to the object's layer.
Edit: sample code
When you have a CGPathRef path, creating the animation is as easy as:
CAKeyframeAnimation* animation = [CAKeyframeAnimation animation];
animation.path = thePath;
animation.duration = 2;
animation.rotationMode = kCAAnimationRotateAuto; // object auto rotates to follow the path
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
Finally, assign the animation to a layer:
[layer1 addAnimation:animation forKey:#"position"];
Edit 2: sample application:
I built an entire project called PathAnimation for you.
Edit 3: this is the code I used in the PathAnimation project:
//
// CustomView.m
// PathAnimation
//
// Created by Dominique d'Argent on 19.04.11.
// Copyright 2011 Nicky Nubbel. All rights reserved.
//
#import "CustomView.h"
#implementation CustomView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
object = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"arrow"]];
object.frame = CGRectMake(10.0, 30.0, 20.0, 20.0);
[self addSubview:object];
[self createPath];
}
return self;
}
- (void)dealloc
{
[object release];
[super dealloc];
}
#pragma mark - Custom drawing
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
CGContextRef g = UIGraphicsGetCurrentContext();
CGFloat bgColor[4] = {1.0f, 1.0f, 1.0f, 1.0f};
CGContextSetFillColor(g, bgColor);
CGContextFillRect(g, self.frame);
CGFloat color[4] = {1.0f, 0.0f, 0.0f, 1.0f};
CGContextAddPath(g,path);
CGContextSetStrokeColor(g, color);
CGContextDrawPath(g, kCGPathStroke);
CGPoint position = CGPathGetCurrentPoint(path);
CGContextAddArc(g, position.x, position.y, 5.0f, 0.0f, 2 * M_PI, 0);
CGContextSetFillColor(g, color);
CGContextDrawPath(g, kCGPathFill);
}
#pragma mark - Path creation
- (void)createPath {
path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, object.center.x, object.center.y);
}
#pragma mark - Touch handling
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint position = [touch locationInView:self];
if (CGRectContainsPoint(object.frame, position)) {
// start animation
[self go];
}
else {
CGPoint lastPosition = CGPathGetCurrentPoint(path);
if (CGPointEqualToPoint(lastPosition, object.center)) {
CGFloat angle = -atan2f(position.x - lastPosition.x, position.y - lastPosition.y);
angle += M_PI_2;
object.layer.transform = CATransform3DMakeRotation(angle, 0.0, 0.0, 1.0);
}
CGPathAddLineToPoint(path, NULL, position.x, position.y);
[self setNeedsDisplay];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
}
#pragma mark - Animations
- (void) go {
NSLog(#"go");
object.layer.transform = CATransform3DIdentity;
CAKeyframeAnimation* animation = [CAKeyframeAnimation animation];
animation.path = path;
animation.duration = 5.0;
animation.rotationMode = kCAAnimationRotateAuto; // object auto rotates to follow the path
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
[object.layer addAnimation:animation forKey:#"position"];
object.center = CGPathGetCurrentPoint(path);
[self createPath];
}
#end

Drawing bezier curves with my finger in iOS?

Hey, I'm trying to figure out how to generate bezier curves in iOS based on user input. Are there any existing classes for this? Can someone give me a general summary of what would be required? I just need help getting started on the right foot.
If you want to stay in objective-c, you can use UIBezierPath's addCurveToPoint:controlPoint1:controlPoint2: method. You can also use a similarly named function with CGPaths. When using bezier curves, you need 4 points: starting point, ending point, and a control point at each end to define the curve.
One way to define this is to have the user drag a finger to define the start and end points, then tap the screen at the control points. Here is an example view to handle this.
BezierView.h
enum {
BezierStateNone = 0,
BezierStateDefiningLine,
BezierStateDefiningCP1,
BezierStateDefiningCP2
};
#interface BezierView : UIView {
CGPoint startPt, endPt, cPt1, cPt2;
UInt8 state;
UIBezierPath *curvePath;
#private
UITouch *currentTouch;
}
#property (nonatomic, retain) UIBezierPath *curvePath;
#end
BezierView.m
#interface BezierView
#dynamic curvePath;
- (UIBezierPath *)curvePath {
return [[curvePath retain] autorelease];
}
- (void)setCurvePath:(UIBezierPath *)newPath {
id tmp = curvePath;
curvePath = [newPath retain];
[tmp release];
state = BezierStateNone;
[self setNeedsDisplay];
}
- (void)_updateCurve {
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:startPt];
[path addCurveToPoint:endPt controlPoint1:cPt1 controlPoint2:cPt2];
}
- (void)_calcDefaultControls {
if(ABS(startPt.x - endPt.x) > ABS(startPt.y - endPt.y)) {
cPt1 = (CGPoint){(startPt.x + endPt.x) / 2, startPt.y};
cPt2 = (CGPoint){cPt1.x, endPt.y};
} else {
cPt1 = (CGPoint){startPt.x, (startPt.y + endPt.y) / 2};
cPt2 = (CGPoint){endPt.x, cPt1.y};
}
}
- (void)drawRect:(CGRect)rect {
UIBezierPath *path = self.curvePath;
if(path) [path stroke];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if(currentTouch) return;
if(state == BezierStateNone) {
state = BezierStateDefiningLine;
currentTouch = [touches anyObject];
startPt = [currentTouch locationInView:self];
} else if(state == BezierStateDefiningCP1) {
currentTouch = [touches anyObject];
cPt1 = [currentTouch locationInView:self];
[self _updateCurve];
} else if(state == BezierStateDefiningCP2) {
currentTouch = [touches anyObject];
cPt2 = [currentTouch locationInView:self];
[self _updateCurve];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if(!currentTouch) return;
if(state == BezierStateDefiningLine) {
endPt = [currentTouch locationInView:self];
[self _calcDefaultControls];
[self _updateCurve];
} else if(state == BezierStateDefiningCP1) {
cPt1 = [currentTouch locationInView:self];
[self _updateCurve];
} else if(state == BezierStateDefiningCP2) {
cPt2 = [currentTouch locationInView:self];
[self _updateCurve];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if(!currentTouch) return;
if(state == BezierStateDefiningLine) {
state = BezierStateDefiningCP1;
} else if(state == BezierStateDefiningCP1) {
state = BezierStateDefiningCP2;
} else if(state == BezierStateDefiningCP2) {
state = BezierStateNone;
}
currentTouch = nil;
}
- (void)touchesCanceled:(NSSet *)touches withEvent:(UIEvent *)event {
if(state == BezierStateDefiningLine) {
self.curvePath = nil;
self.state = BezierStateNone;
}
self.currentTouch = nil;
}
Okay, the easiest way to do that is probably subclassing UIView and use CoreGraphics for drawing. Check out the sample code from QuarzDemo.
Implement the drawInRect-method for your custom view class. And detect the user's touches with touchesBegan,touchesMoved etc.
Here is an example code (taken from QuarzDemo) for drawing a bezier curve:
// Drawing with a white stroke color
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
// Draw them with a 2.0 stroke width so they are a bit more visible.
CGContextSetLineWidth(context, 2.0);
// Draw a bezier curve with end points s,e and control points cp1,cp2
CGPoint s = CGPointMake(30.0, 120.0);
CGPoint e = CGPointMake(300.0, 120.0);
CGPoint cp1 = CGPointMake(120.0, 30.0);
CGPoint cp2 = CGPointMake(210.0, 210.0);
CGContextMoveToPoint(context, s.x, s.y);
CGContextAddCurveToPoint(context, cp1.x, cp1.y, cp2.x, cp2.y, e.x, e.y);
CGContextStrokePath(context);
Hope that helps you getting started ;)

how to create a path using coregraphics?

i want to create a path.something like i touch the screen and draw line in touchmove event.when line intersect from starting point.fill that path using any colour.
now see in the image i've drawn a line.i just want to detect if line intersects again to start point.then fill that path with my own desired color.also i m using core graphics to draw line but it's very slow on real device.could you tell me a way to improve speed?
Header:
#import <UIKit/UIKit.h>
#interface myView : UIView {
CGMutablePathRef path;
CGPathRef drawingPath;
CGRect start;
BOOL outsideStart;
}
#end
Implementation:
#import "myView.h"
#implementation myView
- (id) init {
if (self = [super init]) {
self.userInteractionEnabled = YES;
self.multipleTouchEnabled = NO;
}
}
- (void) finishPath {
if (drawingPath) {
CGPathRelease(drawingPath);
}
CGPathCloseSubpath(path);
drawingPath = CGPathCreateCopy(path);
CGPathRelease(path);
path = NULL;
[self setNeedsDisplay];
return;
}
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
path = CGPathCreateMutable();
UITouch *t = [touches anyObject];
CGPoint p = [t locationInView:self];
start = CGRectZero;
start.origin = p;
start = CGRectInset(start,-10, -10);
outsideStart = NO;
CGPathMoveToPoint(path, NULL, p.x, p.y);
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if (!path) {
return;
}
UITouch *t = [touches anyObject];
CGPoint p = [t locationInView:self];
if (CGRectContainsPoint(start,p)) {
if (outsideStart) {
[self finishPath];
}
} else {
outsideStart = YES;
}
CGPathAddLineToPoint(path,NULL,p.x,p.y);
[self setNeedsDisplay];
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if (!path) {
return;
}
[self finishPath];
}
- (void) touchesCanceled:(NSSet *)touches withEvent:(UIEvent *)event {
if (!path) {
return;
}
CGPathRelease(path);
path = NULL;
}
- (void) drawInRect:(CGRect)rect {
CGContextRef g = UIGraphicsGetCurrentContext();
if (drawingPath) {
CGContextAddPath(g,drawingPath);
[[UIColor redColor] setFill];
[[UIColor blackColor] setStroke];
CGContextDrawPath(g,kCGPathFillStroke);
}
if (path) {
CGContextAddPath(g,path);
[[UIColor blackColor] setStroke];
CGContextDrawPath(g,kCGPathStroke);
}
}
- (void) dealloc {
if (drawingPath) {
CGPathRelease(drawingPath);
}
if (path) {
CGPathRelease(path);
}
[super dealloc];
}
#end
Note that you will probably want to do something so you aren't actually calling setNeedsDisplay every time the path changes. This can get very slow. Suggestions include having an NSTimer that fires every x milliseconds to check if it needs to redisplay and do so if it does, or only redrawing if the touch has moved a significant distance.
Maybe it can be useful to you link text
Core Graphics should definitely not be using [self setNeedsDisplay] every time the image changes, which is probably why your code is so slow on the device. Drawing is slow. If you use OpenGL ES to draw the lines, it will be much quicker, at the expense of being more difficult to understand.