Car (Annotation) animation (like uber app) not working - iphone

I made one demo project (from Moving-MKAnnotationView demo on github) for moving car on map following is its link
https://github.com/pratikbhiyani/Moving-MKAnnotationView
I edit my code on the basis of given answer by vinaut but still problem is that when we zoom or scroll the map animation get distract in ios 7 and in ios 6 when we zoom or scroll the map annotation set to its original angle for a while.
Below is a screen shot of my Demo Project
Here is some code i change
- (void) setPosition : (id) posValue;
{
NSLog(#"set position");
//extract the mapPoint from this dummy (wrapper) CGPoint struct
MKMapPoint mapPoint = *(MKMapPoint*)[(NSValue*)posValue pointerValue];
CLLocationCoordinate2D coord = MKCoordinateForMapPoint(mapPoint);
CGPoint toPos;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
toPos = [self.mapView convertCoordinate:coord toPointToView:self.mapView];
}
else
{
CGFloat zoomFactor = self.mapView.visibleMapRect.size.width / self.mapView.bounds.size.width;
toPos.x = mapPoint.x/zoomFactor;
toPos.y = mapPoint.y/zoomFactor;
}
[self setTransform:CGAffineTransformMakeRotation([self getHeadingForDirectionFromCoordinate:MKCoordinateForMapPoint(previousPoint) toCoordinate: MKCoordinateForMapPoint(mapPoint)])];
if (MKMapRectContainsPoint(self.mapView.visibleMapRect, mapPoint)) {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"position"];
animation.fromValue = [NSValue valueWithCGPoint:self.center];
animation.toValue = [NSValue valueWithCGPoint:toPos];
animation.duration = 1.0;
animation.delegate = self;
animation.fillMode = kCAFillModeForwards;
//[self.layer removeAllAnimations];
[self.layer addAnimation:animation forKey:POSITIONKEY];
//NSLog(#"setPosition ANIMATED %x from (%f, %f) to (%f, %f)", self, self.center.x, self.center.y, toPos.x, toPos.y);
}
self.center = toPos;
previousPoint = mapPoint;
}
My goal is to move car same like in uber app.

It seems that something changed with the conversion functions for CLCoordinate2D/MKMapPoint/CGPoint...
Detecting a point in a MKPolygon broke with iOS7 (CGPathContainsPoint)
The annotation disappears because the conversion beetween MkMapPoints and CGIPoints does not work anymore, if you log the "position" of the CALayer you will get points way outside the view. No idea why it works when doing touch events.
If you change the function to :
- (void) setPosition : (id) posValue;
{
//extract the mapPoint from this dummy (wrapper) CGPoint struct
MKMapPoint mapPoint = *(MKMapPoint*)[(NSValue*)posValue pointerValue];
CLLocationCoordinate2D coord = MKCoordinateForMapPoint(mapPoint);
CGPoint toPos = [self.mapView convertCoordinate:coord toPointToView:self.mapView];
if (MKMapRectContainsPoint(self.mapView.visibleMapRect, mapPoint)) {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"position"];
animation.fromValue = [NSValue valueWithCGPoint:self.center];
animation.toValue = [NSValue valueWithCGPoint:toPos];
animation.duration = 0.8;
animation.delegate = self;
animation.fillMode = kCAFillModeForwards;
//[self.layer removeAllAnimations];
[self.layer addAnimation:animation forKey:POSITIONKEY];
//NSLog(#"setPosition ANIMATED %x from (%f, %f) to (%f, %f)", self, self.center.x, self.center.y, toPos.x, toPos.y);
}
self.center = toPos;
}
It should be working again.

I'm the original contributor to Moving-MKAnnotationView (https://github.com/100grams/Moving-MKAnnotationView.git). This component was originally written using iOS4.3 and a lot has changed since. :-)
The root cause here was the conversion from MKMapPoint to CGPoint (screen coordinates). Although the code worked before, it breaks on iOS7 and I fixed it by using this to convert the lat/lon coordinate to screen coordinates:
convertCoordinate:toPointToView:
I've committed this fix, with a few other updates, to https://github.com/100grams/Moving-MKAnnotationView.git so it now works on iOS7/Xcode5.

