I'm using this code for the moving, scaling and rotating an UIImageView.
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint currentTouch1;
CGPoint currentTouch2;
NSArray *allTouches = [touches allObjects];
UITouch* t;
float scale,rotation;
if([[event allTouches] count]==1){
t=[[[event allTouches] allObjects] objectAtIndex:0];
if (CGRectContainsPoint([Birdie frame], [[allTouches objectAtIndex:0] locationInView:self.view]))
{
touch2=[t locationInView:nil];
Birdie.center=CGPointMake(Birdie.center.x+touch2.x-touch1.x,Birdie.center.y+touch2.y-touch1.y);
touch1=touch2;
}
}
else if([[event allTouches] count]==2)
{
t=[[[event allTouches] allObjects] objectAtIndex:0];
currentTouch1=[t locationInView:nil];
t=[[[event allTouches] allObjects] objectAtIndex:1];
currentTouch2=[t locationInView:nil];
scale = [self distance:currentTouch1 toPoint:currentTouch2] / [self distance:touch1 toPoint:touch2];
rotation=atan2(currentTouch2.y-currentTouch1.y, currentTouch2.x-currentTouch1.x)-atan2(touch2.y-touch1.y,touch2.x-touch1.x);
if(isnan(scale)){
scale=1.0f;
}
NSLog(#"rotation %f",rotation);
NSLog(#"scale %f",scale);
if (CGRectContainsPoint([Birdie frame], [[allTouches objectAtIndex:0] locationInView:self.view]) &&
CGRectContainsPoint([Birdie frame], [[allTouches objectAtIndex:1] locationInView:self.view]))
{
Birdie.transform=CGAffineTransformScale(Birdie.transform, scale,scale);
Birdie.transform=CGAffineTransformRotate(Birdie.transform, rotation);
}
else // In case of scaling or rotating the background imageView
{
imageView.transform=CGAffineTransformScale(imageView.transform, scale,scale);
imageView.transform=CGAffineTransformRotate(imageView.transform, rotation);
}
touch1=currentTouch1;
touch2=currentTouch2;
}
}
-(double)distance:(CGPoint)point1 toPoint:(CGPoint)point2
{
return sqrt(fabs(point1.x - point2.x) + fabs(point1.y - point2.y));
}
However I get one error the whole time, Invalid operands to binary /.
I get this error on the next line:
scale = [self distance:currentTouch1 toPoint:currentTouch2] / [self distance:touch1 toPoint:touch2];
Have you declared distance:toPoint: in the .h files? If not, then the compiler (working strictly top to bottom with just a single pass) doesn't know what type the function returns and assumes something that doesn't work with the division operator.
So most likely, you can solve the problem by either declaring the function in the .h file or moving it before touchesMoved:withEvent:
Related
I have an NSDictionary (allSprites) with a lot of Sprites (spriteX) and in my Touch Method I want to check whether the sprite was touched.
My problem is that it doesn't react to the boundingBox. I don't see my error! Is it a problem with NSDictionary? I get no error or anything... But it doesn't work.
Is there another way to check the boundingBox in an NSDictionary? Can somebody help me?
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in [event allTouches]) {
for (NSValue* value in allSprites) {
CGPoint location = [touch locationInView:touch.view];
location = [[CCDirector sharedDirector] convertToGL:location];
if(CGRectContainsPoint([spriteX boundingBox], location)){
NSLog(#"sprite was touched");
}
}
}
}
you dont seem to refer to spriteX at all in your loop other than in the test for bounding box, it is probably not initialized. maybe you meant to do :
for (CCSprite* spriteX in [allSprites allValues]) {
CGPoint location = [touch locationInView:touch.view];
location = [[CCDirector sharedDirector] convertToGL:location];
if(CGRectContainsPoint([spriteX boundingBox], location)){
NSLog(#"sprite was touched");
}
}
if you need the key of the sprite :
for (NSString*key in [allSprites allKeys]) {
spriteX = [allSprites objectForKey:key];
CGPoint location = [touch locationInView:touch.view];
location = [[CCDirector sharedDirector] convertToGL:location];
if(CGRectContainsPoint([spriteX boundingBox], location)){
NSLog(#"sprite with key %# was touched",key);
}
}
i've 6 uiimageviews, say img1 - img6. when i touch and drag img1, it moves. but as i drag the img1 and when it comes near to other uiimageviews, the img1 stops moving and the img which comes near to it, starts moving. this happens when i drag the image very fast and not when i drag the image slowly. And also the dragging is not so smooth...... :(
Here's what i've done so far...
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
if (CGRectContainsPoint([self.firstImg frame], [touch locationInView:nil]))
{
[self.view bringSubviewToFront:self.firstImg];
self.firstImg.center = [touch locationInView:nil];
}
else if (CGRectContainsPoint([self.secondImg frame], [touch locationInView:nil]))
{
[self.view bringSubviewToFront:self.secondImg];
self.secondImg.center = [touch locationInView:nil];
}
else if (CGRectContainsPoint([self.thirdImg frame], [touch locationInView:nil]))
{
[self.view bringSubviewToFront:self.thirdImg];
self.thirdImg.center = [touch locationInView:nil];
}
else if (CGRectContainsPoint([self.fourthImg frame], [touch locationInView:nil]))
{
[self.view bringSubviewToFront:self.fourthImg];
self.fourthImg.center = [touch locationInView:nil];
}
else if (CGRectContainsPoint([self.fifthImg frame], [touch locationInView:nil]))
{
[self.view bringSubviewToFront:self.fifthImg];
self.fifthImg.center = [touch locationInView:nil];
}
else if (CGRectContainsPoint([self.sixthImg frame], [touch locationInView:nil]))
{
[self.view bringSubviewToFront:self.sixthImg];
self.sixthImg.center = [touch locationInView:nil];
}
}
There are a number of problems with your implementation:
You're passing nil as the view to locationInView:, which means that if you move the superview or support interface rotation, you will get incorrect coordinates.
You're setting the image view's center to the touch location. Because of this, when the user first touches a view, if the touch isn't exactly centered in the view, the view will jump to be centered at the touch location. This is not the behavior users expect.
You're always checking the views in a fixed order, instead of checking the views from front to back. This is why “when it comes near to other uiimageviews, the img1 stops moving and the img which comes near to it, starts moving.” Your code will always move firstImg if the touch is over firstImg, even if the user was dragging secondImg, because you always check firstImg before checking secondImg.
You're repeating yourself a lot. If you have to write the same thing twice, you should think about factoring it out into a separate function or method. If you have to write the same thing three times (or more), you should almost certainly factor it out.
The simplest answer to all of these problems is to stop using touchesMoved:withEvent: and related methods. Instead, add a UIPanGestureRecognizer to each image view:
- (void)makeImageViewDraggable:(UIImageView *)imageView {
imageView.userInteractionEnabled = YES;
UIPanGestureRecognizer *panner = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(imageViewPannerDidFire:)];
[imageView addGestureRecognizer:panner];
}
- (void)imageViewPannerDidFire:(UIPanGestureRecognizer *)panner {
UIView *view = panner.view;
[view.superview bringSubviewToFront:view];
CGPoint translation = [panner locationInView:view];
CGPoint center = view.center;
center.x += translation.x;
center.y += translation.y;
view.center = center;
[panner setTranslation:CGPointZero inView:view];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self makeImageViewDraggable:self.firstImg];
[self makeImageViewDraggable:self.secondImg];
[self makeImageViewDraggable:self.thirdImg];
[self makeImageViewDraggable:self.fourthImg];
[self makeImageViewDraggable:self.fifthImg];
[self makeImageViewDraggable:self.sixthImg];
}
I've searched around on here and see a lot of answers that say to turn on the userInteractionEnabled property. I've already done that.
I create my subviews programmatically (not in Interface Builder). The subviews are a custom subclass of UIView (called PieceSuperClass).
All I want is something that would look like
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView:self.view];
UIView *hitView = [self.view hitTest:currentPosition withEvent:event];
if ([hitView isKindOfClass:[PieceSuperClass class]]) {
return hitView;
}
For some reason, hitView isKindOfClass UIImageView even though I most definitely declared it as a PieceSuperClass. 'PieceSuperClass' is a subclass of UIImageView.
// Draw proper piece
UIImage *pieceImage = [UIImage imageNamed:[NSString stringWithFormat:#"%#%#.png", pieceColor, pieceName]];
PieceSuperClass *pieceImageView = [[PieceSuperClass alloc] initWithFrame:CGRectMake(0, 0, 39, 38)];
pieceImageView.image = pieceImage;
pieceImageView.identifier = [NSString stringWithFormat:#"%#%#%#", pieceColor, pieceName, pieceNumber];
pieceImageView.userInteractionEnabled = YES;
[boardView addSubview:pieceImageView];
I think you are tracking the wrong view with the currectposition. If the PieceSuperClass is a subview of your self.view you should probably track it like:
CGPoint currentPosition = [touch locationInView:self.view.subviews];
That's from the UITouch Documentation:
The view in which the touch initially occurred. (read-only)
#property(nonatomic, readonly, retain) UIView *view
So you can know which view was touched and simply do this:
UIView *hitView = touch.view;
if ([hitView isKindOfClass:[PieceSuperClass class]]) {
return hitView;
}
Figured it out!
- (id)whichPieceTouched:touches withEvent:event
{
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView:self.view];
for (int i = 0; i < [piecesArray count]; i++) {
if (CGRectContainsPoint([[piecesArray objectAtIndex:i] frame], currentPosition)) {
return [piecesArray objectAtIndex:i];
}
}
}
Though I have one lingering problem: it seems to always select the object about 20 pixels below (or the next object down).
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!
SOLVED!
to get coordinates of the sprites frame use sprite.boundingBox.origin.x;
Hello!I am implementing a simple code but I cannot understand its behavior:
anewSprite = [CCSprite spriteWithFile:#"grossini.png"];
anewSprite.position = ccp(80, 80);
[self addChild:anewSprite];
anotherSprite = [CCSprite spriteWithFile:#"grossini.png"];
anotherSprite.position = ccp(300, 80);
[self addChild:anotherSprite];
-(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{
CGPoint point= [touch locationInView:[touch view]];
point = [[CCDirector sharedDirector] convertToGL: point];
NSLog(#"point x:%f y:%f", point.x, point.y);
if (CGRectContainsPoint(anewSprite.textureRect, point)){
NSLog(#"contains point");
} else {
NSLog(#"does not contain");
}
return TRUE;
}
-(void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event{
CGPoint point= [touch locationInView:[touch view]];
point = [[CCDirector sharedDirector] convertToGL: point];
anewSprite.position = point;
if (CGRectContainsRect(anewSprite.textureRect, anotherSprite.textureRect) == TRUE) {
NSLog(#"Intersects");
}
}
The problem is following:
NSLog(#"anotherSpriteTextureRectOrigin X:%f Y:%f", enemy1.textureRect.origin.x, enemy1.textureRect.origin.y);
NSLog(#"anewSpriteTextureRectOrigin X:%f Y:%f", anewSprite.textureRect.origin.x, anewSprite.textureRect.origin.y);
shows:
anotherSpriteTextureRectOrigin X:0.000000 Y:0.000000
anewSpriteTextureRectOrigin X:0.000000 Y:0.000000
Thanks in advance!
To check, whether one sprite intersects/contains another, instead of sprite.frame.rect use sprite.boundingBox
Eg:
if (CGRectContainsRect(sprite1.boundingBox, sprite2.boundingBox)) {
NSLog(#"Contains");
}