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.
Related
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.
I have a layer that I have added as a sublayer of a UIWebView. I would like to be able to touch and drag the layer around. Before I added the UIWebView, I was able to touch and drag around the layer without a problem.
Here's where I create the layer and add it as a sublayer
- (void)viewDidLoad
{
[super viewDidLoad];
starLayer = [CALayer layer];
starLayer.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage
imageNamed:#"star"]].CGColor;
starLayer.shadowOffset = CGSizeMake(0, 3);
starLayer.shadowRadius = 5.0;
starLayer.shadowColor = [UIColor blackColor].CGColor;
starLayer.shadowOpacity = 0.8;
starLayer.frame = CGRectMake(235.0, 200.0, 35.0, 35.0);
[self.aboutWebView.layer addSublayer:starLayer];
[self moveStar];
}
Here's my moveStar method
- (void)moveStar
{
CAKeyframeAnimation *move = [CAKeyframeAnimation animationWithKeyPath:#"position"];
NSMutableArray *values = [NSMutableArray array];
[values addObject:[NSValue valueWithCGPoint:CGPointMake(250.0, 50.0)]];
[values addObject:[NSValue valueWithCGPoint:CGPointMake(250.0, 250.0)]];
[move setValues:values];
[move setDuration:1.0];
[move setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
// Add the animation to the layer to be animated
[starLayer addAnimation:move forKey:#"moveAnimation"];
}
So far so good. The star falls onto the screen and lands a bit above the middle. But when I try and touch the star nothing happens. touchesBegan never gets called for some reason.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self.aboutWebView];
[starLayer setPosition:point];
}
Is the reason I can't touch the star when it's a sublayer of aboutWebView because a touch is reported to a view, not a layer?
Does anyone have any suggestions on how to make the star as a sublayer of aboutWebView touchable? Is it possible?
Thank you!
In your layer... try to set the UIGestureRecognizer property cancelsTouchesInView to NO.
"Setting this value to NO instructs the recognizer to deliver all touches to the underlying view, even when it has recognized the sequence"
something like that:
UIGestureRecognizer *touches = [UIGestureRecognizer alloc];
//or
//UIGestureRecognizer *touches = [[UIGestureRecognizer alloc] initWithTarget:self action:#selector(touchesAction:)];
touches.enabled=YES;
[yourLayer addGestureRecognizer:touches];
touches.cancelsTouchesInView=NO;
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/
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
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.