Draggable image from class file - ios

I understand how to make a draggable image, but I am having trouble making an image from a class file be draggable from my main ViewController.m file. My class is called "bomb". Every two seconds, a new bomb is created, and with it a bombImage (an object of bomb). The bomb is added to an NSMutableArray (bombArray).
- (void) newBomb
{
bomb *bomb1 = [[bomb alloc] init];
[bombArray addObject: bomb1];
[bomb1 displayBombOnView:self.view]; //displayBombOnView just makes a new bomb in a random location
}
I am trying to make it so that the user can drag each "bomb" around.
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event : (bomb *) bomb
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view];
bomb->bombImage.center = location;
}
-(void) touchesMoved:(NSSet*)touches withEvent:(UIEvent *)event
{
bomb *tempBomb = [[bomb alloc] init];
arrayCount = [bombArray count];
for (int k = 0; k<arrayCount; k++)
{
tempBomb = [bombArray objectAtIndex:k];
CGPoint tappedPt = [[touches anyObject] locationInView: self];
int xPos = tappedPt.x;
int yPos = tappedPt.y;
if ((xPos >= tempBomb->bombImage.center.x - 25 && xPos <= tempBomb->bombImage.center.x + 25) && (yPos >= tempBomb->bombImage.center.y - 25 && xPos <= tempBomb->bombImage.center.y + 25))
{
[self touchesBegan:touches withEvent:event : [bombArray objectAtIndex:k]];
break;
}
}
}
It builds, but then when I try to drag the image, it crashes, saying Thread 1: signal SIGABRT. Any help would be greatly appreciated.

Instead of what you're doing, use a UIPanGestureRecognizer. Add a recognizer to each bomb image view when you create it and then when the gesture recognizer calls your action method you can directly access the view and use the recognizer position to move the view.

Related

How to move array of UIImageViews smoothly?

