Moving Annotation View on mapkit - objective-c

I have to add annotation view image as a car. I am able to move that car according to the locations I am getting from server. But I am facing problem when I have applied code for rotation of car. Every time, car is turning to new location, it moves and again it goes to its original position(car does not face to front).then again it turns,moves and goes to original position. It should move straight. I want car should face to front and It should turn only when actually there is turning. My code is:
oldLocation.latitude = [[_lat objectAtIndex:i] doubleValue];
oldLocation.longitude = [[_longitude objectAtIndex:i] doubleValue];
newLocation.latitude = [[latNew objectAtIndex:i] doubleValue];
newLocation.longitude = [[longNew objectAtIndex:i] doubleValue];
float getAngle = [self angleFromCoordinate:oldLocation toCoordinate:newLocation];
[UIView animateWithDuration:2 animations:^{
MKAnnotationView *annotationView = (MKAnnotationView *[self.mapView viewForAnnotation:myAnnotation[i]];
annotationView.transform = CGAffineTransformMakeRotation(getAngle);
myAnnotation[i].coordinate = newLocation;
}];
}
- (float)angleFromCoordinate:(CLLocationCoordinate2D)first
toCoordinate:(CLLocationCoordinate2D)second {
float deltaLongitude = second.longitude - first.longitude;
float deltaLatitude = second.latitude - first.latitude;
float angle = (M_PI * .5f) - atan(deltaLatitude / deltaLongitude);
if (deltaLongitude > 0) return angle;
else if (deltaLongitude < 0) return angle + M_PI;
else if (deltaLatitude < 0) return M_PI;
return 0.0f;
}

Related

Rotating Around Certain CGPoint

How to rotate around a certain CGPoint from the Given UIView.
The following images defines a CAShapeLayer around which there were four UIView's as top, left, bottom and right:
I have added UIRotationGesture to self.view through which I am rotating CAShapeLayer, so I want to move point of the it to move as well along with the CAShapeLayer like shown in the following image:
The following defines the code that I am using to achieve the same but having some issues with it:
- (void)handleRotation:(UIRotationGestureRecognizer *)recognizer
{
if(recognizer.state == UIGestureRecognizerStateBegan)
{
CGPoint touch1 = [recognizer locationOfTouch:0 inView:_imgBackground];
CGPoint touch2 = [recognizer locationOfTouch:1 inView:_imgBackground];
UIBezierPath *bezierpath = [UIBezierPath bezierPath];
bezierpath.CGPath = shapeLayer.path;
}
else if(recognizer.state == UIGestureRecognizerStateChanged)
{
CGPathRef path = createPathRotatedAroundBoundingBoxCenter(shapeLayer.path, rotationGesture.rotation);
shapeLayer.path = path;
CGPathRelease(path);
rotationGesture.rotation = 0;
}
else if (recognizer.state == UIGestureRecognizerStateEnded)
{
NSMutableArray *rotatingLayerPoints = [NSMutableArray array]; // will contain all the points of object that is been rotated
CGPathApply(shapeLayer.path, (__bridge void * _Nullable)(rotatingLayerPoints), CGPathApplier);
degreeOfRotation = DEGREES_TO_RADIANS([self getDegreeFromStartPoint:[[rotatingLayerPoints objectAtIndex:0] CGPointValue] endPoint:[[rotatingLayerPoints objectAtIndex:1] CGPointValue]]); // getting degree of first line's movement
CGRect frame = CGPathGetBoundingBox(shapeLayer.path);
CGPoint rotationPoint = CGPointMake(frame.origin.x + (frame.size.width/2), frame.origin.y + (frame.size.height/2));// The point we are rotating around
CGFloat minX = CGRectGetMinX(topPoint.frame);
CGFloat minY = CGRectGetMinY(topPoint.frame);
CGFloat width = CGRectGetWidth(topPoint.frame);
CGFloat height = CGRectGetHeight(topPoint.frame);
CGPoint anchorPoint = CGPointMake((rotationPoint.x-minX)/width,
(rotationPoint.y-minY)/height);
topPoint.layer.anchorPoint = anchorPoint;
topPoint.layer.position = rotationPoint;
topPoint.transform = CGAffineTransformMakeRotation(degreeOfRotation);
}
}
void CGPathApplier (void *info, const CGPathElement *element) // used to get points from CGPath
{
NSMutableArray *bezierPoints = (__bridge NSMutableArray *)info;
CGPoint *points = element->points;
CGPathElementType type = element->type;
switch(type)
{
case kCGPathElementMoveToPoint: // contains 1 point
[bezierPoints addObject:[NSValue valueWithCGPoint:points[0]]];
break;
case kCGPathElementAddLineToPoint: // contains 1 point
[bezierPoints addObject:[NSValue valueWithCGPoint:points[0]]];
break;
case kCGPathElementAddQuadCurveToPoint: // contains 2 points
[bezierPoints addObject:[NSValue valueWithCGPoint:points[0]]];
[bezierPoints addObject:[NSValue valueWithCGPoint:points[1]]];
break;
case kCGPathElementAddCurveToPoint: // contains 3 points
[bezierPoints addObject:[NSValue valueWithCGPoint:points[0]]];
[bezierPoints addObject:[NSValue valueWithCGPoint:points[1]]];
[bezierPoints addObject:[NSValue valueWithCGPoint:points[2]]];
break;
case kCGPathElementCloseSubpath: // contains no point
break;
}
}

