I have read here a few examples about that, but couldn't make it work.
I have some UIView called Wheel , in which i want to rotate while touch and drag.
It should work like a real wheel, so if i touch at some point it will NOT rotate to that point, only when i drag my finger, i will get rotation .
I tried this,but i get a full rotation for every little move i do. I would like to ADD the angel to the view, not ROTATE to the new angel ..
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchPoint = [touch locationInView:self];
CGPoint center=wheel.center;
float xLeg = fabs(center.x - touchPoint.x);
float yLeg = fabs(center.y-touchPoint.y);
float angle = atan(xLeg / yLeg);
NSLog(#"%f",angle);
wheel.transform = CGAffineTransformMakeRotation( angleInDegree );
}
This should do the trick. Notice how I store the first touch point in touchesBegan:withEvent:. This allows me to subtract the first touch point from every touch point I get while dragging, and thus gives me the real angle I need to rotate.
EDIT
I've done some adjustments, so that it is now smooth and regular even after several touches.
Demo
WheelViewController.m
#interface WheelViewController () {
CGPoint startPoint;
CGFloat startAngle;
CGFloat endAngle;
}
#end
#implementation WheelViewController
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
startPoint = [touch locationInView:self.view];
startAngle = [self angleForTouchPoint:startPoint] - endAngle;
NSLog(#"\r\nStart angle: %f", startAngle);
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchPoint = [touch locationInView:self.view];
CGFloat touchAngle = [self angleForTouchPoint:touchPoint];
NSLog(#"Touch angle: %f", touchAngle);
self.wheel.transform = CGAffineTransformMakeRotation(fmodf(touchAngle - startAngle, 2 * M_PI));
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchPoint = [touch locationInView:self.view];
endAngle = [self angleForTouchPoint:touchPoint] - startAngle;
NSLog(#"End angle: %f", endAngle);
}
- (CGFloat)angleForTouchPoint:(CGPoint)touchPoint {
CGPoint center = self.wheel.center;
float xLeg = touchPoint.x - center.x;
float yLeg = touchPoint.y - center.y;
float angle = fmodf(atan(yLeg/xLeg), (2 * M_PI));
if (xLeg < 0) {
angle += M_PI;
}
return angle;
}
Related
I have an ImageView that moves around the UIView, is it possible to detect collision of the ImageView and the view its self? For example the ImageView hits the side of the view I want it to run an action. -(void)restart {}
If this is possible can you detect which side it has collided with?
You can create a custom UIImageVIew and implement the methods touchBegan and touchMoved (don't forget to add [self setUserInteractionEnabled:YES] in your init method). Then set the rect you want to interact with :
customImageView.interactRect = myView.frame;
And in your customImageView you can add something like:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
lastPosition = [touch locationInView: self.superview];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
CGPoint position = [touch locationInView:self.superview];
CGRect currentFrame = self.frame;
currentFrame.origin = CGPointMake(currentFrame.origin.x + position.x - lastPosition.x, currentFrame.origin.y + position.y - lastPosition.y);
if (CGRectIntersectsRect(currentFrame, interactRect) && !CGRectIntersectsRect(self.frame, interactRect))
{
NSLog(#"I'm in for the first time");
if(self.frame.origin.x + self.frame.size.width <= interactRect.origin.x && currentFrame.origin.x + currentFrame.size.width > interactRect.origin.x)
{
NSLog(#"Coming from the left");
}
if(self.frame.origin.x >= interactRect.origin.x + interactRect.size.width && currentFrame.origin.x < interactRect.origin.x + interactRect.size.width)
{
NSLog(#"Coming from the right");
}
}
self.frame = currentFrame;
lastPosition = position;
}
Want to drag a SKSpriteNode (follow your finger movements) with SKPhysics (collision enabled)
2 failed solutions:
1)
a) Solution: Change the position of the SKSpriteNode
b) Problem: The SKSpriteNode will "magically" pass through a wall unexpectedly when user drags the spriteNode quickly through the wall (Physics seems not to work)
c) code:
#property (nonatomic) CGPoint translationBy;
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
[self selectNodeForTouch:positionInScene];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
CGPoint previousPosition = [touch previousLocationInNode:self];
CGPoint translation = CGPointMake(positionInScene.x - previousPosition.x, positionInScene.y - previousPosition.y);
[self panForTranslation:translation];
}
- (void)panForTranslation:(CGPoint)translation {
CGPoint position = [_selectedNode position];
if([[_selectedNode name] isEqualToString:PLAYER_NAME]) {
[_selectedNode setPosition:CGPointMake(position.x + translation.x, position.y + translation.y)];
}
}
2) After searching around here found this advice by #LearnCocos2D "When you use physics, stick to moving the physics body through force, impulse or changing the body velocity directly." So I tried:
a) Solution: Apply impulse on the sprite node.
b) Problem: The sprite node will keep on moving even if I didn't drag the sprite node. (Behaving like a floating boat. Expect it to act like a car once the drag is stopped the sprite node will stop; once drag begins it will follow the finger.)
c) Code:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
[self selectNodeForTouch:positionInScene];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
CGPoint previousPosition = [touch previousLocationInNode:self];
CGPoint translation = CGPointMake(positionInScene.x - previousPosition.x, positionInScene.y - previousPosition.y);
CGVector forceVector = CGVectorMake(translation.x, translation.y);
[self.player.physicsBody applyImpulse:forceVector];
//[self.player.physicsBody setVelocity:CGVectorMake(0, 0)];
}
-->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Updated:
From the comment:
"i) You could determine the distance of the node to the finger location, then set the velocity every update: so that it will exactly be on the finger's position the next frame. ii) If both finger and node position are identical this will cancel out any movement, otherwise the node will stick to your finger while performing all physics collisions and will continue to push objects between itself and the finger in cases where it is obstructed."
a) Question: How to disable flick?
b) Problem: The sprite node will move very far away with a minor (accidental) flick.
c) code:
#property (nonatomic) CGPoint translationBy;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
CGPoint previousPosition = [touch previousLocationInNode:self];
// 1) You could determine the distance of the node to the finger location,
CGPoint xyTranslation = CGPointMake(positionInScene.x - previousPosition.x, positionInScene.y - previousPosition.y);
if (fabsf(positionInScene.x - previousPosition.x) < kPlayerMovementThreshold) {
xyTranslation.x = 0;
}
if (fabsf(positionInScene.y - previousPosition.y) < kPlayerMovementThreshold) {
xyTranslation.y = 0;
}
self.translationBy = xyTranslation;
}
static const CGFloat kPlayerMovementSpeed = 55.0f;
static const CGFloat kPlayerMovementThreshold = 1.0f;
-(void)update:(CFTimeInterval)currentTime{
// ii) then set the velocity every update: so that it will exactly be on the finger's position the next frame.
CGFloat dx = self.translationBy.x*kPlayerMovementSpeed;
CGFloat dy = self.translationBy.y*kPlayerMovementSpeed;
CGVector velocityVector = CGVectorMake(dx,dy);
[self.player.physicsBody setVelocity:velocityVector];
}
What I do is make an SKNode, let's call it 'a', with a very small physics body attached to it. This new node is connected via an SKPhysicsJointSpring to the node I want to move, let's call that 'b'. The new node a tracks my finger movement, and the spring makes sure the target node b follows that. Because the node b is positioned using physics, the collision detection works as expected.
This is my first post so go easy haha.
I'm new to 'iOS' 'coding', 'Xcode' and 'spritekit'. I'm looking to make an image node "jump" a distance on the positive y-axis if I touch anywhere on the screen, although if I touch somewhere to the left or right if the image and hold for a certain time, it moves in the respective left or right direction, a distance respective to the length of the touch.
Not sure if that's very clear, but any help would be appreciated! Thanks!
You could move a node like this
In touchesEnded: or touchesBegan: method:
{
node.position.y += 50;
}
In order for sprite to move somewhere you could also use actions, there is a family of actions like moveTo action.
I would suggest picking upa tutorial or a book about sprite kit. Good site with free tutorials is raywenderlich.com
Please Try this code :
a sprite move : left/right according to your touch direction anywhere on screen
// CGPoint initialPos; // in .h file
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchPt = [touch locationInView:touch.view];
initialPos = [[CCDirector sharedDirector] convertToGL:touchPt];
}
-(void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *myTouch=[touches anyObject];
CGPoint currentPos=[myTouch locationInView:[myTouch view]];
currentPos=[[CCDirector sharedDirector] convertToGL:currentPos];
float diffX = currentPos.x - initialPos.x;
float diffY = currentPos.y - initialPos.y;
CGPoint velocity = ccp(diffX, diffY);
initialPos = currentPos;
[Sprite setPosition:ccpAdd([SmileBall position], velocity)];
}
- (void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *myTouch=[touches anyObject];
CGPoint currentPos=[myTouch locationInView:[myTouch view]];
currentPos=[[CCDirector sharedDirector] convertToGL:currentPos];
float diffX = currentPos.x - initialPos.x;
float diffY = currentPos.y - initialPos.y;
CGPoint velocity = ccp(diffX, diffY);
initialPos = currentPos;
[Sprite setPosition:ccpAdd([SmileBall position], velocity)];
}
a sprite jump up: according to your touch on screen
-(void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CCSprite *RunningChar;
RunningChar=(CCSprite *)[self getChildByTag:10];
UITouch *touch = [touches anyObject];
CGPoint touchPt = [touch locationInView:touch.view];
touchPt = [[CCDirector sharedDirector] convertToGL:touchPt];
float radian = ccpToAngle(ccpSub(touchPt, prevPoint));
float degrees = CC_RADIANS_TO_DEGREES(radian);
if (degrees >= 22.5 && degrees <= 112.5) // for upward direction :). otherwise you can write only code without ant condition
{
CCJumpTo *jump=[CCJumpTo actionWithDuration:0.7 position:CGPointMake(RunningChar.position.x, 87) height:110 jumps:1];
[RunningChar runAction:seq];
}
else
{
}
}
Try this :)
I have been trying to get a UIImageView to rotate around a point when I touch and drag on it. I am having the problem where it jumps back to its original position if I try and rotate it a second time. I have tried the solution here, but that makes the image spin crazily!
What am I doing wrong? Here's my code:
Updated code with fixes:
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view];
if (CGRectContainsPoint(Game10Wheel.frame, location)) {
Game10Angle = atan2([Game10Wheel center].y - location.y, [Game10Wheel center].x - location.x);
Game10WheelTouched = true;
}
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if (Game10WheelTouched) {
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view];
float theAngle = atan2([Game10Wheel center].y - location.y, [Game10Wheel center].x - location.x);
[Game10Wheel setTransform: CGAffineTransformRotate([Game10Wheel transform], theAngle - Game10Angle)];
Game10Angle = theAngle;
}
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
Game10WheelTouched = false;
}
-(void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
Game10WheelTouched = false;
}
Thanks!
Your problem in calculation of the angle of rotation for further transformation. Try use code below.
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view];
float theAngle = atan2([Game10Wheel center].y - location.y, [Game10Wheel center].x - location.x);
[Game10Wheel setTransform: CGAffineTransformRotate([Game10Wheel transform], theAngle - Game10Angle)];
Game10Angle = theAngle;
}
You need to modify the current transformation of the object, otherwise you are just setting it to an initial value. Using CGAffineTransformMakeRotation creates a new transform object with only the rotation specified applied to it. What you want to do is use CGAffineTransformRotate and pass the current transform of the Game10Wheel object to add the rotation to that current transform.
EDIT: In other words, when you create your rotation, it should start from the current transform of the Game10Wheel, not from a new transformation.
I want to translate UIView in same direction as touch moves .
Please suggest.
Modify the touchesBegan and touchesMoved methods to be like the following
float oldX, oldY;
BOOL dragging;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if (CGRectContainsPoint(window.frame, touchLocation)) {
dragging = YES;
oldX = touchLocation.x;
oldY = touchLocation.y;
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if (dragging) {
CGRect frame = window.frame;
frame.origin.x = window.frame.origin.x + touchLocation.x - oldX;
frame.origin.y = window.frame.origin.y + touchLocation.y - oldY;
window.frame = frame;
}
oldX = touchLocation.x;
oldY = touchLocation.y;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
dragging = NO;
}
Hope it help
Try to do like the code below. I'm not sure if it is a best solution for all direction translation.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if ([touches count] != 1) return;
_swipeStartInX = [[touches anyObject] locationInView:self].x;
_swipeStartInY = [[touches anyObject] locationInView:self].y;
_swiping = YES;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!_swiping || [touches count] != 1) return;
CGFloat swipeDistanceInX = [[touches anyObject] locationInView:self].x - _swipeStartInX;
CGFloat swipeDistanceInY = [[touches anyObject] locationInView:self].y - _swipeStartInY;
CGSize contentSize = self.frame.size;
[_yourView setFrame:CGRectMake(swipeDistanceInX - contentSize.width, swipeDistanceInY - contentSize.width, contentSize.width, contentSize.height)];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!_swiping) return;
// You can set the last position when touches end.
// E.g. You can set positions like slide page does, just the the origin of _yourView.
}
If you just want translate in vertical and horizontal direction, you can use UIScrolView instead. :)
You can just add this method to your subclassed UIView class.
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint currentLocation = [touch locationInView:self];
CGPoint previousLocation= [touch previousLocationInView:self];
CGFloat deltaX = currentLocation.x - previousLocation.x;
CGFloat deltaY = currentLocation.y - previousLocation.y;
self.frame = CGRectMake(self.frame.origin.x + deltaX, self.frame.origin.y + deltaY, self.frame.size.width, self.frame.size.height);
}