Rotating only the MapView's content - iphone

I am trying to rotate a map view in my app according the the user's current route. (I don't like to use the built in compasse because only 3GS uses it and it suffers too much interference from other machines, i.e. your own car.).
the CGAffineTransformMakeRotation method will rotate the Whole Map View, so the Google Logo will not be in the lower right of the screen anymore. Also all Annotation Views will rotate and look weird on the App.
Does anyone knows how to rotate just the content (streets drwaings) of the MkMapView?
Thanks

Here is how I'm rotating the MapView (see Rotate MapView using Compass orientation):
[self.mapView setTransform:CGAffineTransformMakeRotation(-1 * currentHeading.magneticHeading * 3.14159 / 180)];
Then to keep the orientation of the annotation views with the top of the device:
for (MKAnnotation *annotation in self.mapView.annotations) {
MKAnnotationView *annotationView = [self.mapView viewForAnnotation:annotation];
[annotationView setTransform:CGAffineTransformMakeRotation(currentHeading.magneticHeading * 3.14159 / 180)];
}
If you want the point of rotation of the annotationViews to be say the bottom center, I believe you would set each view's anchorPoint as follows:
annotationView.layer.anchorPoint = CGPointMake(0.5, 1.0);
I'm still having some issues with the annotationView position changing when I do this though (probably same problem as stackoverflow question: changing-my-calayers-anchorpoint-moves-the-view).

Related

Auto scroll mkmapview when dragging pin

Is there any good code which implements auto scrolling of MKMapView when dragging pin?
The effect I'm trying to achieve is a map scrolling when I drag the pin and reach edges of the map. When I move pin out of the edges I expect scrolling to stop and when I drop it, map shell move until pin reaches center of the screen.
I know how to center map on chosen location, but I'm not really have an idea how to scroll it while dragging pin.
It would really help if someone could just direct me to a logic of the way how to implement it.
MKMapRect mapRect = [self.mapView visibleMapRect];
CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(42.777126,-76.113281);
MKMapPoint point = MKMapPointForCoordinate(coordinate);
mapRect.origin.x = point.x - mapRect.size.width * 0.3;
mapRect.origin.y = point.y - mapRect.size.height * 0.70;
[self.mapView setVisibleMapRect:mapRect animated:YES];

iPhone iOS calculate location within a UIScrollView with zooming

