Iphone SDK: move image around with the accelerometer - iphone

Im trying to move an image around with the accelerometer by doing that:
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
image.center = CGPointMake(acceleration.x, acceleration.y);
}
When i test the app, the image that is supposed to move around just sits in the x0 y0 position.
I declared the accelerometer, called the .h UIAccelerometerDelegate and so on...
What am i doing wrong?
Thanks in advance! -DD

You do realize that the accelerometer returns, as the name would suggest, measures of acceleration not points on the display? Anyway, what you need to do, is alter the center (not replace it completely), which will allow you to move the image.
Something along these lines:
image.center = CGPointMake(image.center.x + acceleration.x,
image.center.y - acceleration.y);
It is also important to note that the acceleration usually stays between -1 and 1 (unless the user shakes the device), which is due to the gravity being 1G. Therefore you should probably multiply the acceleration.x and .y values with some constant to make the image move a bit faster than about 1 point at a time.
There are additional things you should think about, what if the image is at the edge of the screen? What if the user wants to use the app in some other position than flat on a surface (needs calibration of the accelerometer)?

-(void)moveImage:(id)sender
{
[operationView bringSubviewToFront:[(UIPanGestureRecognizer*)sender view]];
[[[(UIPanGestureRecognizer*)sender view] layer] removeAllAnimations];
CGPoint translatedPoint = [(UIPanGestureRecognizer*)sender translationInView:self.view];
if([(UIPanGestureRecognizer*)sender state] == UIGestureRecognizerStateBegan)
{
firstX = [[sender view] center].x;
firstY = [[sender view] center].y;
[imgDeleteView setHidden:FALSE];
}
else if ([(UIPanGestureRecognizer*)sender state] == UIGestureRecognizerStateEnded)
{
[imgDeleteView setHidden:TRUE];
}
translatedPoint = CGPointMake(firstX+translatedPoint.x, firstY+translatedPoint.y);
[[(UIPanGestureRecognizer *)sender view] setCenter:translatedPoint];
}

Related

Improve Responsiveness of ccTouchesBegan

At the moment, I have a puzzle game, with some objects that are movable using the ccTouchesBegan && ccTouchesMoved method
Called in ccTouchesBegan:
if (CGRectContainsPoint(newBoundingBox, touchLocation))
{
//Set the selectedSprite to the sprite with newBoundingBox
}
Then in ccTouchesMoved i do:
CGPoint translation = ccpSub(touchLocation, oldTouchLocation);
newPos = ccpAdd(selSprite.position, translation);
selectedSprite.position = newPos;
Now this all works fine and dandy, HOWEVER, some of the objects that I am moving are about 140x40px (on the 320x480 iPhone), and sometimes the ccTouchesBegan method won't acknowledge the boundingBox containing the touchLocation.
My question is, Can I increase the size of the boundingBox of the object so that if the user "misses" with there touch a little bit, it will help them out? It's a little frustrating requiring you to be so precise on a touch screen to move an obstacle.
EDIT: for iPhonic:
How I get touchLocation, this is in ccTouchesBegan:
NSSet *touchSet = [event allTouches];
int numberOfTouches = [touchSet count];
CGPoint touchLocation = [self convertTouchToNodeSpace:[touches anyObject]];
allTouches is passed automatically when the a touch begins.
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint location = [self convertTouchToNodeSpace: touch];
for (CCSprite *tempSprite in sprites )
{
//Increase the size of bounding box..
CGRect rect=tempSprite.boundingBox;
rect.size.height=rect.size.height + 20;
rect.size.width=rect.size.width + 20;
if (CGRectContainsPoint(rect, location))
{
//Your stuffs
}
}
}

Making use of velocityInView with UIPanGestureRecognizer

