hy,
How can i make something that changes color when i touch it in objective c, i want the whole screen to be touchable and want to draw stuff on it with finger.
Everywhere i touch it should change the color.
Whats the best way to do it? I already have touchesMoved implemented to get the coordinates of the touch.
UITouch * touch = [touches anyObject];
CGPoint pos = [touch locationInView: [UIApplication sharedApplication].keyWindow];
Any example code would be nice thanks.
thank you in advance,
i have the following code but it doesnt print anything where i touch
-(void)setPaths:(NSMutableArray *)paths
{
self.paths =[[NSMutableArray alloc]init];
}
-(void)setAPath:(UIBezierPath *)aPath
{
self.aPath=[UIBezierPath bezierPath];
}
-(void) drawRect:(CGRect)rect{
[super drawRect:rect];
[[UIColor blackColor] set];
for (UIBezierPath *path in paths) {
[path stroke];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.paths addObject:self.aPath];
//self.aPath=[UIBezierPath bezierPath];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch * touch = [touches anyObject];
CGPoint pos = [touch locationInView: [UIApplication sharedApplication].keyWindow];
[self.aPath addLineToPoint:[touch locationInView:self]];
// [self.aPath addLineToPoint:[touch locationInView:touch]];
NSLog(#"Position of touch: %.3f, %.3f", pos.x, pos.y);
}
#end
When your touch starts, create a UIBezierPath:
self.myPath = [UIBezierPath bezierPath];
Then, every time the touches moves, add a point to the path:
[self.myPath addLineToPoint:[touch locationInView:self]];
Once your touch has ended, in your drawRect just draw the path:
-(void) drawRect:(CGRect)rect{
[super drawRect:rect];
[[UIColor blueColor] set];
for (UIBezierPath *path in paths) {
[path stroke];
}
}
Find all the doc here:
UIBezierPath Class Reference
Maybe you're going to change your drawRect: method. insert these 2 lines:
[[UIColor blackColor] setStroke];
[path stroke];
and you should add [self setNeedsDisplay]; after you've added a line inside touchesMoved:
I just passed following tutorial, it should solve your problem. It also explains how you can improve your code so it won't get too lazy if you're drawing longer lines:
http://mobile.tutsplus.com/tutorials/iphone/ios-sdk_freehand-drawing/
Related
I have a drawing view on a UIScrollView.
What I want to do is draw lines with one finger, and scrolling with two fingers.
The drawing view is to draw lines through touchesMoved as below.
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch;
CGPoint lastTouch, currentTouch;
for (touch in touches)
{
lastTouch = [touch previousLocationInView:self];
currentTouch = [touch locationInView:self];
CGContextRef ctx = CGLayerGetContext(drawLayer);
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, lastTouch.x, lastTouch.y);
CGContextAddLineToPoint(ctx, currentTouch.x, currentTouch.y);
CGContextStrokePath(ctx);
}
[self setNeedsDisplay];
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
CGContextDrawLayerInRect(drawContext, self.bounds, drawLayer);
CGContextClearRect(CGLayerGetContext(drawLayer), self.bounds);
[self setNeedsDisplay];
}
and on a viewController,
_scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
[_scrollView setContentSize:CGSizeMake(320, 800)];
[self.view addSubview:_scrollView];
_drawingView = [[DrawingView alloc] initWithFrame:CGRectMake(0, 0, 320, 800)];
[_scrollView addSubview:_drawingView];
for (UIGestureRecognizer *gestureRecognizer in _scrollView.gestureRecognizers)
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]])
{
UIPanGestureRecognizer *panGR = (UIPanGestureRecognizer *) gestureRecognizer;
panGR.minimumNumberOfTouches = 2;
}
}
It works ok on simulator however the drawing is too slow on a real device. What is wrong and any suggestion?
Ty!
I solved.
Shouldn't draw whole screen with [self setNeedsDisplay]. Should draw a area where need to redraw with [self setNeedsDisplay withRect:]
Better use panGesture recogniger than touchesBegin~End. There's delay between touchesBegin and touchesEnd.
Here is the relevant .m that I am currently using.
- (void)drawRect:(CGRect)rect
{
[[UIColor redColor] setStroke];
for (UIBezierPath *_path in pathArray)
[_path strokeWithBlendMode:kCGBlendModeNormal alpha:1.0];
}
#pragma mark -
#pragma mark - Touch Methods
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
myPath=[[UIBezierPath alloc]init];
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
if([ud objectForKey:#"lineThickness"] == nil) {
myPath.lineWidth=5;
}
else {
float thicknessFloat = [ud floatForKey:#"lineThickness"];
myPath.lineWidth= 10. * thicknessFloat;
}
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
[myPath moveToPoint:[mytouch locationInView:self]];
[pathArray addObject:myPath];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
if([ud objectForKey:#"lineThickness"] == nil) {
myPath.lineWidth=5;
}
else {
float thicknessFloat = [ud floatForKey:#"lineThickness"];
myPath.lineWidth= 10. * thicknessFloat;
}
[myPath addLineToPoint:[mytouch locationInView:self]];
[self setNeedsDisplay];
}
It works great, but since this is tutorial code that is slightly modified by me, I do not know how to approach the problem of wanting to draw lines between two points, and have the framework connect the the points each time a point is added.
Can anyone please point me in a good direction on how to accomplish this please?
The particulars of how to implement this depend upon the effect that you're looking for. If you're just tapping on a bunch of points and want to add them to a UIBezierPath you can do something like the following in your view controller:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch = [[touches allObjects] objectAtIndex:0];
CGPoint location = [mytouch locationInView:self.view];
// I'm assuming you have a myPath UIBezierPath which is an ivar which is
// initially nil. In that case, we'll check if it's nil and if so, initialize
// it, otherwise, it's already been initialized, then we know we're just
// adding a line segment.
if (!myPath)
{
myPath = [UIBezierPath bezierPath];
[myPath moveToPoint:location];
shapeLayer = [[CAShapeLayer alloc] initWithLayer:self.view.layer];
shapeLayer.lineWidth = 1.0;
shapeLayer.strokeColor = [UIColor redColor].CGColor;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
[self.view.layer addSublayer:shapeLayer];
}
else
{
[myPath addLineToPoint:location];
shapeLayer.path = myPath.CGPath;
}
}
If you wanted something where you can draw with your finger (e.g. dragging your finger draws), then it might look something like:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch = [[touches allObjects] objectAtIndex:0];
CGPoint location = [mytouch locationInView:self.view];
myPath = [UIBezierPath bezierPath];
[myPath moveToPoint:location];
shapeLayer = [[CAShapeLayer alloc] initWithLayer:self.view.layer];
shapeLayer.lineWidth = 1.0;
shapeLayer.strokeColor = [UIColor redColor].CGColor;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
[self.view.layer addSublayer:shapeLayer];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch = [[touches allObjects] objectAtIndex:0];
CGPoint location = [mytouch locationInView:self.view];
[myPath addLineToPoint:location];
shapeLayer.path = myPath.CGPath;
}
I would not use UIBezierPath since it is intended more for drawing curved paths.
The most efficient way to accomplish this would be to use core graphics draw commands within drawRect while using an array to store the points you want to draw; this array is appended to in your touch methods.
- (void)drawRect:(CGRect)rect {
CGContextRef c = UIGraphicsGetCurrentContext();
CGFloat black[4] = {0, 0,
0, 1};
CGContextSetStrokeColor(c, black);
CGContextBeginPath(c);
CGContextMoveToPoint(c, 100, 100);
CGContextAddLineToPoint(c, 100, 200); //call this in a loop that goes through the point array
CGContextStrokePath(c);
}
There is much more information here: Quartz 2D Programming Guide
Hope this helps!
In cocos2d game development, CGRectContainsPoint method often used to detect if touch on a CCSprite.
I use code fllow to get a sprite's (which in a CCNode) rect property
- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {
CCLOG(#"ccTouchEnded");
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
CCLOG(#"location.x:%f, y:%f", location.x, location.y);
CGRect rect;
rect = CGRectMake(self.firstCard.face.position.x-(self.firstCard.face.contentSize.width/2), self.firstCard.face.position.y-(self.firstCard.face.contentSize.height/2),
self.firstCard.face.contentSize.width, self.firstCard.face.contentSize.height);
if (CGRectContainsPoint(rect, location)) {
CCLOG(#"first card touched");
[firstCard open];
}
rect = CGRectMake(self.secondCard.face.position.x-(self.secondCard.face.contentSize.width/2), self.secondCard.face.position.y-(self.secondCard.face.contentSize.height/2),
self.secondCard.face.contentSize.width, self.secondCard.face.contentSize.height);
if (CGRectContainsPoint(rect, location)) {
CCLOG(#"second card touched");
[secondCard open];
}
}
I want to know if there is a convenient way to get a CCSprite 's rect straightforward?
Please use boundingBox i think it will be a great option to use.
Like this:
- ( void ) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
locationTouchBegan = [touch locationInView: [touch view]];
//location is The Point Where The User Touched
locationTouchBegan = [[CCDirector sharedDirector] convertToGL:locationTouchBegan];
//Detect the Touch On sprite
if(CGRectContainsPoint([sprite boundingBox], locationTouchBegan))
{
isSpriteTouched=YES;
}
}
Kobold2D has a convenience method containsPoint as a CCNode extension (Objective-C category) which you can replicate in your project:
-(BOOL) containsPoint:(CGPoint)point
{
CGRect bbox = CGRectMake(0, 0, contentSize_.width, contentSize_.height);
CGPoint locationInNodeSpace = [self convertToNodeSpace:point];
return CGRectContainsPoint(bbox, locationInNodeSpace);
}
Your code then be simplified to this and it will work with rotated and/or scaled sprites as well (the boundingBox method fails to test rotated and scaled sprites correctly).
if ([firstCard.face containsPoint:location]) {
CCLOG(#"first card touched");
}
quick question (which might be a no-brainer for most here) :)
My code below should draw a circle for every time touch that is recognised but although more than ones touches are sensed only one circle will drawn up at a time.
Can anyone see any obvious issues?
This method sits in the XYZViewControler.m class.
TouchPoint.m is the class that defines the circle.
Thanks a bundle for your help and redirects.
Chris
- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *)event {
NSSet * allTouches = [event allTouches]; // get all events
for (UITouch * touch in touches) {
TouchPoint * touchPoint = [[TouchPoint alloc] initWithFrame:CGRectMake(0, 0, circleWidth, circleWidth)];
touchPoint.center = [touch locationInView:[self view]];
touchPoint.color = [UIColor redColor];
touchPoint.backgroundColor = [UIColor whiteColor];
[[self view] addSubview: touchPoint];
[touchPoint release];
CFDictionarySetValue(touchMap, touch , touchPoint);
}
[[self view] setNeedsDisplay];
}
code if fine! One has to enable multitouch for the view in order to make it work!
#property(nonatomic, getter=isMultipleTouchEnabled) BOOL multipleTouchEnabled
I have a UIView where I set the background color to clear in the init method. I then draw a blue line between 2 points in drawRect. The first point is the very first touch on the view, and the second point is changed when touchesMoved is called.
So that one end of the line is fixed, and the other end moves with the users finger. However when the backgroundcolor is clear the line has quite a big of delay and skips (is not smooth).
If I change the background color to black (just uncommenting the setbackgroundcolor line) then the movement is much smoother and it looks great, apart from you can't see the views behind it.
How can I solve this issue? (on the iPad, I only have the simulator, and haven't currently got access to a device) So that the performance is smooth, but the background is clear so you can see the background views.
-(id) initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
//self.backgroundColor = [UIColor clearColor];
}
return self;
}
-(void) drawRect:(CGRect)rect {
if (!CGPointEqualToPoint(touchPoint, CGPointZero)) {
if (touchEnded) {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
} else {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
CGContextSetLineWidth(context, 3.0f);
CGFloat blue[4] = {0.0f, 0.659f, 1.0f, 1.0f};
CGContextSetAllowsAntialiasing(context, YES);
CGContextSetStrokeColor(context, blue);
CGContextMoveToPoint(context, startPoint.x, startPoint.y);
CGContextAddLineToPoint(context, touchPoint.x, touchPoint.y);
CGContextStrokePath(context);
CGContextSetFillColor(context, blue);
CGContextFillEllipseInRect(context, CGRectMakeForSizeAroundCenter(CGSizeMake(10, 10), touchPoint));
CGContextFillEllipseInRect(context, CGRectMakeForSizeAroundCenter(CGSizeMake(10, 10), startPoint));
}
}
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
self.touchEnded = NO;
if ([[touches allObjects] count] > 0)
startPoint = [[[touches allObjects] objectAtIndex:0] locationInView:self];
touchPoint = [[[touches allObjects] objectAtIndex:0] locationInView:self];
[self setNeedsDisplay];
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
self.touchEnded = NO;
if ([[touches allObjects] count] > 0)
touchPoint = [[[touches allObjects] objectAtIndex:0] locationInView:self];
[self setNeedsDisplay];
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
self.touchEnded = YES;
[self setNeedsDisplay];
[self removeFromSuperview];
}
- (void)dealloc {
[super dealloc];
}
First of all, you should test performance on a device, rather than in Simulator. Most of graphics-related stuff works very differently in Simulator, some being much faster and some, surprisingly, much slower.
That said, if a transparent background does prove to be a performance problem on a device, it's most likely because you fill too many pixels with CGContextClearRect(). I can think of two solutions off the top of my head:
In touchesBegan:withEvent: you can make a snapshot of the underlying view(s) using CALayer's renderInContext: method and draw the snapshot as the background instead of clearing it. That way you can leave the view opaque and thus save time on compositing the view with views underneath it.
You can create a one-point-tall CALayer representing a line and transform it in touchesMoved:withEvent: so that its ends are in correct positions. Same goes for dots representing the line ends (CAShapeLayer is great for that). There will be some ugly trigonometry involved, but the performance is going to be excellent.