I have a color map within a UIScrollView and am trying to sample the color of a pixel of this map. The sample reticle is positioned above the scrollview, while the user moves the contents of the scrollview under the reticle.
The user can drop the reticle with a tap gesture, but I would like to offer an extra option of moving the view under the reticle.
I'm trying to find out how I can understand what x,y coordinate of the zoomed view is currently under the reticle. The logic for this so far eludes me, especially since zooming in/out is involved.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGPoint mapLocation = [tapGestureRecognizer locationInView:self.surfaceMap];
NSLog(#"mapLocation (x,y) %.0f,%.0f",mapLocation.x,mapLocation.y);
NSLog(#"contentOffset (x,y) %.0f,%.0f",self.scrollView.contentOffset.x,self.scrollView.contentOffset.y);
//calculate where the marker is pointing to in the surface map while the scrollview is scrolling
int frameWidth = self.surfaceMap.frame.size.width;
int frameHeight = self.surfaceMap.frame.size.height;
//this is what I'm trying to calculate
CGPoint trueLocation = CGPointMake(self.scrollView.contentOffset.x+frameWidth-self.surfaceMap.frame.origin.x, self.scrollView.contentOffset.y-self.surfaceMap.frame.origin.y);
NSLog(#"trueLocation (x,y) %.0f,%.0f",trueLocation.x,trueLocation.y);
[self colorOfPixelAtPoint:trueLocation];
}
Any input is appreciated!
You may want to a have look at these two methods in UIView:
- (CGPoint)convertPoint:(CGPoint)point toView:(UIView *)view;
- (CGPoint)convertPoint:(CGPoint)point fromView:(UIView *)view;

(iphone) how to set view.center when detaching a view from scroll view and adding it to another view?

I'd like to move a view from a scrollview to a uiview.
I'm having trouble changing it's center(or frame) so that it remains in the same position in screen (but in a different view, possibly the superview of scrollview).
How should I convert the view's center/frame?
Thank you.
EDIT:
CGPoint oldCenter = dragView.center;
CGPoint newCenter = [dragView convertPoint: oldCenter toView: self.navigationView.contentView];
dragView.center = newCenter;
[self.navigationView.contentView addSubview: dragView];
I can also use (NSSet*) touches since i'm in touchesBegan:
I was having hard time to make it work but the doc wasn't so clear to me.
You can use convertPoint:toView: method of UIView. It is used to convert a point from one view's coordinate system to another. See Converting Between View Coordinate Systems section of UIView class reference. There are more methods available.
-edit-
You are using the wrong point when calling convertPoint: method. The given point should be in dragView's coordinate system where as dragView.center is in its superview's coordinate system.
Use the following point and it should give you the center of dragView in its own coordinate system.
CGPoint p;
p = CGPointMake(dragView.bounds.size.width * 0.5, dragView.bounds.size.height * 0.5);

MKMapView ignores update of centerOffset in iOS 4

I previously created a custom callout bubble as a subview to the MKAnnotationView because the built in callout is so limited. This requires me to change to centerOffset of the MKAnnotationView when it is selected to account for the size of the callout bubble. This all worked perfectly before iOS 4 came out. Now, with iOS 4, it completely ignores my updating of the centerOffset property and therefore the pin and bubble appear to jump down and to the right (the top left corner of the callout bubble is now at the location where the pin point should be).
Does anyone know why this has changed in iOS 4? Is there something I can do to get the MKMapView to recognize the new centerOffset? Is this a bug that apple introduced?
Thanks for the help!
Make sure you are using MKAnnotationView and not MKPinAnnotationView! You can't set the centerOffset of a MKPinAnnotationView-object (except if you subclass of course).
I have the same problem - centerOffset seems to be taken into account only the first time.
It is changed internally, but the view is not moved - so what you need to do is move the view yourself.
You can move the view by adjusting its center with the required offset - the selected view remains aligned at the top-left corner with the unselected view, so you need to realign their centers. Here's my case:
Selected -> Unselected:
self.center = CGPointMake(self.center.x + 56.0, self.center.y + 130.0);
self.centerOffset = CGPointMake(5.0, -14.0);
Unselected -> Selected:
self.center = CGPointMake(self.center.x - 56.0, self.center.y - 130.0);
self.centerOffset = CGPointMake(64.0, -81.0);
Where 130 is the difference in height between the views(center point is at the bottom), and 56 is the difference between the X offsets of their centers.
Remember - you still need to change the center offset because it'll be taken into account when zooming.
Hope this helps, I've lost a few hours on this. Remember to submit a bug report to Apple.
I think instead of centerOffset you can use setRegion which works fine in all versions.
CGPoint point = [mapView convertCoordinate:selectedAnnotation.coordinate toPointToView:self.view];
CGRect frame = [customView frame];
frame.origin.y = point.y - frame.size.height;
frame.origin.x = point.x - frame.size.width / 2;
MKCoordinateRegion region = [mapView convertRect:frame toRegionFromView:self.view];
[mapView setRegion:region animated:YES];

Rotate MapView using Compass orientation

Is it possible to have an embedded MKMapView rotate to always face the direction the iPhone is facing? Basically I want to mimic the Map app rotation feature on my own app.
I see that the iPhone SDK does not expose the functionality. However, I wonder if it would work to rotate the entire view using CGAffineTransformMakeRotate. Would it affect tapping and zooming? Is there a better way?
To rotate the mapView but not the annotations you could use the following code to compensate for the maps rotation.
- (void)locationManager:(CLLocationManager *)manager
didUpdateHeading:(CLHeading *)newHeading
{
double rotation = newHeading.magneticHeading * 3.14159 / 180;
CGPoint anchorPoint = CGPointMake(0, -23); // The anchor point for your pin
[mapView setTransform:CGAffineTransformMakeRotation(-rotation)];
[[mapView annotations] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
MKAnnotationView * view = [mapView viewForAnnotation:obj];
[view setTransform:CGAffineTransformMakeRotation(rotation)];
[view setCenterOffset:CGPointApplyAffineTransform(anchorPoint, CGAffineTransformMakeRotation(rotation))];
}];
}
Another sollution is using a new method that has been added in iOS 5 to MKMapView.
Take a look at: http://developer.apple.com/library/ios/#documentation/MapKit/Reference/MKMapView_Class/MKMapView/MKMapView.html
- (void)setUserTrackingMode:(MKUserTrackingMode)mode animated:(BOOL)animated;
I can confirm that it works fine. Here's the code that I'm using:
[mapView setTransform:CGAffineTransformMakeRotation(-1 * currentHeading.magneticHeading * 3.14159 / 180)];
mapView is my MKMapView instance
Simple solution in swift 3.0. Make sure to put the line in mapViewDidFinishLoadingMap or it will ignore it
public func mapViewDidFinishLoadingMap(_ mapView: MKMapView)
{
mapView.setUserTrackingMode(.followWithHeading, animated: false)
}
If you don't want the map to center on the user location you might do something like this
public func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading)
{
if(CLLocationCoordinate2DIsValid(mapView.centerCoordinate))
{
mapView.camera.heading = newHeading.trueHeading
}
}
If you look in the 3.0 iPhone Application Programming Guide under Device Support you'll find information on the magnetometer (aka compass). Once you start getting heading information (through the didUpdateHeading method) you should be able to get the compass data then use that to calculate the proper rotation transform value to apply to the map view.
Not sure if it handles zooming. In the standard map application I've noticed that compass heading tracking stops as soon as you start pinch-zooming.
Also, keep in mind that location directions are in degrees whereas transform rotation angles are in radians.
if you use a navigationVontroller try this:
//For Left button
MKUserTrackingBarButtonItem *buttonItem = [[MKUserTrackingBarButtonItem
alloc]initWithMapView:_mapView];
self.navigationItem.leftBarButtonItem = buttonItem;
// For Right Button
MKUserTrackingBarButtonItem *buttonItem = [[MKUserTrackingBarButtonItem
alloc]initWithMapView:_mapView];
self.navigationItem.rightBarButtonItem = buttonItem;