I have a custom slider-type object, that I wish to make more usable. Currently I use UIPanGestureRecognizer and translationInView to make it work. It works pretty well but I'd like some sort of velocity in there to make it feel a bit more useful. I've tried a few things but cant quite figure out how to properly implement velocity changedLevel equation.
- (void)panDetected:(UIPanGestureRecognizer *)gesture {
CGPoint swipeLocation = [gesture locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:swipeLocation];
LevelCounterTableCell *swipedCell = (LevelCounterTableCell *)[self.tableView cellForRowAtIndexPath:indexPath];
if([gesture state] == UIGestureRecognizerStateBegan) {
NSString *originalLevelString = swipedCell.levelNumber.text;
originalLevel = [originalLevelString intValue]; // int originalLevel
}
if ([gesture state] == UIGestureRecognizerStateChanged) {
CGFloat xTranslation = [gesture translationInView:[[gesture view] superview]].x;
CGFloat xVelocity = [gesture velocityInView: [[gesture view] superview]].x;
// Pan threshold is currently set to 8.0.
// 8.0 is a decent level for slow panning
// for fast panning 2.0 is more reasonable
changedLevel = ceilf((xTranslation / panThreshold) + originalLevel); // int changedLevel
// Raw velocity seems to go from around 3 (slow)
// to over 200 (fast)
NSLog(#"raw velocity = %f", xVelocity);
if (changedLevel >= 15 && changedLevel <= 100) {
swipedCell.levelNumber.text = [NSString stringWithFormat:#"%i", changedLevel];
swipedCell.meter.frame = [self updateMeter: changedLevel];
}
}
if ([gesture state] == UIGestureRecognizerStateEnded || [gesture state] == UIGestureRecognizerStateCancelled) {
if (changedLevel >= 15 && changedLevel <= 100) {
//... Save the values...
}
}
}
Any help would be greatly appreciated. Thank you.
In my experience, the velocityInView: of a pan gesture recognizer isn't important until the user lifts their finger(s) and the recognizer finishes. At that point, you can use the velocity to calculate an animation duration to move your views.
Just stick with translationInView: until the state is UIGestureRecognizerStateEnded and then use velocityInView: to animate any onscreen view changes.

How to prevent user from panning my view off screen?

I've got a UIView that receives user panning from a gesture recognizer. I realized some times user would "throw" my view almost out of screen and that looks very bad. I want to prevent this from happening. I know I should do some check in my selector, but I don't know how to do it when the view is translated.
Here is my code:
- (void)panPiece:(UIPanGestureRecognizer *)gestureRecognizer
{
UIView *piece = [gestureRecognizer view];
[self adjustAnchorPointForGestureRecognizer:gestureRecognizer];
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) {
CGPoint translation = [gestureRecognizer translationInView:[piece superview]];
[piece setCenter:CGPointMake([piece center].x + translation.x, [piece center].y + translation.y)];
[gestureRecognizer setTranslation:CGPointZero inView:[piece superview]];
}
}
Thanks in advance
Regards
Leo
You can add following condition in you code.
if([gestureRecognizer state] == UIGestureRecognizerStateEnd)
Inside that you can check weather any corner of view is outside of screen or not. If its is outside then you can bounce it back inside screen.
Following is a possible example.
if (zl.layer.frame.origin.x > 0) {
self.moveFactorX = self.moveFactorX-zl.layer.frame.origin.x;
[zl.layer setValue:[NSNumber numberWithFloat: moveFactorX+totalMoveX] forKeyPath: #"transform.translation.x"];
}
else if(zl.layer.frame.origin.x < pageSize.width - zl.layer.frame.size.width){
self.moveFactorX = self.moveFactorX-(zl.layer.frame.origin.x - pageSize.width + zl.layer.frame.size.width);
[zl.layer setValue:[NSNumber numberWithFloat: moveFactorX+totalMoveX] forKeyPath: #"transform.translation.x"];
}
This checked for me weather left side of layer of zl class is inside the screen. When It is inside screen then i was pushing is back to left most corner.
In else it checks weather it is inside the screen from right side or not. and when condition satisfies it will push it to right border.
Same way you have to implement for top and bottom also.
If you find code confusing then post here. I will modify it accordingly.

How to make dragging faster using velocityView in UIPanGestureRecognizer?

Having a little trouble understanding how to implement a faster dragging speed for the code I'm using below (written by #PaulSoltz) which allows you to drag an object across the screen. I realize you have to use the velocityInView method from UIPanGestureRecognizer, and I understand it returns the x velocity vector and y velocity vector. If velocity = distance over time, then for instance velocityx = (x2 - x1) / time and I'm unsure how to use this formula to get what I need. Basically I just want to be able to adjust the speed of my movement to make it a little faster. Maybe I'm over thinking things, but if anyone could help me understand this it would be appreciated. Thanks.
- (void)handlePanGesture:(UIPanGestureRecognizer *)gestureRecognizer {
UIView *myView = [gestureRecognizer view];
CGPoint translate = [gestureRecognizer translationInView:[myView superview]];
if ([gestureRecognizer state] == UIGestureRecognizerStateChanged || [gestureRecognizer state] == UIGestureRecognizerStateChanged) {
[myView setCenter:CGPointMake(myView.center.x + translate.x, myView.center.y + translate.y)];
[gestureRecognizer setTranslation:CGPointZero inView:[myView superview]];
}
}
Just multiply the components of the translation vector by some constant.
[myView setCenter:CGPointMake(myView.center.x + translate.x * 2, myView.center.y + translate.y * 2)];

Test for a pinch on UIImageView (not pinch and zoom - just test for pinch)

This isn't another one of "those" questions, don't worry.
Basically I want to test for a pinch and then run an animation. Here is the code I currently have:
// test for a pinch
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSSet *allTouches = [event allTouches];
if([allTouches count] == 2){
//there are two touches...
UITouch *firstTouch = [[allTouches allObjects] objectAtIndex:0];
UITouch *secondTouch = [[allTouches allObjects] objectAtIndex:1];
CGPoint firstPoint = [firstTouch locationInView:self.superview];
CGPoint secondPoint = [secondTouch locationInView:self.superview];
int X1 = firstPoint.x;
int Y1 = firstPoint.y;
int X2 = secondPoint.x;
int Y2 = secondPoint.y;
// lets work out the distance:
// sqrt( (x2-x1)^2 + (y2-y1)^2 );
preDistance = sqrtf( (float)pow((X2-X1),2) + (float)pow((Y2-Y1),2) );
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSSet *allTouches = [event allTouches];
if([allTouches count] == 2){
//there are two touches...
UITouch *firstTouch = [[allTouches allObjects] objectAtIndex:0];
UITouch *secondTouch = [[allTouches allObjects] objectAtIndex:1];
CGPoint firstPoint = [firstTouch locationInView:self.superview];
CGPoint secondPoint = [secondTouch locationInView:self.superview];
int X1 = firstPoint.x;
int Y1 = firstPoint.y;
int X2 = secondPoint.x;
int Y2 = secondPoint.y;
// lets work out the distance:
// sqrt( (x2-x1)^2 + (y2-y1)^2 );
postDistance = sqrtf( (float)pow((X2-X1),2) + (float)pow((Y2-Y1),2) );
// lets test now
// if the post distance is LESS than the pre distance then
// there has been a pinch INWARDS
if(preDistance > postDistance){
NSLog(#"Pinch INWARDS");
// so we need to move it back to its small frame
if(CGRectEqualToRect(self.frame, largeFrame) && !CGRectIsEmpty(self.smallFrame)){
// we can go inwards
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
self.frame = smallFrame;
[UIView commitAnimations];
}
}
else {
NSLog(#"Pinch OUTWARDS");
// so we need to move it back to its small frame
if(!CGRectEqualToRect(self.frame, largeFrame)){
// we can go outwards
// set org frame
smallFrame = self.frame; // so we can go back later
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
self.frame = largeFrame;
[UIView commitAnimations];
}
}
}
}
All it does is measure the distance between the two touches if there is a multi touch, then it measures the distance again when the touches end. If the difference between the two is positive then it was an outwards pinch, and if it was negative it was an inwards pinch.
Depending on the direction of the pinch the image view will scale bigger or smaller to and from set CGRects (this is why I don't want the "normal" pinch and zoom functionality). I just want it to be a gesture to scale an image up and down again.
However, this isn't working very well... it doesn't always pick up pinches. I don't know if this is because the view its in is also detecting touches (only single pinches), but still...
Anyway, I was wondering if anyone has a better way of detecting pinches? I've currently subclassed the UIImageView to create my ImageController class which has these touch methods in them. All I need to know is a) they are touching this image and b) which direction they've pinched in.
Thanks
Check out UIPinchGestureRecognizer in the documentation. I haven't used it, but seems like it's just what you need.
Anyway, I was wondering if anyone has a better way of detecting pinches?
If you're targeting iOS 3.2 and upwards you can use gesture recognizers - there's no good reason not to. If you need to target pre 3.2, then your above approach (or varients therein, using touchesBegin, etc) is the only way, so it's just tweaking the algorithm.
If you can, I'd strongly recommend using UIPinchGestureRecognizer. It will make your life considerably easier, as it handles all the details. If you want to detect both the scale and direction of the pinch you can combine UIPinchGestureRecognizer with a UIRotationGestureRecognizer - the former will just give you the scale of a pinch, not the relative angle.
If you can live without 3.1.x devices I would recommend that you use the UIPinchGestureRecognizer. It's very simple to use, but the problem is that you're only going to be able to run it on iOS devices running 3.2 and upwards.
For some reason you are using locationInView to identify the position of the touches. Have you tried identifying the touches location in the current view (the actual image view)? Calling superview means that you will get the position of the touches in the view that contains your image view.