0

OK the code is now working, but it still needs work. The values I get are "sticky", they are not stable (the magnetic North seems to move a bit every time I try to return to it), and I need to shake the device a bit in order to refresh/wake-up the values..

Game.h

#import <Foundation/Foundation.h>

#import "CoreLocation.h"

@interface Game : NSObject

<CLLocationManagerDelegate>

@property BOOL stopButtonPressed;

-(void) play;

@end

Game.m

@implementation Game

- (id) init 
{
    self = [super init];

    self.stopButtonPressed = NO;

    CLLocationManager *locationManager;

    locationManager = [[CLLocationManager alloc] init];

    locationManager.delegate = self;

    return self;
}

-(void) play 
{

    [locationManager startUpdatingHeading]; 

    while(!self.stopButtonPressed)
    {
         double degrees = locationManager.heading.magneticHeading;

         int degreesRounded = (int)degrees;

         NSLog(@"Degrees : %i", degreesRounded);
    }
}

@end

MyViewController.m

@interface MyViewController()
{
    Game *game;
}
@end

@implementation MyViewController

-(void) viewDidLoad
{
    game = [[Game alloc] init];
}

- (IBAction)playPressed:(UIButton *)sender 
{
    [game performSelectorInBackground:@selector(play) withObject:nil];
}

- (IBAction)stopPressed:(UIButton *)sender 
{
    game.stopButtonPressed = YES;
}

@end

What am I doing wrong?

2 Answers2

2

This code will block the thread, and if it's happening in the main thread, you will never get the button press.

CLLocationManager is an asynchronous mechanism. To work with it properly, you must provide a delegate which it will notify when updates to location are available (this can be self in most cases (where self is a viewController or similar). See CLLocationManagerDelegate docs

...
    CLLocationManager *locationManager;
    locationManager = [[CLLocationManager alloc] init];
    locationManager.delegate = self;
    [locationManager startUpdatingHeading];
}

- (void)locationManager:manager didUpdateHeading:newHeading {
    double degrees = newHeading.magneticHeading;
     NSLog(@"Degrees : %F", degrees);
}
Chris Trahey
  • 18,202
  • 1
  • 42
  • 55
  • it doesn't seem to be blocking the main thread, I get "Heading is available" on the console and then I just keep getting "Degrees : 0.000" –  Jul 30 '12 at 14:18
  • Right, in an infinite loop. It's not frozen, it's just that this code won't let anything else occur. – Chris Trahey Jul 30 '12 at 14:20
  • I'm also executing all this in a -(void) play method that runs in a separate thread : '[game performSelectorInBackground:@selector(play) withObject:nil];' –  Jul 30 '12 at 14:22
  • Ah, so it's not blocking the `main` thread, which means you can still press the button and get out of that loop. Better; but the essence of the answer remains your best option: leverage the CLLocationManagerDelegate Callbacks :-) – Chris Trahey Jul 30 '12 at 14:23
  • I edited my original post and included the whole of my code so that you can have a better picture of what I'm doing. I'm new to Obj-C and I'm not sure how delegating works. I'll try to implement your code in mine and see what I get.. –  Jul 30 '12 at 14:31
  • Is this part : CLLocationManager *locationManager; locationManager = [[CLLocationManager alloc] init]; locationManager.delegate = self; [locationManager startUpdatingHeading]; supposed to go in my -(id) init in Game.m ?? –  Jul 30 '12 at 14:34
  • It is far more common to put code like that into a ViewController, and leave your `Game` to it's own domain. The delegate needs to conform to the appropriate protocol (add `` to it's `@interface` line). However, if location updates are vital to your "Game", then it may be better for the `Game` to be the delegate in this case (then add the delegate methods to `Game` class and set it as the delegate on locationManager) – Chris Trahey Jul 30 '12 at 14:41
  • hmm... you lost me there.. have to check the documentation again although it's not very helpful.. more confusing actually.. it just presents methods assuming you know where everything goes... –  Jul 30 '12 at 14:45
  • Edited my original post and replaced it with working code. It's still not perfect but I'll save the rest for a different Question I guess.. Please tell me whether there is something wrong with this new version.. Thanks! –  Jul 30 '12 at 18:59
  • I would remove the while loop, and instead implement the `- (void)locationManager:manager didUpdateHeading:newHeading` method in `Game`. This method, if you implement it, will be called automatically when the location information becomes available. It's pretty slick if you're new to asynchronous programming, and a far better approach than what I see there (in fact, if you do that, you don't need to fork your own thread) – Chris Trahey Jul 31 '12 at 16:38