The problem of distracting a car while zoom/scrolling the map. Actually it can not be achieved by adding an animation to annotation. I have found out the Interpolate function through which I can get the locations between "From" and "To" coordinates and set it to the annotation(setting annotation coordinate in milliseconds) will look like an animation.
It is not an issue of iOS or Map, if you are adding animation to annotation it will add to annotation layer not respect to the map point.

Related

How to move and rotate object in iphone

I am developing a small game in iphone... Game concept is place an object in top of the bar...Rotating a bar using accelerometer. When bar rotating , i need to move an object with respect to bar. How to implement this concept...Any examples or references?
Rotating both img:
barImg,objImg //UIImageView
barImg.transform = CGAffineTransformMakeRotation(Ypos);
objImg.transform=CGAffineTransformMakeRotation(Ypos);
So for rotating 360 Degree with animation:
CABasicAnimation *rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.toValue = [NSNumber numberWithFloat:-M_PI * 2.0]; // full rotation*/ * rotations * duration ];
rotationAnimation.duration = 1;
rotationAnimation.cumulative = YES;
rotationAnimation.repeatCount = MAXFLOAT;
[rotatingTelIamgeview.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
you can play with toValue for changing the angle.
For moving an object:
CGPoint startPoint = CGPointMake(lvMessageInputBar.animationBubbleImageView.layer.frame.origin.x+lvMessageInputBar.animationBubbleImageView.layer.frame.size.width/2
, lvMessageInputBar.animationBubbleImageView.layer.frame.origin.y +lvMessageInputBar.animationBubbleImageView.layer.frame.size.height/2 );
CGPoint endPoint = CGPointMake(endPoint.origin.x+Endpoint.size.width/2, bubleRect.origin.y+bubleRect.size.height/2);
CGPoint middlePoint = // any point between start and end corrdinates
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, startPoint.x, startPoint.y);
CGPathAddQuadCurveToPoint(path, NULL,middlePoint.x, middlePoint.y,endPoint.x,endPoint.y);
CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
pathAnimation.delegate = self;
pathAnimation.removedOnCompletion = NO;
pathAnimation.path = path;
[pathAnimation setCalculationMode:kCAAnimationCubic];
[pathAnimation setFillMode:kCAFillModeForwards];
pathAnimation.duration = 0.3;
[lvMessageInputBar.animationBubbleImageView.layer addAnimation:pathAnimation forKey:nil];
The above example moves the layer of the object over a path.but CGPathAddQuadCurveToPoint makes the path not diagonal rather circular path (curve).
you can use CGPathAddPath if you do not want this effect.
If you are new to iPhone I would start with layer tutorial to get the idea behind CALayer and then take a look CALayer animations
CALayers introduction:
https://www.raywenderlich.com/3096-calayers-tutorial-for-ios-introduction-to-calayers
CALayer Animation documentation of from Apple:
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreAnimation_guide/Introduction/Introduction.html
You can also watch Session 424 (Core Animation in Practice, Part 1) and 425 (Core Animation in Practice, Part 2) of WWDC 2010:
https://developer.apple.com/videos/archive/

iPhone - Touch event is not received by UIImageView while animating using CABasicAnimation