SKSpriteNode rotation not working properly

I'm trying to rotate a SKSpriteNode by using UIRrotationGestureRecognizer. I've implemented a code but sometimes, when I rotate the node it jumps to a rotation that isn't the one it should be. Here you have the code:
- (void) handleRotation:(UIRotationGestureRecognizer *) rotationrecognizer{
CGFloat initialrotation = 0.0;
if (rotationrecognizer.state == UIGestureRecognizerStateBegan) {
CGPoint touchLocation = [rotationrecognizer locationInView:rotationrecognizer.view];
touchLocation = [self convertPointFromView:touchLocation];
[self selectNodeForTouch:touchLocation];
initialrotation = selected.zRotation;
}
else if (rotationrecognizer.state == UIGestureRecognizerStateChanged) {
CGFloat angle = initialrotation + rotationrecognizer.rotation;
selected.zRotation = angle;
}
}
You are resetting initial rotation to 0 on every call... you should move this to be an ivar of your view if you need it to stay resident.
The way you have it written now, the line that sets 'angle' is effectively equal to this:
CGFloat angle = 0 + rotationrecognizer.rotation;
Instead, you should do the following (where initialRotation is defined as a private ivar):
- (void) handleRotation:(UIRotationGestureRecognizer *) rotationrecognizer{
if (rotationrecognizer.state == UIGestureRecognizerStateBegan) {
CGPoint touchLocation = [rotationrecognizer locationInView:rotationrecognizer.view];
touchLocation = [self convertPointFromView:touchLocation];
[self selectNodeForTouch:touchLocation];
_initialrotation = selected.zRotation;
}
else if (rotationrecognizer.state == UIGestureRecognizerStateChanged) {
CGFloat angle = _initialrotation + rotationrecognizer.rotation;
selected.zRotation = angle;
}
}

Custom MKOverlayView Drawing

I am trying to create a custom MKOverlayView class that draws the overlay since the normal MKOverlay and MKOverlayView does not suit my needs for the application I am building.
I am having a problem with the drawing of the polygon overlay, when I draw it; it looks no where near as good as if I let MKOverlayView draw it and by that i mean the edges are not sharp and are all pixelated when you zoom in on the map. Also the lines from point to point dont get drawn either for some reason.
Also when zooming in some of the polygons drawing gets clipped out until I zoom back out again.
here is my draw code
- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context{
MapOverlay *newOverlay = (MapOverlay *)self.overlay;
CGColorRef ref2 = [newOverlay.strokeColor CGColor];
CGContextSetLineWidth(context, newOverlay.lineWidth);
CGColorRef ref = [newOverlay.fillColor CGColor];
int _countComponents = CGColorGetNumberOfComponents(ref);
if (_countComponents == 4) {
const CGFloat *_components2 = CGColorGetComponents(ref);
CGFloat red = _components2[0];
CGFloat green = _components2[1];
CGFloat blue = _components2[2];
CGFloat alpha = _components2[3];
CGContextSetRGBFillColor(context, red, green, blue, alpha);
}
for (int i=0; i<newOverlay.coordinates.count; i++){
if(i % 2 == 0) {
CLLocationCoordinate2D p;
p.latitude = (CLLocationDegrees)[(NSString *)[newOverlay.coordinates objectAtIndex:i+1]floatValue];
p.longitude = (CLLocationDegrees)[(NSString *)[newOverlay.coordinates objectAtIndex:i] floatValue];
//CLLocation *p = [[CLLocation alloc] initWithLatitude:(CLLocationDegrees)[(NSString *)[newOverlay.coordinates objectAtIndex:i+1] floatValue] longitude:(CLLocationDegrees)[(NSString *)[newOverlay.coordinates objectAtIndex:i] floatValue]];
MKMapPoint point = MKMapPointForCoordinate(p);
CGPoint point2 = [self pointForMapPoint:point];
if (i==0){
CGContextMoveToPoint(context, point2.x, point2.y);
}else{
CGContextAddLineToPoint(context, point2.x, point2.y);
}
}
}
CGContextDrawPath(context, kCGPathFillStroke);
//CGContextStrokePath(context);
}
I have found very little information on custom MKOverlayView classes and how to draw on the map but these were the 2 tutorials that i was using to do this
http://spitzkoff.com/craig/?p=65
http://i.ndigo.com.br/2012/05/ios-maps-with-image-overlays/
UPDATE
I have a feeling it might have something to do with the bounding box of the overlay because if I return the bounding box of the world it displays fine but thats obviously not efficient to have every possible overlay drawn.
here is how I found the bounding box of my overlay in my custom MKOverlay class
- (MKMapRect)boundingMapRect{
double maxY = 0;
double minY = 0;
double maxX = 0;
double minX = 0;
for(NSUInteger i = 0;i < coordinates.count; i++) {
if(i % 2 == 0) {
CLLocationCoordinate2D tempLoc;
tempLoc.latitude =(CLLocationDegrees)[(NSString *)[coordinates objectAtIndex:(NSUInteger)i + 1] floatValue];
tempLoc.longitude = (CLLocationDegrees)[(NSString *)[coordinates objectAtIndex:(NSUInteger)i] floatValue];
MKMapPoint tempPoint = MKMapPointForCoordinate(tempLoc);
if(i == 0){
minX = tempPoint.x;
minY = tempPoint.y;
if(tempPoint.x > maxX){
maxX = tempPoint.x;
}
if (tempPoint.y > maxY){
maxY = tempPoint.y;
}
}else{
if(tempPoint.x > maxX){
maxX = tempPoint.x;
}
if(tempPoint.x < minX){
minX = tempPoint.x;
}
if (tempPoint.y > maxY){
maxY = tempPoint.y;
}
if(tempPoint.y < minY){
minY = tempPoint.y;
}
}
}
}
MKMapRect b2 = MKMapRectMake(minX, maxY, minX-maxX, minY-maxY);
return b2;
//return MKMapRectWorld;
}

