Use native iOS compass within an app - objective-c

Is it possible to use the native compass that iOS has within my own application? Or do I need to draw and animate my own compass?

There is no native compass UIView. In order to use the magnetometer, you'll have to use CoreLocation and the following delegate method:
- (void) locationManager:(CLLocationManager *)manager
didUpdateHeading:(CLHeading *)newHeading
to rotate a UIView to point North (bearingView is a UIImageView):
float heading = newHeading.magneticHeading; //in degrees
float headingDegrees = (heading*M_PI/180); //assuming needle points to top of iphone. convert to radians
self.bearingView.transform = CGAffineTransformMakeRotation(headingDegrees);

Related

How to improve magnification and rotation gesture recogniser methods in Objective-C?

I'm playing with NSGestureRecognizer(s) in Objective-C.
I have a simple Custom View in the XIB canvas to which I applied the press, pan, magnify, and rotate gesture recognisers. From each one I have created an action in AppDelegate.m and then added code to it. They all work to some extent but the way magnify and rotate behave does not satisfy me.
Here is the code for both of them:
- (IBAction)magnifyView:(NSMagnificationGestureRecognizer *)sender {
CGFloat magnification = sender.magnification + 1.0;
NSView *view = sender.view;
CGAffineTransform transform = CGAffineTransformMakeScale(magnification, magnification);
[[view layer] setAffineTransform:transform];
sender.magnification = 0;
}
and ...
- (IBAction)rotateView:(NSRotationGestureRecognizer *)sender {
CGFloat rotation = sender.rotation;
NSView *view = sender.view;
CGAffineTransform transform = CGAffineTransformMakeRotation(rotation);
[[view layer] setAffineTransform:transform];
sender.rotation = 0;
}
I'm using a 2016 MBP with macOS 12.4 and Xcode 13.4.1 to realise this and, using the trackpad for the gestures, I see that magnifyView seems to stutter unless I strongly open my fingers in an "unpinch" gesture—which risks triggering the macOS show desktop, while rotateView only rotates of a few degrees before coming back.
The whole project can be found here, if you feel like giving it a look. It doesn't contain much else anyway, it's an experiment.
Could you please give it a look (or even just at the methods above) and tell me what I could do to improve it?
Thank you!

IOS Programmatic tap using Earlgrey in Objective-c

Is it possible to use EarlGrey to programmatically tap the device view on objective c?
I tried doing that using this API with a tap recogniser:
CGPoint point;
point.x =2;
point.y=2;
[GREYActions actionForTapAtPoint:point];
It failed to get the tap event

MKMapView in reverse, when compass mode using in my iphone app project

