I am making a simple map app - I am sending and receiving locations and text on a pubnub channel. When a chat message comes in, I want to use a simple MKAnnotation to draw the chat (I'm aware this is a horrifying act of UX and I don't care).
When my app delegate receives a message on the pubnub channel, it calls a method in the main view controller to draw the text message on the map. The method should use the latest user location for the pin's coordinates.
I'm not sure why, but I can't get the annotation to show from within my method. I have tried building the annotation from within the method and showing it. I've also tried making a custom annotation class and calling it from within my method. When I use the very same annotation code but hardcode it in my viewDidLoad then it shows up just fine. Any insight would be most appreciated.
My App Delegate:
#import "MyLocationAppDelegate.h"
#import "MyLocationViewController.h"
#implementation MyLocationAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[PubNub setDelegate: self];
return YES;
}
- (void)pubnubClient:(PubNub *)client didReceiveMessage:(PNMessage *)message
{
NSString* text = message.message;
//Call drawChat method of MyLocationViewController
MyLocationViewController *MyLocViewController = [[MyLocationViewController alloc] init];
[MyLocViewController drawChat:text];
}
My View Controller:
#import "MyLocationViewController.h"
#import "MyLocationAppDelegate.h"
#import "MyLocationAnnotation.h"
CLLocation *userLocation;
#implementation MyLocationViewController {
CLLocationManager *locationManager;
}
- (void)viewDidLoad
{
[super viewDidLoad];
//Delegate map view
self.mapView.delegate = self;
[self SetUpChat];
[self configurePubNub];
//Instantiate location manager
if (nil == locationManager) {
locationManager = [[CLLocationManager alloc] init];
}
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
NSLog(#"Application: viewDidLoad");
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
userLocation = [locations lastObject];
}
- (void)drawChat:(NSString *)message
{
//Create new annotation object
CLLocationCoordinate2D location;
location.latitude = userLocation.coordinate.latitude;
location.longitude = userLocation.coordinate.longitude;
MyLocationAnnotation *chat = [[MyLocationAnnotation alloc] initWithLocation:location andTitle:message];
[self.mapView addAnnotation:chat];
}
And MyLocationAnnotation.m
#import "MyLocationAnnotation.h"
#implementation MyLocationAnnotation
- (id)initWithLocation:(CLLocationCoordinate2D)coord andTitle:(NSString *)ttl {
self = [super init];
if (self) {
_coordinate = coord;
_title = ttl;
}
return self;
}
#end
Thanks to Anna's pointing out what I was doing wrong I was able to fix this:
Instead of redclaring my main view controller in the app delegate I did this instead:
MyLocationViewController *mainController = (MyLocationViewController *) self.window.rootViewController;
[mainController drawChat:text];
I was then able to call the method just fine with the data from app delegate.
Related
I'm trying to put everything location related inside a model. When I call this my MainViewController, the simulator doesn't ask me for my location, and nothing happens.
When I use the same code from my model, but put it directly in ViewDidLoad in my ViewController, everything works. I'm having a hard time understanding why.
Here is my model:
#implementation Location
{
CLLocationManager *_locationManager;
CLLocation *_location;
}
- (void)startLocationManager
{
NSLog(#"In startLocationManager");
_locationManager = [[CLLocationManager alloc] init];
_locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
_locationManager.delegate = self;
[_locationManager startUpdatingLocation];
}
#pragma mark - LocationManager Delegates
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
NSLog(#"In didUpdateLocations");
if (locations) {
_location = [locations lastObject];
NSLog(#"%#", _location);
}
}
#end
I call this in my MainViewController like this:
- (void)viewDidLoad
{
[super viewDidLoad];
Location *location = [[Location alloc] init];
[location startLocationManager];
}
Why does the code work like a charm directly in the viewController, but not through my model?
I figured it out.
I needed to make location a property of your view controller instead of a local variable in the viewDidLoad. Otherwise it is created and deallocated within that method. I need it to live through the lifecycle of my view controller.
I'm trying to make an iOS7 app that uses the current location of the device. I'm using the iPhone simulator on my Mac, but I'm having some problems. Every time my view that the location manager is in appears, it prints out 0.000000 for both latitude and longitude, even after I've set a custom location (from simulator>debug>location).
Also, it seemed strange that the simulator didn't ask for permission to use current location when it opened the app. Anybody know what's going on here?
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[super viewDidLoad];
CLLocationManager *locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters; // 100 m
[locationManager startUpdatingLocation];
_location = [locationManager location];
_coord.longitude = _location.coordinate.longitude;
_coord.latitude = _location.coordinate.latitude;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
_coord.longitude = _location.coordinate.longitude;
_coord.latitude = _location.coordinate.latitude;
printf("%f\n",self.coord.longitude);
printf("%f\n",self.coord.latitude);
}
You need to get the newLocation from the delegate method didUpdateLocationToLocation:fromLocation:. Also implement didFailWithError delegate method. It takes some time before you start getting updated locations, hence the delegate call.
The last location is usually cached, so it maybe wise to check location's timestamp and filter the old location out.
Edit:
This is the cleanest example I can provide. Start new project in Xcode, pick Single View application template, iPhone. Don't touch storyboard, just replace content of your ViewController.m with this and run in Simulator or device. If on Simulator, go to Debug and set some location and you will get coordinates in the console. I am also starting and stopping location updates when the view goes on or off screen.
#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
#interface ViewController () <CLLocationManagerDelegate>
#property (strong, nonatomic) CLLocationManager *locationManager;
#end
#implementation ViewController
#pragma mark - Location Manager delegate methods
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
if ([newLocation.timestamp timeIntervalSinceNow] >= -300.0) {
NSLog(#"updated location with latitude %f longitude %f", newLocation.coordinate.longitude, newLocation.coordinate.latitude);
}
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.locationManager startUpdatingLocation];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.locationManager stopUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
if(error.code == kCLErrorDenied) {
// alert user
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Access to location services is disabled"
message:#"You can turn Location Services on in Settings -> Privacy -> Location Services"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
} else if(error.code == kCLErrorLocationUnknown) {
NSLog(#"Error: location unknown");
} else {
NSLog(#"Error retrieving location");
}
}
#pragma mark - Location Manager getter
- (CLLocationManager *)locationManager
{
if (!_locationManager) {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
_locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
_locationManager.distanceFilter = 60.0;
}
return _locationManager;
}
#end
I'm making an app that determines a user's location and makes the [locationManager startUpdatingLocation] call in the initWithNibName function, but initWithNibName isn't being called right off the back so I added a call for it in the AppDelegate.m:
WhereamiViewController *wvc = [[WhereamiViewController alloc] initWithNibName:#"WhereamiViewController" bundle:nil];
This works, however then there is a problem with calling this function within the initWithNibName function: [locationManager startUpdatingLocation]. It literally returns nothing. But, if I change my code to call initWithNibName in my viewDidLoad line, it works. Why is this? I'm very confused and have been searching for answers but I'm at a standstill. I understand initWithNibName needs to be called, but I don't understand why it won't work if I call it from AppDelegate.m.
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations`
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error
Here is my initWithNibName function:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
// Set a movement threshold for new events.
locationManager.distanceFilter = 500; // meters
[locationManager startUpdatingLocation];
}
return self;
}
you do have a Xib file set up right?
things to try:
if you are not using Nibs then create a method called init:
- (id) init {
self = [super init];
if(self) {
//call code here
}
return self;
}
and then instead of calling nib method in app delegate just call init
or second option would be to call the code straight in viewdidload instead of doing above
I have the following code in my ViewController:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
int desiredHA = 200;
RemoteDataController *rdc = [[RemoteDataController alloc]init];
double ha = newLocation.horizontalAccuracy;
if (ha <= desiredHA)
{
[rdc addLoc];
[self.locationManager stopUpdatingLocation];
return;
}
}
-(void)startLogging
{
if(self.locationManager==nil)
{
self.locationManager=[[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy=kCLLocationAccuracyBest;
self.locationManager.distanceFilter=kCLDistanceFilterNone;
}
if([CLLocationManager locationServicesEnabled])
{
NSLog(#"Start Location Tracking");
[self.locationManager startUpdatingLocation];
}
}
-(void)addLocResponse
{
NSLog(#"send checkin response");
self.silenceTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self
selector:#selector(onTick:) userInfo:nil repeats:NO];
}
-(void)onTick:(NSTimer *)timer
{
[self startLogging];
}
Then my RemoteDataController.m file looks like this:
#implementation RemoteDataController
-(void)addLoc
{
ViewController *vc = [[ViewController alloc]init];
[vc addLocResponse];
NSLog(#"Add Loc");
}
#end
I know this looks stupid right now but I stripped out a lot of the details of it so it's not too complicated.
My question is when I call addLocResponse from the RemoteDataController class the timer runs and then hits the onTick which fires of startLogging again. I can see it's running startLogging again from the NSLog but it does not run the locationManager delegate again.
If I keep all this in the ViewController it works fine but when I try to go out to RemoteDataController and back it does not work.
I am just trying to figure out what I am doing wrong here.
This is iOS6.
Any help would be great.
Thanks in advance!
You are not using a correct initializer for your view controller. Also, put a breakpoint on the dealloc method of your ViewController instance. Chances are it's being deallocated (if you are using ARC) because you are not doing anything with it (modal presentation or push).
I'm getting an error in my IOS application. I've searched in the google and here, but the specific solution was not found!
I have a viewController called mapView that I use in two moments in my app, this view contains a MKMapView and the code.
In my mapView.h there is:
#property (strong, nonatomic) IBOutlet MKMapView *mapSpot;
And in my mapView.m there is:
- (void)viewDidLoad {
[super viewDidLoad];
[mapSpot setShowsUserLocation:YES];
}
- (void) mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation{
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance([userLocation coordinate], 500, 500);
[mapSpot setRegion:region animated:YES];
}
So, in the first moment I load the mapView into other ViewController using:
#property (strong, nonatomic) ViewMap *mapView;
mapView = [[ViewMap alloc] initWithNibName:#"ViewMap" bundle:nil];
[self.view addSubview:[mapView view]];
I unload that ViewController and in another ViewController in other moment I load the MapView again, but in this moment the method: - (void) mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation not is called.
I verify if the first ViewController was unloaded and that was.
When I load the second ViewController there is a new instace of MapView, but not call the delegate method.
Anyone know something about that?
Thanks
==================================================================================
EDIT AND SOLVED:
the problem is in the way you are adding the view, in this line
[self.view addSubview:[mapView view]];
if you only add the view the controller code is not executed, instead of that you has to present the mapView, for example:
[self presentViewController:mapView animated:YES completion:nil];
The problem above, maybe happen because I'm using simulator to test app, and how the simulator not change the position map not get didUpdateUserLocation:
That's the unique explanation that I could have after the review the code, organize the classes read documentation and get error again.
Now, I'm using CLLocationManager to get position, after getting first time the position I stop it.
In the future I'll implement a system that track the user path, so using CLLocationManager is inevitable.
The mapView.m code after changes:
- (void)viewDidLoad {
[super viewDidLoad];
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
CLLocation *loc = [locations lastObject];
// store the location to use in any moment, it needs to be checked because the first time when get the coordinate not pass infos to load places according the current position
if (!location.latitude) {
location = [loc coordinate];
// set center the map at the current position
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(location, 500, 500);
[mapSpotView setRegion:region animated:YES];
[[NSNotificationCenter defaultCenter] postNotificationName:#"loadPlaces" object:nil];
[locationManager stopUpdatingLocation];
}
}
if someone has a better solution, please, post here!
That's it!