I have drawn the polygon by using the Core Graphics. But I can't able to resize the polygon. I used UIBezierPath to draw Polygon. This is my code
CGPoint gestureStartPoint,currentPosition;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
currentPath = [[UIBezierPath alloc]init];
currentPath.lineWidth=1;
xx1 = 30;
yy1 = 30;
xx = 30;
yy = 30;
CGPoint gestureStartPoint,currentPosition;
}
return self;
}
- (void)drawRect:(CGRect)rect {
if(drawColor==nil){
[[UIColor redColor]setStroke];
[currentPath stroke];
}
else {
[drawColor setStroke];
[currentPath stroke];
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
gestureStartPoint = [touch locationInView:self];
currentPosition.x = xx;
currentPosition.y = yy;
xx = gestureStartPoint.x;
yy = gestureStartPoint.y;
[currentPath moveToPoint:(currentPosition)];
[currentPath addLineToPoint:(gestureStartPoint)];
[self setNeedsDisplay];
}
This is the link of the sample resizable Polygon. How to draw the polygon with the resizable property like this? I don't know where to start to make a resizable polygon.
This is more complicated than simply invoking some CoreGraphics magic.
To simply duplicate the logic on the site you linked to, I'd start by breaking down the problem:
Two types of gestures are recognized: taps, and tap-hold-drag.
Tap should add an x,y (point) to a list of points you are storing and redraw.
Tap-hold-drag should use the x,y location of the user's tap to determine the closest vertex - and you should probably do some max distance check as well. Once you've determined which vertex the user is "dragging", you can manipulate that point in your list and redraw.
Related
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).
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).
transform a square image or round image is easy to do...
But what if a rectangle image ???
This is the image I want to transform with touch event .
The circle center is (30 , 236) , if I touch any place on the screen
Imageview will transform the arrow to my touch point.
But the circle center still the same place .
I try to transform the image use a test angle
It will become like this ...
How to adjust the image ?
Also the code is here
- (void)viewDidLoad {
CGRect arrowImageRect = CGRectMake(20.0f, 20.0f, 15.0f, 220.0f);
arrow = [[UIImageView alloc] initWithFrame:arrowImageRect];
[arrow setImage:[UIImage imageNamed:#"arrow.png"]];
arrow.opaque = YES;
[self.view addSubview:arrow];
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self.view];
NSLog(#"Position X: %f \n, Y: %f",touchPoint.x,touchPoint.y);
CGAffineTransform transform = CGAffineTransformIdentity;
arrow.transform = CGAffineTransformRotate(transform, ?????? );
}
The ?????? part should be the last solution ...
Or Maybe I need to resize the imageview ???
Thanks for any reply or answer :-)
Webber
Here is the final solution I figure this problem
check this code
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch=[[event allTouches]anyObject];
[self transformSpinnerwithTouches:touch];
}
-(void)transformSpinnerwithTouches:(UITouch *)touchLocation
{
CGPoint touchLocationpoint = [touchLocation locationInView:self.view];
CGPoint PrevioustouchLocationpoint = [touchLocation previousLocationInView:self.view];
//origin is the respective piont from that i gonna measure the angle of the current position with respective to previous position ....
CGPoint origin;
origin.x = arrow.center.x;
origin.y = arrow.center.y;
CGPoint previousDifference = [self vectorFromPoint:origin toPoint:PrevioustouchLocationpoint];
CGAffineTransform newTransform = CGAffineTransformScale(arrow.transform, 1, 1);
CGFloat previousRotation = atan2(previousDifference.y, previousDifference.x);
CGPoint currentDifference = [self vectorFromPoint:origin toPoint:touchLocationpoint];
CGFloat currentRotation = atan2(currentDifference.y, currentDifference.x);
CGFloat newAngle = currentRotation- previousRotation;
temp1 = temp1 + newAngle;
//NSLog(#"temp1 is %f",temp1);
//NSLog(#"Angle:%F\n",(temp1*180)/M_PI);
newTransform = CGAffineTransformRotate(newTransform, newAngle);
[self animateView:arrow toPosition:newTransform];
}
-(void)animateView:(UIView *)theView toPosition:(CGAffineTransform) newTransform
{
arrow.transform = newTransform;
}
-(CGPoint)vectorFromPoint:(CGPoint)firstPoint toPoint:(CGPoint)secondPoint
{
CGFloat x = secondPoint.x - firstPoint.x;
CGFloat y = secondPoint.y - firstPoint.y;
CGPoint result = CGPointMake(x, y);
//NSLog(#"%f %f",x,y);
return result;
}
From the documentation about transform method:
The origin of the transform is the value of the center property, or the layer’s anchorPoint property if it was changed. (Use the layer property to get the underlying Core Animation layer object.) The default value is CGAffineTransformIdentity.
So you should be able to set the center of your rotation this way!
And for determining the rotation angle, use the atan2 or atan2f function:
float angleInRadians = atan2f(touchPoint.y-circleCenter.y , touchPoint.x-circleCenter.x);
You can re set the center after transform.
CGPoint centerBeforeTransform = view.center;
//your transform code
view.center = centerBeforeTransform;
How is it possible to drag a UIImageView but only within a certain area of the screen? My code currently looks like this:
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
touch.view.frame = CGRectMake(104, 171, 113, 49);
if([touch view] == toggle)
{
CGPoint location = [touch locationInView:self.view];
CGPoint newLocation = CGPointMake(toggle.center.x, location.y);
toggle.center = newLocation;
NSLog(#"%f \n",toggle.center.y);
}
}
I want to be able to drag the image only within the frame i have defined.
You can use CGRectContainsRect(rect1, rect2) to check if the first rect is completely inside of the second
bool CGRectContainsRect (
CGRect rect1,
CGRect rect2
);
When you are using UIViews and want to see if one view falls completely within the frame of a second, a related function CGRectContainsRect will do the checking for you. This does not check for an intersection; the union of both rectangles must be equal to the first rectangle for this to return true. The function takes two arguments. The first rectangle is always the surrounding item. The second argument either falls fully inside the first or it does not.
So your code could be something like this
CGPoint location = [touch locationInView:self.view];
CGPoint newLocation = CGPointMake(toggle.center.x, location.y);
CGRect r = CGRectMake(newLocation.x-self.frame.size.width/2,
newLocation.y-self.frame.size.height/2,
self.frame.size.width,
self.frame.size.height);
if(CGRectContainsRect(r, theOtherRect)){
toggle.center = newLocation;
NSLog(#"%f \n",toggle.center.y);
}
Other useful CoreGraphics functions: http://blogs.oreilly.com/iphone/2008/12/useful-core-graphics-functions.html
Another hint:NSLog(#"%#", NSStringFromCGPoint(toggle.center)) makes logging of CGTypes easier. use equivalently: NSStringFromCGRect(rect)
if (newLocation.y > NSMaxY(touch.view.frame)) newLocation.y = NSMaxY(touch.view.frame);
if (newLocation.y < NSMinY(touch.view.frame)) newLocation.y = NSMinY(touch.view.frame);
toggle.center = newLocation;
You can do the same for the x coordinate if you like.
I'm creating a simple drawing application and would like some help with drawing rectangles based on the user's touch events. I'm able to draw a rectangle using Core Graphics from the point in touchesBegan to the point in touchesEnded. This rectangle is then displayed once the touchesEnded event is fired.
However, I would like to be able to do this in a 'live' fashion. Meaning, the rectangle is updated and displayed as the user drags their finger. Is this possible? Any help would be greatly appreciated, thank you!
UPDATE:
I was able to get this to work by using the following code. It works perfectly fine for small images, however it get's very slow for large images. I realize my solution is hugely inefficient and was wondering if anyone could suggest a better solution. Thanks.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
_firstPoint = [touch locationInView:self.view];
_firstPoint.y -= 40;
_lastPoint = _firstPoint;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
currentPoint.y -= 40;
[self drawSquareFrom:_firstPoint to:currentPoint];
_lastPoint = currentPoint;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
currentPoint.y -= 40;
[self drawSquareFrom:_firstPoint to:currentPoint];
[_modifiedImage release];
_modifiedImage = [[UIImage alloc] initWithCGImage:_imageView.image.CGImage];
}
- (void)drawSquareFrom:(CGPoint)firstPoint to:(CGPoint)lastPoint
{
_imageView.image = _modifiedImage;
CGFloat width = lastPoint.x - firstPoint.x;
CGFloat height = lastPoint.y - firstPoint.y;
_path = [UIBezierPath bezierPathWithRect:CGRectMake(firstPoint.x, firstPoint.y, width, height)];
UIGraphicsBeginImageContext( _originalImage.size );
[_imageView.image drawInRect:CGRectMake(0, 0, _originalImage.size.width, _originalImage.size.height)];
_path.lineWidth = 10;
[[UIColor redColor] setStroke];
[[UIColor clearColor] setFill];
[_path fill];
[_path stroke];
_imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
What you are doing right now is creating a paired CGImage and CGBitmapContext for every call to drawSquareFrom:to: and disposing them each time. Instead, create a single paired CGImage and CGBitmapContext that you reuse for each call.
-(void) drawSquareFrom:(CGPoint)from to:(CGPoint)to {
CGContextRef context = [self offscreenContext];
CGRect draw , full = [self offscreenContextBounds];
draw.origin = from;
draw.size.width = to.x - from.x;
draw.size.height = to.y - from.y;
draw = CGRectStandardize( draw );
[[UIColor redColor] setStroke];
[[UIColor clearColor] setFill];
CGContextClearRect( context , full );
CGContextFillRect( context , draw );
CGContextStrokeRectWithWidth( context , draw , 10 );
[_imageView setNeedsDisplay];
}
You create a paired CGImage and CGContext like this, although there are some ... to fill in. All the myX variables are intended to be class members.
-(CGContextRef) offscreenContext {
if ( nil == myContext ) {
size_t width = 400;
size_t height = 300;
CFMutableDataRef data = CFDataCreateMutable( NULL , width * height * 4 ); // 4 is bytes per pixel
CGDataProviderRef provider = CGDataProviderCreateWithCFData( data );
CGImageRef image = CGImageCreate( width , height , ... , provider , ... );
CGBitmapContextRef context = CGBitmapContextCreate( CFDataGetMutableBytePtr( data ) , width , height , ... );
CFRelease( data ); // retained by provider I think
CGDataProviderRelease( provider ); // retained by image
myImage = image;
myContext = context;
myContextFrame.origin = CGPointZero;
myContextFrame.size.width = width;
myContextFrame.size.height = height;
_imageView.image = [UIImage imageWithCGImage:image];
}
return myContext;
}
-(CGRect) offscreenContextBounds {
return myContextFrame;
}
This sets the _imageView.image to the newly created image. In drawSquareFrom:to: I assume that setNeedsDisplay is sufficient to draw the changes to made, but it may be that you need to assign a new UIImage each time that wraps the same CGImage.
Just use two Contexts.
In one you just "preview" draw the Rectangle and you can clear it on TouchesMoved.
On TouchesEnd you finally draw on your original context and then on the Image.