I am developing an iphone application,in that app i am showing the client Parking places in different locations on MKMapView. Now I need to use compass mode in MKMapView to show the client parking places in particular direction (SE,SW,NE,NW), for that I run this below code.
-(void)updateHeading:(CLHeading *) newHeading
{
NSLog(#"New magnetic heading: %f", newHeading.magneticHeading);
NSLog(#"New true heading: %f", newHeading.trueHeading);
double rotation = newHeading.magneticHeading * 3.14159/-180;
[mapView setTransform:CGAffineTransformMakeRotation(-rotation)];
[[mapView annotations] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
MKAnnotationView * view = [mapView viewForAnnotation:obj];
[view setTransform:CGAffineTransformMakeRotation(rotation)];
}];
}
Everthing works fine in MKMapView, but my MKMapView shows in reverse order, when the device starts rotating, I am facing this problem in ios5 and ios6 also.
NOTE: when I test this app in America, map shows correctly, while I test the app in my location (India) Map turns into reverse.
`
Thanks in advance for any help.
Is the map intended to be centered on the user while rotating? If so you could just set the MKMapView's userTrackingMode to MKUserTrackingModeFollowWithHeading.
If not then how about telling us what you see in magneticHeading, trueHeading and rotation when things go right and when they go wrong.

iOS - Math help - base image zooms with pinch gesture need overlaid images adjust X/Y coords relative

I have an iPad application that has a base image UIImageView (in this case a large building or site plan or diagram) and then multiple 'pins' can be added on top of the plan (visually similar to Google Maps). These pins are also UIImageViews and are added to the main view on tap gestures. The base image is also added to the main view on viewDidLoad.
I have the base image working with the pinch gesture for zooming but obviously when you zoom the base image all the pins stay in the same x and y coordinates of the main view and loose there relative positioning on the base image (whose x,y and width,height coordinates have changed).
So far i have this...
- (IBAction)planZoom:(UIPinchGestureRecognizer *) recognizer;
{
recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale);
recognizer.scale = 1;
for (ZonePin *pin in planContainer.subviews) {
if ([pin isKindOfClass:[ZonePin class]]){
CGRect pinFrame = pin.frame;
// ****************************************
// code to reposition the pins goes here...
// ****************************************
pin.frame = pinFrame;
}
}
}
I need help to calculate the math to reposition the pins x/y coordinates to retain there relative position on the zoomed in or out plan/diagram. The pins obviously do not want to be scaled/zoomed at all in terms of their width or height - they just need new x and y coordinates that are relative to there initial positions on the plan.
I have tried to work out the math myself but have struggled to work it through and unfortunately am not yet acquainted with the SDK enough to know if there is provision available built in to help or not.
Help with this math related problem would be really appreciated! :)
Many thanks,
Michael.
InNeedOfMathTuition.com
First, you might try embedding your UIImageView in a UIScrollView so zooming is largely accomplished for you. You can then set the max and min scale easily, and you can scroll around the zoomed image as desired (especially if your pins are subviews of the UIImageView or something else inside the UIScrollView).
As for scaling the locations of the pins, I think it would work to store the original x and y coordinates of each pin (i.e. when the view first loads, when they are first positioned, at scale 1.0). Then when the view is zoomed, set x = (originalX * zoomScale) and y = (originalY * zoomScale).
I had the same problem in an iOS app a couple of years ago, and if I recall correctly, that's how I accomplished it.
EDIT: Below is more detail about how I accomplished this (I'm looking my old code now).
I had a UIScrollView as a subview of my main view, and my UIImageView as a subview of that. My buttons were added to the scroll view, and I kept their original locations (at zoom 1.0) stored for reference.
In -(void)scrollViewDidScroll:(UIScrollView *)scrollView method:
for (id element in myButtons)
{
UIButton *theButton = (UIButton *)element;
CGPoint originalPoint = //get original location however you want
[theButton setFrame:CGRectMake(
(originalPoint.x - theButton.frame.size.width / 2) * scrollView.zoomScale,
(originalPoint.y - theButton.frame.size.height / 2) * scrollView.zoomScale,
theButton.frame.size.width, theButton.frame.size.height)];
}
For the -(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView method, I returned my UIImageView. My buttons scaled in size, but I didn't include that in the code above. If you're finding that the pins are scaling in size automatically, you might have to store their original sizes as well as original coordinates and use that in the setFrame call.
UPDATE...
Thanks to 'Mr. Jefferson' help in his answer above, albeit with a differing implementation, I was able to work this one through as follows...
I have a scrollView which has a plan/diagram image as a subview. The scrollView is setup for zooming/panning etc, this includes adding UIScrollViewDelegate to the ViewController.
On user double tapping on the plan/diagram a pin image is added as a subview to the scrollView at the touch point. The pin image is a custom 'ZonePin' class which inherits from UIImageView and has a couple of additional properties including 'baseX' and 'baseY'.
The code for adding the pins...
- (IBAction)planDoubleTap:(UITapGestureRecognizer *) recognizer;
{
UIImage *image = [UIImage imageNamed:#"Pin.png"];
ZonePin *newPin = [[ZonePin alloc] initWithImage:image];
CGPoint touchPoint = [recognizer locationInView:planContainer];
CGFloat placementX = touchPoint.x - (image.size.width / 2);
CGFloat placementY = touchPoint.y - image.size.height;
newPin.frame = CGRectMake(placementX, placementY, image.size.width, image.size.height);
newPin.zoneRef = [NSString stringWithFormat:#"%#%d", #"BF", pinSeq++];
newPin.baseX = placementX;
newPin.baseY = placementY;
[planContainer addSubview:newPin];
}
I then have two functions for handling the scrollView interaction and this handles the scaling/repositioning of the pins relative to the plan image. These methods are as follows...
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return planImage;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
for (ZonePin *pin in planContainer.subviews) {
if ([pin isKindOfClass:[ZonePin class]]){
CGFloat newX, newY;
newX = (pin.baseX * scrollView.zoomScale) + (((pin.frame.size.width * scrollView.zoomScale) - pin.frame.size.width) / 2);
newY = (pin.baseY * scrollView.zoomScale) + ((pin.frame.size.height * scrollView.zoomScale) - pin.frame.size.height);
CGRect pinFrame = pin.frame;
pinFrame.origin.x = newX;
pinFrame.origin.y = newY;
pin.frame = pinFrame;
}
}
}
For reference, the calculations for position the pins, by the nature of them being pins' centres the pin image on the x axis but has the y-axis bottom aligned.
The only thing left for me to do with this is to reverse the calculations used in the scrollViewDidScroll method when I add pins when zoomed in. The code for adding pins above will only work properly when the scrollView.zoomScale is 1.0.
Other than that, it now works great! :)

Drawing lat/lng points on an image

I have the following:
An image - a hand drawn map - of an area of roughly 600x400 meters. The image is drawn on top of Google Maps tiles.
The latitude/longitude (from Google Maps) of the corners of this image. Or put differently, I have the north and south latitude and the east and west longitude of the image.
A latitude/longitude coordinate from iPhone's CoreLocation.
How do I draw a point on this image (or nothing if it's out of bounds), representing the coordinate from CoreLocation?
Added bonus: draw an arrow on the edge of the map, pointing to the coordinate, if the coordinate is out of bounds of the image.
I would like to do this without using a library like proj, in order to not have to bundle a large library, and understand what I'm doing and why.
As you probably guessed by know, I'm writing this in Objective-C. Your answer doesn't have to be in Objective-C, though.
If I understand it correctly, you need to do two things. The first is to put your custom image into a map view and have your custom tiles appear at the correct coordinates, then pan, zoom and so on. The second thing you need to do is to draw a point onto that image at a certain latitude and longitude.
What you need is custom overlays, available in iOS 4 and up. The best place to find out about custom overlays is the WWDC 2010 video called "Session 127 - Customizing Maps with Overlays". There is also custom code available for the video. In the video, the presenter creates a custom map and embeds it in an MKMapView. He also describes a tool which you can use to make your tiles (to cut them up, get their shapes into the Mercator projection and name them properly). His map is scanned from a marine map, then placed on top of the normal map view.
You would be able to use boundingMapRect to create a bounds rectangle by converting your custom map's bounds to points. You can convert between points and coordinates using MKMapPointForCoordinate and MKCoordinateForMapPoint.
As for getting a point drawn on the map, you can do this a couple of ways. The easiest is to just use a custom MKAnnotationView with a dot as its image. This way the image doesn't grow and shrink as you zoom in. If you want the dot to grow and shrink, you should use a custom overlay for that too. You could easily use an MKCircleView, which is a subclass of MKOverlayView
For an arrow, you could use a normal view and rotate it (and place it on one side of the screen) according to the direction of your out-of-bounds point. Use MKMapPointForCoordinate and then calculate the directtion from the centre of your view.
But your best source is going to be that video. He goes into great depth about the whole process and gives source for a working app which is 90% of what you need for your own map.
After some research, I wrote my own library: libPirateMap. It's not very polished, but it works.
In case the link goes down, I'll paste the relevant source code here.
Usage:
// .h
PirateMap *pirateMap;
PirateMapPoint *pirateMapPoint;
// .m
- (void)viewDidLoad {
[super viewDidLoad];
pirateMap = [[PirateMap alloc] initWithNorthLatitude:59.87822
andSouthLatitude:59.87428
andWestLongitude:10.79847
andEastLongitude:10.80375
andImageWidth:640
andImageHeight:960];
pirateMapPoint = [[PirateMapPoint alloc] init];
pirateMapPoint.pirateMap = pirateMap;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
pirateMapPoint.coordinate = PirateMapCoordinate2DMake(newLocation.coordinate.latitude, newLocation.coordinate.longitude)
PirateMapPoint2D point = [pirateMapPoint pointOnImage];
// use point.x and point.y to place your view.
}
Relevant .m-code.
#import "PirateMap.h"
static const double RAD_TO_DEG = 180 / M_PI;
static const double DEG_TO_RAD = M_PI / 180;
PirateMapCoordinate2D PirateMapCoordinate2DMake(double latitude, double longitude) {
return (PirateMapCoordinate2D) {latitude, longitude};
}
// atan2(y2-y1,x2-x1)
#implementation PirateMap
#synthesize northLatitude, southLatitude, westLongitude, eastLongitude,
imageWidth, imageHeight, latitudeImageToWorldRatio, longitudeImageToWorldRatio;
-(id)initWithNorthLatitude:(double)aNorthLatitude
andSouthLatitude:(double)aSouthLatitude
andWestLongitude:(double)aWestLongitude
andEastLongitude:(double)anEastLongitude
andImageWidth:(int)anImageWidth
andImageHeight:(int)anImageHeight{
if (self = [super init]) {
self.northLatitude = aNorthLatitude;
self.southLatitude = aSouthLatitude;
self.westLongitude = aWestLongitude;
self.eastLongitude = anEastLongitude;
self.imageWidth = anImageWidth;
self.imageHeight = anImageHeight;
self.latitudeImageToWorldRatio = [self computeLatitudeImageToWorldRatio];
self.longitudeImageToWorldRatio = [self computeLongitudeImageToWorldRatio];
}
return self;
}
-(double)computeLatitudeImageToWorldRatio {
return fabs(self.northLatitude - self.southLatitude) / self.imageHeight;
}
-(double)computeLongitudeImageToWorldRatio {
return fabs(self.eastLongitude - self.westLongitude) / self.imageWidth;
}
+(double)latitudeToMercatorY:(double)latitude {
static const double M_PI_TO_4 = M_PI / 4;
return RAD_TO_DEG * log(tan(M_PI_TO_4 + latitude * (DEG_TO_RAD / 2)));
}
#end
#import "PirateMapPoint.h"
PirateMapPoint2D PirateMapPoint2DMake(int x, int y) {
return (PirateMapPoint2D) {x, y};
}
#implementation PirateMapPoint
#synthesize pirateMap, coordinate;
-(id)initWithPirateMap:(PirateMap *)aPirateMap andCoordinate:(PirateMapCoordinate2D)aCoordinate {
if (self = [super init]) {
self.pirateMap = aPirateMap;
self.coordinate = aCoordinate;
}
return self;
}
-(PirateMapPoint2D)pointOnImage {
double xDelta = self.coordinate.longitude - self.pirateMap.westLongitude;
double yDelta = self.pirateMap.northLatitude - self.coordinate.latitude;
return PirateMapPoint2DMake(round(xDelta / self.pirateMap.longitudeImageToWorldRatio), round(yDelta / self.pirateMap.latitudeImageToWorldRatio));
}
#end
Have you looked into using MapKit? It has methods for converting map coordinates to view coordinates. Have a look at the convert family of methods.
http://developer.apple.com/library/ios/#documentation/MapKit/Reference/MKMapView_Class/MKMapView/MKMapView.html
If you are on 4.0 only, you might benefit from the overlay class as well.
http://developer.apple.com/library/ios/#documentation/MapKit/Reference/MKOverlayView_class/Reference/Reference.html
Cheers!