Cutting off the lower end of a float

This is more of a math question but I'm just not having any luck. Basically Ive got the code below to change cell colors depending on their row equivalent in the colors array. It all works very well but I would like it so that I could somehow cut off the bottom end of the brightness spectrum so that I never end up with anything say, below 0.2. Any suggestions on how to solve this would be appreciated.
-(IBAction)reloadTable {
float arrayCount = [masterListArray count];
float increment = (1.0 / arrayCount);
NSMutableArray *tempColor = [[NSMutableArray alloc]init];
colors = [[NSMutableArray alloc]init];
for (float brightness = 0.0; brightness < 1.0; brightness += increment) {
UIColor *color = [UIColor colorWithHue:50.0f/255.0f
saturation:1.0
brightness:brightness
alpha:1.0];
[tempColor addObject:color];
NSLog(#"brightness: %f", brightness);
}
colors = [[tempColor reverseObjectEnumerator] allObjects];
[self.tableView reloadData];
}
You can use some extra variables to make sure you always end up between 1.0 and the bottom end. In the below code we use the number of cells to iterate through (better practice than what you had before) and start with brightness 1.0 and end at your bottom end. Also no need to use tempColors.
-(IBAction)reloadTable {
float arrayCount = [masterListArray count];
float bottomEnd = 0.2;
float brightnessRange = 1.0 - bottomEnd;
float increment = (brightnessRange / arrayCount);
colors = [NSMutableArray array];
for (int i = 0; i < arrayCount; i++) {
// We start with light and go darker per cell.
float brightness = bottomEnd + (brightnessRange - i * increment);
UIColor *color = [UIColor colorWithHue:50.0f/255.0f
saturation:1.0
brightness:brightness
alpha:1.0];
[colors addObject:color];
NSLog(#"brightness: %f", brightness);
}
[self.tableView reloadData];
}

UIScrollview questions

I'm trying to implement a uiscrollview with small images.
I need that uiscrollview last image in every end remain in the center when scroll finishes, leaving one side empty. Now last image remain in the sides when scroll finishes.
I need to know which image is in the center of the scollview, to apply a shadow on it,
jugadorSlide.pagingEnabled = YES;
NSError *error = nil;
fotosJugadores = [[NSFileManager defaultManager] contentsOfDirectoryAtPath: #"/Users/exular/Developer/GBC/exular/GBC/Caras/" error: &error];
NSInteger numberOfViews = [fotosJugadores count];
for (int i = 0; i < numberOfViews; i++) {
UIImage *myImage = [UIImage imageNamed:[fotosJugadores objectAtIndex:i]];
CGFloat yOrigin = i * myImage.size.width;
UIImageView *awesomeView = [[UIImageView alloc] initWithFrame:CGRectMake(yOrigin, 0, myImage.size.width, myImage.size.height)];
awesomeView.alpha = 0.5;
awesomeView.image = myImage;
[self.jugadorSlide addSubview:awesomeView];
awesomeView=nil;
}
jugadorSlide .contentSize = CGSizeMake(65 * numberOfViews,78);
jugadorSlide.layer.cornerRadius = 11;
jugadorSlide.layer.masksToBounds = YES;
Many thanks for your help.
To allow the first and last image to be in the center just add an empty space before the first image and after your last image. In other words: start your yOrigin at 65 and add 65 to the contentSize width (I am assuming that 65 is your images width):
...
CGFloat yOrigin = i * myImage.size.width + 65;
...
jugadorSlide.contentSize = CGSizeMake(65 * numberOfViews + 65,78);
...
To find out which image is in the center add the following inside the UIScrollViewDelegate method scrollViewDidEndDecelerating:.
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
int currentIndex = roundf(scrollView.contentOffset.x / 65) - 1;
UIImageView *currentImage = [scrollView viewWithTag:currentIndex];
}
To easily get the currentImage, add tags to your images when you create them:
...
awesomeView.tag = i;
...