i Have a UIImageview object moving along a path and i have use CABasic animation and BezierCure to move the object along the path, Now the problem is if i touch the moving object the touch event is not recognized by the UIImageView but the touch event is received by main view. can anybody help me to solve this problem?
Below is my code
UITouch *touch = [[event allTouches] anyObject];
if (![touch.view isEqual: beaconPose1]) {
return;
}
[touch.view.layer removeAllAnimations];
CGPoint location = [touch locationInView: self.view];
CGRect frame = touch.view.frame;
frame.origin.x = location.x;
frame.origin.y = location.y;
///frame.origin.x += xDisplacement;
//frame.origin.y += yDisplacement;
touch.view.frame = frame;
-- Code where User Interaction is enabled
[beaconPose1 setUserInteractionEnabled:true];
CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
pathAnimation.duration = 1.0f;
pathAnimation.path = beaconPose3Path.CGPath;
pathAnimation.calculationMode = kCAAnimationLinear;
CABasicAnimation *fullRotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
fullRotation.fromValue = [NSNumber numberWithFloat:0];
fullRotation.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)];
fullRotation.duration = pathAnimation.duration;
fullRotation.repeatCount = pathAnimation.duration/fullRotation.duration;
[beaconPose1.layer addAnimation:fullRotation forKey:#"360"];
[beaconPose1.layer addAnimation:pathAnimation forKey:#"movingAnimation"];
I figured out how to do this,
The object under CAKeyframeAnimation or CABasicAnimation will not respond to touch event, instead what we can do is calculate if the location of the touch is withing the area of the object using Pythagorus theorem.
I'm afraid the currently accepted answer is wrong:
When the user puts his finger on the screen, all the views are hit-tested with the touch position. However, when hit-testing them, the view's frame is requested. This is, however, linked to the modelLayers frame, which is the end point of your animation.
What you would want is hit-testing the presentationLayer, but I guess this is not possible.

Rotate an imageView arrow with CAKeyframeAnimation and also have arrow respond to touch events

I have an arrow that spins when the user clicks a button using CAKeyframeAnimation.
The position the arrow points to dictates what image is seen by the user.
This works fine.
The final position of the rotation is selected randomly and sets up the next image for the user when they click the button.
-(void)rotateAnimation
{
[self setDialStartPosition];
[self setDialEndPosition];
//Spin the Arrow
CALayer *layer = imageView.layer;
CAKeyframeAnimation *animation;
animation = [CAKeyframeAnimation animationWithKeyPath:#"transform.rotation.z"];
animation.duration = 8;
animation.cumulative = NO;
animation.repeatCount = 0;
animation.values = [NSArray arrayWithObjects: // i.e., Rotation values for the 3 keyframes, in RADIANS
[NSNumber numberWithFloat:[self dialStartPosition] * M_PI],
[NSNumber numberWithFloat:2.5 * M_PI],
[NSNumber numberWithFloat:[self dialEndPosition] * M_PI], nil];
animation.keyTimes = [NSArray arrayWithObjects: // Relative timing values for the 3 keyframes
[NSNumber numberWithFloat:0],
[NSNumber numberWithFloat:.055],
[NSNumber numberWithFloat:1], nil];
animation.timingFunctions = [NSArray arrayWithObjects:
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn],
// from keyframe 1 to keyframe 2
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut], nil]; // from keyframe 2 to keyframe 3
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
[layer addAnimation:animation forKey:nil];
....
I have this working, but I want the user to be able to move the arrow manually using a touch and have the arrow point in the direction of their finger as they move it around.
When they lift their finger and hit the button, i want the position of the Arrow to dictate what image is then seen by the user. So touchesEnded would have to update the dialStartPosition.
Here is my touches code..
double wrapd(double _val, double _min, double _max)
{
if(_val < _min) return _max - (_min - _val);
if(_val > _max) return _min - (_max - _val);
return _val;
}
- (void) touchesBegan:(NSSet *)_touches withEvent:(UIEvent *)_event
{
UITouch* touch = [_touches anyObject];
CGPoint location = [touch locationInView:self.view];
m_locationBegan = location;
}
- (void) touchesMoved:(NSSet *)_touches withEvent:(UIEvent *)_event
{
UITouch* touch = [_touches anyObject];
CGPoint location = [touch locationInView:self.view];
[self updateRotation:location];
}
- (void) touchesEnded:(NSSet *)_touches withEvent:(UIEvent *)_event
{
UITouch* touch = [_touches anyObject];
CGPoint location = [touch locationInView:self.view];
m_currentAngle = [self updateRotation:location];
}
- (float) updateRotation:(CGPoint)_location
{
float fromAngle = atan2(m_locationBegan.y-imageView.center.y, m_locationBegan.x-imageView.center.x);
float toAngle = atan2(_location.y-imageView.center.y, _location.x-imageView.center.x);
float newAngle = wrapd(m_currentAngle + (toAngle - fromAngle), 0, 2*3.14);
CGAffineTransform cgaRotate = CGAffineTransformMakeRotation(newAngle);
imageView.transform = cgaRotate;
return newAngle;
}
Heres whats happing now..
Before I hit the button I can move the arrow around with a touch the way I want.
When I hit the button it doesn't respond to where I have set the position manually with my touch.
Once the CAKeyframeAnimation plays it no longer will respond to any touches unless I change
animation.fillMode = kCAFillModeForwards;
to
animation.fillMode = kCAFillModeRemoved;
but if I do this it removes the animation after it runs and shows the arrow in the spot I left it when I set it manually.
I'm quite new a programming so any help would be appreciated
Thanks

How to rotate image 360 degrees endlessly using iPhone SDK?

I want to rotate image 360 degrees endlessly using iPhone SDK?
How can this be done?
Thanks!
-(void)startAnimationWithRevolutions:(float)revPerSecond forTime:(float)time
{
spinWheel.userInteractionEnabled = FALSE;
float totalRevolutions = revPerSecond * time;
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:time] forKey:kCATransactionAnimationDuration];
CABasicAnimation* spinAnimation = [CABasicAnimation
animationWithKeyPath:#"transform.rotation"];
CGAffineTransform transform = spinWheel.transform;
float fromAngle = atan2(transform.b, transform.a);
float toAngle = fromAngle + (totalRevolutions*4*M_PI);
spinAnimation.fromValue = [NSNumber numberWithFloat:fromAngle];
spinAnimation.toValue = [NSNumber numberWithFloat:toAngle];
spinAnimation.repeatCount = 0;
spinAnimation.removedOnCompletion = NO;
spinAnimation.delegate = self;
spinAnimation.timingFunction = [CAMediaTimingFunction functionWithName:
kCAMediaTimingFunctionEaseOut];
[spinWheel.layer addAnimation:spinAnimation forKey:#"spinAnimation"];
[CATransaction commit];
}
Try using this, it works.
Assuming you are displaying the image in a UIImageView, you can rotate the view's local coordinate system (see here) and use NSTimer to increase the rotation periodically (see here).
(I would use OpenGL for this, but that's just a personal preference and is probably overkill for your situation.)

Rotation always start from original position

I am trying to implement a wheel type functionality. I calculate the center point of the view and find the angle created on moving in touchesMoved: method. For the first move it rotated by some degree. But for next move it comes back to the original position of the view and then rotating.Actually i want the rotation from the end point of the previous rotation.
Any thing missing?Any help
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
float a, b,c;
UITouch *aTouch = [touches anyObject];
if ([aTouch view] == dialView) {
CGPoint loc = [aTouch locationInView:dialView];
CGPoint prevloc = [aTouch previousLocationInView:dialView];
c = [self GetSides:prevloc point2:loc];
a = [self GetSides:loc point2:cntrePoint];
b = [self GetSides:prevloc point2:cntrePoint];
float angle = acos((a*a+b*b-c*c)/(2*a*b));// Calculating angle created on moving
CABasicAnimation *rotate ;
rotate = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
rotate.fromValue = [NSNumber numberWithFloat:0];
rotate.toValue = [NSNumber numberWithFloat:((angle*M_PI)/180)];
rotate.autoreverses = NO;
rotate.fillMode = kCAFillModeForwards;
rotate.duration = 1;
rotate.repeatCount = 1;
rotate.removedOnCompletion = NO;
rotate.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[dialView.layer addAnimation:rotate forKey:#"360"];
}
}
The animation you are performing, which looks to be drawn from this answer, has both the to and from values specified, so it will simply rotate from 0 to angle radians every time. That means that your image will jump back to the start on every animation.
Normally, if you were to remove the fromValue line, your animation should go from the current angle to your new angle, but it looks like the transform property behaves a little differently. You'll need to set the fromValue to be the current value of the rotational transform extracted from your layer's presentationLayer. The following code should rotate to the target angle from your current angle:
CABasicAnimation *rotate;
rotate = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotate.fromValue = [[dialView.layer presentationLayer] valueForKeyPath:#"transform.rotation.z"];
rotate.toValue = [NSNumber numberWithFloat:angle];
rotate.fillMode = kCAFillModeForwards;
rotate.duration = 1;
rotate.removedOnCompletion = NO;
rotate.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
Note that I've removed the degrees-to-radians conversion in the toValue, because your acos() calculation returns values in radians.
EDIT (10/23/2009): Corrected my earlier assumption about the functioning of fromValue with the transform property.