Am working on kind of seating arrangement application. Where the app allows the admin to arrange the seatings by click and move the each seat and then save the seating arrangement with frame sizes of each seat. I tried by loading 5 UIImageView on custom view and move the imageview but, only one imageview is getting moved. Rest of the imageviews not getting moved. Can you please help me on this?
- (void) addSeatsToTheView {
for (int i=0; i<5; i++) {
self.hotelImage = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, 50, 50)];
self.hotelImage.image = [UIImage imageNamed:#"DiningIcon"];
self.hotelImage.userInteractionEnabled = YES;
[self.hotelImage setUserInteractionEnabled:YES];
self.hotelImage.tag = i;
[self.hotelView addSubview:self.hotelImage];
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if (touch.view == self.hotelImage) {
CGPoint location = [touch locationInView:self.hotelView];
NSLog(#"Begen Touch Location: %#", NSStringFromCGPoint(location));
self.hotelImage.frame=CGRectMake(location.x, location.y, 50, 50);
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if (touch.view == self.hotelImage) {
CGPoint location = [touch locationInView:self.hotelView];
NSLog(#"Begen Touch Location: %#", NSStringFromCGPoint(location));
self.hotelImage.frame=CGRectMake(location.x, location.y, 50, 50);
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if (touch.view == self.hotelImage) {
CGPoint location = [touch locationInView:self.hotelView];
NSLog(#"Begen Touch Location: %#", NSStringFromCGPoint(location));
self.hotelImage.frame=CGRectMake(location.x, location.y, 50, 50);
}
}
Once the admin arranged the seatings we have to save all the visible uiimageview framesizes. Thanks.
Edited:
I tried the using UIPanGestureRecognizer but, only one imageview getting moved. Don't know where am doing wrong? Can you please help me? Thanks.
- (void) addSeatsToTheView {
for (int i=0; i<5; i++) {
self.hotelImage = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, 50, 50)];
self.hotelImage.image = [UIImage imageNamed:#"DiningIcon"];
self.hotelImage.userInteractionEnabled = YES;
[self.hotelImage setUserInteractionEnabled:YES];
self.hotelImage.tag = i;
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(gestureRecognizerMethod:)];
[self.hotelImage addGestureRecognizer:panGestureRecognizer];
[self.hotelView addSubview:self.hotelImage];
}
}
- (void)gestureRecognizerMethod:(UIPanGestureRecognizer *)recogniser
{
if (recogniser.state == UIGestureRecognizerStateBegan || recogniser.state == UIGestureRecognizerStateChanged)
{
CGPoint touchLocation = [recogniser locationInView:self.hotelView];
self.hotelImage.frame = CGRectMake(touchLocation.x, touchLocation.y, 50, 50);
}
}
Don't use touches methods for this; you'll go insane. Make each image view user-interactive and give it a UIPanGestureRecognizer. There is standard code for the pan gesture recognizer's action handler for following a finger, i.e. making the view draggable.
Once you've done that, everything stems from your misuse of self.hotelImage. Get rid of it entirely. In your for loop, just use a local variable, UIImageView* hotelImage = .... In the gesture recognizer method, use [recogniser view] to refer to the image view to which this gesture recognizer is attached. Everything will sort itself out after that!

Speed of touch varies with number of objects on Scene

I am creating game in which user can hit the object falling from the top of the screen with the racket. user can continuously move the racket but if it is at minimal speed or is at rest it should not hit the object, but if it above the minimal speed user should hit them. I have achieved that but the issue is when user start touching the racket which continously move with the user touch, the speed varition is their it does not start with the same speed and while touch is moving at that time also some times speed is very less even though the movement is fast. Here is my piece of code
-(void)didMoveToView:(SKView *)view {
self.physicsWorld.contactDelegate = (id)self;
racketNode = [SKSpriteNode spriteNodeWithImageNamed:#"racket"];
racketNode.size = CGSizeMake(50,50);
racketNode.position = CGPointMake(self.frame.origin.x + self.frame.size.width - 50,50);
racketNode.name = #"racket";
[self addChild:racketNode];
}
-(void) didBeginContact:(SKPhysicsContact *)contact {
SKSpriteNode *nodeA = (SKSpriteNode *)contact.bodyA.node ;
SKSpriteNode *nodeB = (SKSpriteNode *) contact.bodyB.node;
if (([nodeA.name isEqualToString:#"racket"] && [nodeB.name isEqualToString:#"fallingObject"])) {
if (racketNode.speed > kMinSpeed)
[nodeB removeFromParent];
else {
nodeB.physicsBody.contactTestBitMask = 0;
[self performSelector:#selector(providingCollsion:) withObject:nodeB afterDelay:0.1];
}
}
}
-(void) providingCollsion:(SKSpriteNode *) node {
node.physicsBody.contactTestBitMask = racketHit;
}
-(void) touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
start = location;
startTime = touch.timestamp;
racketNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:racketNode.frame.size];
racketNode.physicsBody.categoryBitMask = racket;
racketNode.physicsBody.contactTestBitMask = HitIt;
racketNode.physicsBody.dynamic = NO;
racketNode.physicsBody.affectedByGravity = NO;
[racketNode runAction:[SKAction moveTo:location duration:0.01]];
}
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
racketNode.physicsBody = nil;
racketNode.speed = 0;
}
-(void) touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
CGFloat dx = location.x - start.x;
CGFloat dy = location.y - start.y;
CGFloat magnitude = sqrt(dx*dx+dy*dy);
// Determine time difference from start of the gesture
CGFloat dt = touch.timestamp - startTime;
// Determine gesture speed in points/sec
CGFloat speed = magnitude/dt;
racketNode.speed = speed;
[handNode runAction:[SKAction moveTo:[touch locationInNode:self] duration:0.01]];
}
Please tell me which part my code is wrong so as to make same object collide with high speed only not on slow speed and also no collision on stable state.
Instead of doing it manually, use UIPanGestureRecognizer to handle your swipes. With it, there is a velocity property that you can use to check if the speed is greater than a given value.
Here is a great tutorial to do it:
https://www.raywenderlich.com/76020/using-uigesturerecognizer-with-swift-tutorial

Superview does not receive the touch action

I have one base view A with size of (0,0,320,280), it contain another view B with size of
(100,100,50,50) and View B has one button and one image view(C) as sub view. image view frame is same as view B, button has added in B's top left corner.
My requirement is when we drag the B's bottom right corner its size has to increased or decreased.
if we drag the B from any other place except bottom right corner it has to move. view size should not be modified.
My problem is view B does not receive the touch action.
i added the code below. please guide me.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
//baseVw-------> view B//
if ([touch view]==baseVw)
{
touchStart = [[touches anyObject] locationInView:baseVw];
isResizingLR = (baseVw.bounds.size.width - touchStart.x < kResizeThumbSize && baseVw.bounds.size.height - touchStart.y < kResizeThumbSize);
isResizingUL = (touchStart.x <kResizeThumbSize && touchStart.y <kResizeThumbSize);
isResizingUR = (baseVw.bounds.size.width-touchStart.x < kResizeThumbSize && touchStart.y<kResizeThumbSize);
isResizingLL = (touchStart.x <kResizeThumbSize && baseVw.bounds.size.height -touchStart.y <kResizeThumbSize);
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchPoint = [[touches anyObject] locationInView:baseVw];
CGPoint previous=[[touches anyObject]previousLocationInView:baseVw];
float deltaWidth = touchPoint.x-previous.x;
float deltaHeight = touchPoint.y-previous.y;
if ([touch view]==baseVw)
{
if (isResizingLR)
{
baseVw.frame = CGRectMake(baseVw.frame.origin.x, baseVw.frame.origin.y,touchPoint.x + deltaWidth, touchPoint.y + deltaWidth);
}
else
{
CGPoint activePoint = [[touches anyObject] locationInView:baseVw];
// Determine new point based on where the touch is now located
CGPoint newPoint = CGPointMake(baseVw.center.x + (activePoint.x - touchStart.x),
baseVw.center.y + (activePoint.y - touchStart.y));
//--------------------------------------------------------
// Make sure we stay within the bounds of the parent view
//--------------------------------------------------------
float midPointX = CGRectGetMidX(baseVw.bounds);
// If too far right...
if (newPoint.x > baseVw.superview.bounds.size.width - midPointX)
newPoint.x = baseVw.superview.bounds.size.width - midPointX;
else if (newPoint.x < midPointX) // If too far left...
newPoint.x = midPointX;
float midPointY = CGRectGetMidY(baseVw.bounds);
// If too far down...
if (newPoint.y > baseVw.superview.bounds.size.height - midPointY)
newPoint.y = baseVw.superview.bounds.size.height - midPointY;
else if (newPoint.y < midPointY) // If too far up...
newPoint.y = midPointY;
// Set new center location
baseVw.center = newPoint;
}
}
}
View B probably does not receive any touch events because these are absorbed by the subview C. Assuming you implemented the touch methods in B you should also make sure that you set B as the firstResponder. Implement this:
-(BOOL)canBecomeFirstResponder
{
return YES;
}
And then call [self becomeFirstResponder]; after you add C as the subview. The code inside the touchesBegan/Moved doesn't matter if they are not being called (touch events not received).

iOS touchesBegan issue

I have an NSArray whit 15 UIImageViews:
#interface ViewController : UIViewController{
NSArray *ArrayImages1;
NSArray *ArrayImages2;
}
in viewDidLoad:
ArrayImages1 = [[NSArray alloc] initWithObjects: a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, nil];
Where a1, a2... are the outlets of the UIImageViews
And the same for ArrayImages2
TouchesBegan and TouchesMoved:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self.view];
UIImageView *posible;
int imagen;
if (point.x < 161) {
imagen = Contador1;
if (Contador1 > 14) {imagen = Contador1 - 15;}
imagen--;
posible = [ArrayImages1 objectAtIndex:imagen];
}
else if (point.x > 159) {
imagen = Contador2;
if (Contador2 > 14) {imagen = Contador2 - 15;}
imagen--;
posible = [ArrayImages2 objectAtIndex:imagen];
}
if ([touch view] != posible) { return; }
original2 = posible.center;
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self.view];
UIImageView *posible;
int imagen;
if (point.x < 161) {
imagen = Contador1;
if (Contador1 > 14) {imagen = Contador1 - 15;}
imagen--;
posible = [ArrayImages1 objectAtIndex:imagen];
}
else if (point.x > 159) {
imagen = Contador2;
if (Contador2 > 14) {imagen = Contador2 - 15;}
imagen--;
posible = [ArrayImages2 objectAtIndex:imagen];
}
if ([touch view] != posible) { return; }
posible.center = point;
}
I have to know if the touch was in one of the two posible UIImageViews, and if it was, move it.
The Contador1 and Contador2 ints, are counters that counts how many UIImageView are visible, the user only can move the last 2 of them.
It works, the thing is when i touch outside, it makes the app crash.
If i change in touchesBegan and TouchesMoved, the index of "posible = [ArrayImage..", for 0, it only works for the first UIImageView (i understand why), but it doesent crash.
Any ideas?
I have to know if the touch was in one of the two possible UIImageViews
I can't follow your code very well but it seems like you could simplify things if you first check if the point is within the rect of a view. Then base your logic off of the result. Something like
Using CGRectContainsPoint:
bool CGRectContainsPoint (
CGRect rect,
CGPoint point
);
UIImageView *foundView
for(UIImageView* theView in ArrayImages1){
if (CGRectContainsPoint(theView.frame, touchPoint){
foundView = theView;
break;
}
}
Then...
if (foundView != nil){
// do some logical thing
}
Or...
if ([foundView isEqual:someOtherView]){
// I may have the syntax wrong on the above
}

Detect diagonal swipe gestures in a UIView

I want to detect a two-finger diagonal swipe that starts from the bottom right of the screen to the middle. I tried adding UISwipeGestureRecognizer with direction set as "UISwipeGestureRecognizerDirectionUp | UISwipeGestureRecognizerDirectionLeft" but to no vail as the handler is getting invoked even if I start the swipe from the middle of the screen to the top left.
Do I need to sub-class UIGestureRecognizer or can I handle this using touchesBegan and touchesMoved ?
You need to subclass it to use touchesBegan and touchesMoved anyway. I'd do UISwipeGestureRecognizerDirectionUp | UISwipeGestureRecognizerDirectionLeft like you said, then set up two CGRects, one for acceptable starting points, one for acceptable end points. Then use the UIGestureRecognizerDelegate method gestureRecognizer:shouldReceiveTouch:, and check if the touch point is in the acceptable start rect by using CGRectContainsPoint(). Then (I think) in your UISwipeGestureRecognizer subclass, override touchesEnded:withEvent:, and check if the end touch is in the acceptable rect. If it's not, set the state to UIGestureRecognizerStateCancelled (or however you're supposed to cancel a gesture). Also make sure the view it's attached to has its multipleTouchEnabled property set. I haven't actually tried this, but something of the sort should do it. Good luck!
EDIT
Actually, if you didn't want to have to worry about specific rect values for the acceptable start/end points, and make it device independent, you could do this:
//swap in whatever percentage of the screen's width/height you want in place of ".75f"
//set the origin for acceptable start points
CGFloat startRect_x = self.view.frame.size.width * .75f;
CGFloat startRect_y = self.view.frame.size.height * .75f;
//set the size
CGFloat rectWidth = self.view.frame.size.width - startRect_x;
CGFloat rectHeight = self.view.frame.size.height - startRect_y;
//make the acceptable start point rect
CGRect startRect = CGRectMake(startRect_x, startRect_y, rectWidth, rectHeight);
//set the origin for the accepable end points
CGFloat endRect_x = self.view.center.x - rectWidth/2;
CGFloat endRect_y = self.view.center.y - rectHeight/2;
//make the acceptable end point rect
CGRect endRect = CGRectMake(endRect_x, endRect_y, rectWidth, rectHeight);
the touches method of cause can do every Gesture, the Gesture is supported since ios3.2.
i can give you a simple code , you maybe modify by yourself.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
isTwoFingersBegin = NO;
isTwoFingersEnd = NO;
UITouch *touch = [touches anyObject];
UIView *touchView = [touch view];
if ([touches count] == 2)
{
isTwoFingersBegin = YES;
}
touchStartPoint = [touch locationInView:self.view];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
isSwip = YES;
}
#define TOUCH_MOVE_EFFECT_DIST 30
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
UIView *touchView = [touch view];
touchEndPoint = [touch locationInView:self.view];
if ([touches count] == 2)
{
isTwoFingersEnd = YES;
}
CGPoint deltaVector = CGPointMake(touchEndPoint.x - touchStartPoint.x, touchEndPoint.y - touchStartPoint.y);
if (fabsf(deltaVector.x) > TOUCH_MOVE_EFFECT_DIST
&&fabsf(deltaVector.y) > TOUCH_MOVE_EFFECT_DIST
&& isSwip &&isTwoFingersBegin&&isTwoFingersEnd) {
theSwipGesture=RightUpGesture;
}
else if (fabsf(deltaVector.x) <- TOUCH_MOVE_EFFECT_DIST
&& fabsf(deltaVector.y) <- TOUCH_MOVE_EFFECT_DIST
&& isSwip&&isTwoFingersBegin&&isTwoFingersEnd) {
theSwipGesture=LeftDownGesture;
}
isSwip = NO;
}
You can use a UIPanGestureRecognizer
- (void)viewPanned:(UIPanGestureRecognizer *)panGR
{
CGPoint translation = [panGR translationInView:self];
CGFloat threshold = self.bounds.size.width/2;
// Detect
Direction direction = DirectionUnknown;
if (translation.x > threshold) {
if (translation.y > threshold) {
direction = DirectionBottomRight;
} else if (translation.y < -threshold) {
direction = DirectionTopRight;
} else {
direction = DirectionRight;
}
} else if (translation.x < -threshold) {
if (translation.y > threshold) {
direction = DirectionBottomLeft;
} else if (translation.y < -threshold) {
direction = DirectionTopLeft;
} else {
direction = DirectionLeft;
}
} else {
if (translation.y > threshold) {
direction = DirectionBottom;
} else if (translation.y < -threshold) {
direction = DirectionTop;
}
}
}
One possible solution would be to map off a group of coordinates on the iPhone's screen where the swipe could potentially start, and another where it could potentially end. Then in the touchesBegan: and touchesMoved: methods, you could compare the starting and ending points of the swipe and determine whether it qualified as diagonal.
Thanks Guys!
I ended up writing custom code in touchesBegan, touchesMoved and touchesEnded and ot works like charm.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if ([touches count] == 2) {
CGPoint nowPoint = [[touches anyObject] locationInView:self];
if( nowPoint.x >= ALLOWED_X)
swipeUp = YES;
}
[super touchesBegan:touches withEvent:event];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
if ([touches count] == 2 && swipeUp) {
CGPoint nowPoint = [[touches anyObject] locationInView:self];
CGPoint prevPoint = [[touches anyObject] previousLocationInView:self];
if( nowPoint.x <= prevPoint.x && nowPoint.y <= prevPoint.y){
}
else {
swipeUp = NO;
}
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
CGPoint nowPoint = [[touches anyObject] locationInView:self];
if ([touches count] == 2 && swipeUp && nowPoint.x <= DELTA_X && nowPoint.y <= DELTA_Y) {
NSLog(#"Invoke Method");
}
swipeUp = NO;
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesCancelled:touches withEvent:event];
swipeUp = NO;
}

Resources