I am unable to figure out why the roll, pitch and yaw values are giving 0.0000 when logged.. I am sure it is a something that i miss but i cant figure it out..
This is the code:
//ViewController.m
#import "ViewController.h"
#interface ViewController (){
}
#property (nonatomic) CMMotionManager *motionManager;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.motionManager = [[CMMotionManager alloc] init];
CMDeviceMotion *devMotion = [[CMDeviceMotion alloc]init];
if ([self.motionManager isDeviceMotionAvailable]) {
NSLog(#"Device Motion Available");
[self.motionManager setDeviceMotionUpdateInterval:1.0/30.0];
// Pull mechanism is used
[self.motionManager startDeviceMotionUpdates];
}
devMotion = self.motionManager.deviceMotion;
NSLog(#"Roll Pitch and Yaw are %f, %f, %f",devMotion.attitude.roll, devMotion.attitude.pitch, devMotion.attitude.yaw);
}
I have gone thru this similar question: SO Question
Please help me understand this..
Thanks..
Updated Code:
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.motionManager = [[CMMotionManager alloc] init];
if ([self.motionManager isDeviceMotionAvailable]) {
NSLog(#"Device Motion Available");
[self.motionManager setDeviceMotionUpdateInterval:1.0/30.0];
// Pull mechanism is used
[self.motionManager startDeviceMotionUpdates];
}
CMDeviceMotion *devMotion = self.motionManager.deviceMotion;
NSLog(#"*Roll,Pitch and Yaw are %f, %f, %f",devMotion.attitude.roll, devMotion.attitude.pitch, devMotion.attitude.yaw);
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0 target:self selector:#selector(updateValues:) userInfo:nil repeats:YES];
}
-(void) updateValues:(NSTimer *)timer{
CMDeviceMotion *currDeviceMotion = self.motionManager.deviceMotion;
NSLog(#"Roll Pitch and Yaw are %f, %f, %f",currDeviceMotion.attitude.roll, currDeviceMotion.attitude.pitch, currDeviceMotion.attitude.yaw);
}
This code also has a large part of its initial values as 0.000000 . After that it starts to get values... So i guess there is some delay for startDeviceMotionUpdates for providing values to deviceMotion. So looks like i need to figure out how to save the first non zero values.
Notice that your devMotion variable won’t hold usable values (or any at all) right away as CMMotionManager takes a while to update the deviceMotion property after startDeviceMotionUpdates. So you should have some sort of timer that fires periodically and reads that property. Your linked article does something similar.
As long as the gyroscope is starting up, the deviceMotion property will be null (you’re only seeing 0.0 because messages to nil return zero).
As a sidenote: your call to [[CMDeviceMotion alloc] init] is useless as you then override the variable the result was assigned to with self.motionManager.deviceMotion.
Related
I am trying to increment a NSNumber every 5 seconds by 1 in Cocos2d but for some reason it isn't working (probably obvious mistake). I am using the update method but I think this may be the wrong place. Currently it is only adding +1 once. I need it to do this every 5 seconds constantly:
-(void) update: (CCTime) delta
{
// Save a string:
NSNumber *anInt = [NSNumber numberWithInt:500];
NSNumber *bNumber = [NSNumber numberWithInt:[anInt intValue] + 1];
NSLog(#"Update Number%#",bNumber);
}
An easy way to run something every 5 seconds would be to:
Create a property storing your number and a timer:
#property (nonatomic, assign) int bNumber;
#property (nonatomic, strong) NSTimer* timer;
Then initialize the number to 500 (I assume based on your example you want it to start at 500) and create the timer:
- (instanceType)init
{
self = [super init];
if (self)
{
self.bNumber = 500;
self.timer = [NSTimer scheduledTimerWithTimeInterval:5.0f
target:self
selector:#selector(incrementScore:)
userInfo:nil
repeats:YES];
}
}
Then create a method that does the increment:
- (void)incrementScore:(NSTimer *)timer
{
self.bNumber++;
NSLog(#"Number = %d", self.bNumber);
}
Don't forget to invalidate the timer when you are done:
- (void)dealloc
{
// If not using arc don't forget super dealloc
[super dealloc];
[self.timer invalidate];
self.timer = nil;
}
That is one way. If you want to use the update method in cocos2d then you need to be keeping track of the accumulated delta. Once that value has reached or exceeded the total number of milliseconds in 5 seconds, then you add 1 to the number. Delta is the number of milliseconds that have passed since the last update. So for example the properties will be:
#property (nonatomic, assign) int bNumber;
#property (nonatomic, assign) CCTime totalDelta;
Then in update you would do the following (there are 1000 milliseconds in a second):
- (void)update:(CCTime)delta
{
const CCTime FiveSeconds = 5000.0f;
self.totalDelta += delta;
if (self.totalDelta >= FiveSeconds)
{
self.totalDelta = 0;
self.bNumber++;
NSLog(#"Number = %d", self.bNumber);
}
}
I hope this helped. What you are trying to do is pretty simply so I recommend brushing up on Obj-C programming before jumping into making games.
I think method [self schedule:#selector(methodName) interval:intervalValue]; is your choice.
Code sample:
```
static CGFloat playTime = 0.0;
#implementation GameScene {
}
-(void)onEnter
{
CCLOG(#"on enter");
[self schedule:#selector(runTimer) interval:5.0];
[super onEnter];
}
-(void) runTimer {
playTime += 1;
}
```
I'm using MapBox and RMTileCache's beginBackgroundCacheForTileSource: to preload tiles before rendering maps in my client app. I want to preload the tiles for the currently visible area of the map from zoom level 3 thru zoom level 7. So, I pass the current bounds of my mapView using the rect returned from [mapView latitudeLongitudeBoundingBox]. My ViewController implements RMTileCacheBackgroundDelegate so as to get callbacks as the caching occurs and then completes. I do receive callbacks on didBeginBackgroundCacheWithCount:forTileSource: and didBackgroundCacheTile:withIndex:ofTotalTileCount: However, tileCacheDidFinishBackgroundCache: is never called.
I stepped thru the source of RMTileCache and noticed that the actual count of cached tiles in "progTile" never reaches the calculated total count in "totalTiles". Therefore, the final callback, tileCacheDidFinishBackgroundCache:, is not reached.
I'm not sure how to change (or if I should change) the totalTiles calculation. Perhaps I am passing the wrong viewing rect in my initial call? I'm not totally clear if that is correct. I can create a simple fix that calls tileCacheDidFinishBackgroundCache: in the case that caching simply finishes, but this seems to just hide the issue. Any guidance here would be appreciated.
For reference, my test code is straight forward:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
RMMapboxSource *onlineSource = [[RMMapboxSource alloc] initWithMapID:#"appleweed.control-room"];
mapView = [[RMMapView alloc] initWithFrame:self.view.bounds andTilesource:onlineSource];
//mapView.delegate = self;
mapView.bouncingEnabled = YES;
mapView.clusteringEnabled = YES;
mapView.clusterMarkerSize = CGSizeMake(40, 40);
mapView.clusterAreaSize = CGSizeMake(40, 40);
mapView.zoom = 1;
RMSphericalTrapezium rect = [mapView latitudeLongitudeBoundingBox];
mapView.tileCache.backgroundCacheDelegate = self;
[mapView.tileCache beginBackgroundCacheForTileSource:mapView.tileSource
southWest:rect.southWest
northEast:rect.northEast
minZoom:3.0
maxZoom:7.0];
}
- (void)tileCache:(RMTileCache *)tileCache didBeginBackgroundCacheWithCount:(int)tileCount forTileSource:(id <RMTileSource>)tileSource {
NSLog(#"start");
}
- (void)tileCache:(RMTileCache *)tileCache didBackgroundCacheTile:(RMTile)tile withIndex:(int)tileIndex ofTotalTileCount:(int)totalTileCount {
// float percentComplete = ((float)tileIndex / (float)totalTileCount) * 100;
//NSString *update = [NSString stringWithFormat:#"%.0f%%", percentComplete];
//NSLog(#"%#",update);
}
- (void)tileCacheDidFinishBackgroundCache:(RMTileCache *)tileCache {
NSLog(#"DONE!");
}
- (void)tileCacheDidCancelBackgroundCache:(RMTileCache *)tileCache {
NSLog(#"Canceled!");
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Is it stopping at the same tile count each time? Is there a map view visible at the same time? If you pause your app in the debugger, what is it doing once it seems to have stopped downloading tiles in the background?
I am pretty new to iOS coding. I need some help to fix an issue with CLLocation.
I use didUpdateLocations to retrieve location and speed information. I am able to get these details and display using NSLog.
However, I am unable to display it on UIview. How do I fix it?
Below is my code.
I use CoreLocationController to retrieve my location details.
#implementation CoreLocationController
#synthesize locMgr;
#synthesize location;
- (id)init {
self = [super init];
if(self != nil) {
self.locMgr = [[CLLocationManager alloc] init];
self.locMgr.delegate = self;
}
return self;
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations
{
DriveTrapViewController *driveTrapViewController = [[DriveTrapViewController alloc]init];
self.location = [locations lastObject];
//NSLog(#"Speed, %f", self.location.speed);
[driveTrapViewController locationUpdate:(CLLocation *)self.location];
}
I send the retrieved details back to DriveTrapViewController. I am able to display them using NSLog. But self.speedText.text is always NULL and nothing is showing on the screen.
#implementation DriveTrapViewController
#synthesize CLController;
#synthesize speedText = _speedText;
- (void)viewDidLoad {
[super viewDidLoad];
CLController = [[CoreLocationController alloc] init];
CLController.delegate = self;
[CLController.locMgr startUpdatingLocation];
}
- (void)locationUpdate:(CLLocation *)location
{
NSString *speed = [NSString stringWithFormat:#"SPEED: %f", location.speed];
NSLog(#"Speed %#",speed);
self.speedText.text = speed;
}
How do I fix this? Any help is appreciated. Thanks
Every time your locationManager:didUpdateLocations: method is called, it creates a new DriveTrapViewController, which is destroyed as soon as the method finishes. Maybe you should use an ivar for this instead. That way it wouldn't be created and destroyed every time.
Also, both objects seem to be making instances of the other. Instead of a Core Location problem, this is a general architecture-type problem.
I'd suggest that at this stage you should just have a CLLocationManager variable inside your DriveTrapController instead of trying to break it out into a controller of it's own.
Try CLController.locMgr.desiredAccuracy = kCLLocationAccuracyBestForNavigation;. this should include some extra information about the users location, speed, bearing etc...
I'm currently taking my first shaky steps in ios development. I'm trying to animate a free falling ball. I have a button connected to an IBAction, and and UIImageView containing an image of my ball.
Inside my action, I have a while loop that's timed using NSTimeInterval, and based on the time it takes it calculates a new position until the ball reaches 'the ground'(Yes, I realize this is probably the least optimal way to do it. But I'm having a hard time grasping the syntax (and therein my problem probably lays), the optimisation will have to come later). From what I can understand, NSTimeInterval returns the elapsed time in seconds, so even though it will increment incredibly small steps, it should work. But I may have a serious case of brain fart.
So far so good. But when I tap the button, the ball moves straight from it's starting point to it's finishing point without an animation.
-(IBAction)doshit:(id)sender{
int G = 10;
CGPoint center = [myImage center];
NSDate *startTime = [NSDate date];
NSTimeInterval T;
T = fabs([startTime timeIntervalSinceNow]);
while (center.y<480)
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:T];
center.y=center.y+((G*T*T)/2);
[myImage setCenter:center];
T = fabs([startTime timeIntervalSinceNow]);
[UIView commitAnimations];
}
}
I welcome all suggestions! =)
One way to do it is to use a CADisplayLink to provide the timing -- it is tied to the display refresh rate of 1/60 of a second. If you 're using auto layout (which is on by default now), it is better to animate a constraint, rather then set a frame. So, this example uses a button at the top of the screen, whose constraint to the top of the view is connected to the IBOutlet topCon.
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#interface ViewController ()
#property (nonatomic, strong) CADisplayLink *displayLink;
#property (weak,nonatomic) IBOutlet NSLayoutConstraint *topCon;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self performSelector:#selector(startDisplayLink) withObject:nil afterDelay:2];
}
- (void)startDisplayLink {
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(handleDisplayLink:)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)stopDisplayLink {
[self.displayLink invalidate];
self.displayLink = nil;
}
- (void)handleDisplayLink:(CADisplayLink *)displayLink {
static BOOL first = YES;
static double startTime = 0;
if (first) {
startTime = displayLink.timestamp;
}
first = NO;
double T = (double)displayLink.timestamp - startTime;
self.topCon.constant += ((10 * T * T)/2);
if (self.topCon.constant > 420) {
[self stopDisplayLink];
}
}
As Carl notes, you cannot perform animations by repeatedly changing things in the middle of a method call. See What is the most robust way to force a UIView to redraw? for more discussion on that.
As you may suspect, not only is this non-optimal, it actively fights iOS. iOS has many easy-to-use techniques for performing smooth animations without resorting to timers of any kind. Here is one simple approach:
- (IBAction)dropAnimate:(id)sender {
[UIView animateWithDuration:3 animations:^{
self.circleView.center = CGPointMake(100, 300);
}];
}
In the animations block, you change the thing you want to change to the final value (myImage.center in your case). UIKit will sample the current value and the final value, and figure out a path to get you there in the time you requested. For a full, runnable example with a few other features (like chained animations), see the example code from iOS:PTL Chapter 9.
The above code will use the default timing function. Once you understand that, you can move on to customizing the timing function. See Animation Pacing in the Animation Types and Timing Programming Guide. Also How to create custom easing function with Core Animation? and Parametric acceleration curves in Core Animation. But I would get your head around simple, default animations first.
I have an app that uses a GoogleMap view.
Into that app, I want to display some custom anotations, and a custom view for the userLocation. When the location manager gives a heading, I want to display that custom view for userLocation, if it fails to deliver one, I want the default blue animated dot to come back, etc...
Project and Source code available HERE
Well, there are 2 problems here :
1st problem, THE REALLY BAD ONE. You can play for hours with that tiny app. But... Launch it, wait it loads, send it to the background, put your iPhone in sleep mode, wait a minute, wake up your iPhone, launch the app (that wakes it up) : crash, EXC_BAD_ACCESS. Do the same thing not putting the iPhone in sleep mode : no crash. Not waiting for a minute but a few seconds, no crash. 10 seconds, no crash, 20 seconds, no crash, nears 30 seconds, perhaps a crash, near a minute, crash !
2nd problem (bonus :-) ), that can be linked with the first one, but that is not really the heart of this question : the solution I use to achieve the userPinLocation to update from/to the blue pin seems to have a really bad side effect : the userLocation does not move on the map as you move in the street. For any answer for that 2nd problem, if it's not directly linked with the crash, please use comments not answers. This is not the heart of the question... I think...
To find the faulty code, I've reduced my app at its minimum. That gives the following code (some tips in the source code as comments) :
EXC_BAD_ACCESS_TestViewController.h
#import <MapKit/MapKit.h>
#interface EXC_BAD_ACCESS_TestViewController : UIViewController<CLLocationManagerDelegate> {
CLLocationManager* locationMgr;
NSMutableArray* customAnnotations;
CLLocationDirection userHeading;
MKMapView* myMapView;
}
#property(nonatomic, retain) CLLocationManager* locationMgr;
#property(nonatomic, retain) NSMutableArray* customAnnotations;
#property(nonatomic, assign) CLLocationDirection userHeading;
#property(nonatomic, retain) IBOutlet MKMapView* myMapView;
- (void) loadAnnotations;
#end
EXC_BAD_ACCESS_TestViewController.m
// On first launch, due to code simplification, the app may crash when asked authorization to use geo hardware. Just kill/relaunch after accepting.
// Breakpoints on each method entry : EXC_BAD_ACCESS crash without any break-stop
#import "EXC_BAD_ACCESS_TestViewController.h"
#import "CustomAnnotation.h"
#implementation EXC_BAD_ACCESS_TestViewController
#synthesize locationMgr, customAnnotations, myMapView, userHeading;
// ===========================================================================================================
-(id) initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (!self) return nil;
self.userHeading = -1;
return self;
}
// ===========================================================================================================
- (void) viewDidLoad
{
[self loadAnnotations];
self.myMapView.mapType = MKMapTypeStandard;
self.myMapView.showsUserLocation = YES;
// -----------------------------------------------
// Just comment this sole line : no more crash
// -----------------------------------------------
for (CustomAnnotation* pin in self.customAnnotations) [self.myMapView addAnnotation:pin];
// locationManager
self.locationMgr = [[CLLocationManager alloc] init];
self.locationMgr.desiredAccuracy = kCLLocationAccuracyBest;
self.locationMgr.distanceFilter = 1.0;
self.locationMgr.headingFilter = 1.0;
self.locationMgr.purpose = #"Some purpose.";
self.locationMgr.delegate = self;
[self.locationMgr startUpdatingHeading];
[super viewDidLoad];
}
// ===========================================================================================================
- (void) loadAnnotations
{
// Code for testing, real code gets the datas using another way of doing, as simple as this one
self.customAnnotations = [NSMutableArray array];
double latitude = 45.0;
double longitude = -45.0;
for (int i=1; i<=400; i++) {
CLLocationCoordinate2D pinLocation = CLLocationCoordinate2DMake(latitude, longitude);
CustomAnnotation* pin = [[[CustomAnnotation alloc] initWithCoordinate:pinLocation] autorelease];
[self.customAnnotations addObject:pin];
latitude += 0.01;
longitude += 0.01;
}
}
// ===========================================================================================================
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation
{
MKAnnotationView* pinView = nil;
NSString* annotationIdentifier;
UIImage* pinImage;
BOOL canShowCallout;
// User location
if ([annotation isKindOfClass:[MKUserLocation class]] && self.userHeading >= 0) {
annotationIdentifier = #"UserLocationAnnotation";
pinImage = [UIImage imageNamed:#"custom_userlocation.png"];
canShowCallout = NO;
}
// Custom Pin
else if ([annotation isKindOfClass:[CustomAnnotation class]]) {
annotationIdentifier = #"CustomAnnotation";
pinImage = [UIImage imageNamed:#"custom_pin.png"];
canShowCallout = YES;
}
// Others
else {
return nil;
}
pinView = (MKAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:annotationIdentifier];
if (pinView) {
pinView.annotation = annotation;
}
else {
pinView = [[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annotationIdentifier] autorelease];
if (pinView) {
pinView.image = pinImage;
pinView.canShowCallout = canShowCallout;
if (canShowCallout) {
pinView.calloutOffset = CGPointMake(-5, 5);
pinView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
}
}
}
return pinView;
}
// -----------------------------------------------
// Just comment this method : no more crash
// -----------------------------------------------
// ===========================================================================================================
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
if (!self.myMapView) return;
if (!self.myMapView.userLocation) return;
CLLocationDirection compassHeading_True = newHeading.trueHeading;
CLLocationDirection compassHeading_Magnetic = newHeading.magneticHeading;
CLLocationDirection heading;
if (compassHeading_True == -1) heading = compassHeading_Magnetic;
else if (newHeading.headingAccuracy >= 0) heading = compassHeading_True;
else heading = -1;
// If we get/loose the heading, update pin image
// I didn't found any other solution to force the user pin to update its display.
if ((self.userHeading == -1 || heading == -1) && self.userHeading != heading) {
[self.myMapView removeAnnotation:self.myMapView.userLocation];
self.userHeading = heading;
[self.myMapView addAnnotation:self.myMapView.userLocation];
}
self.userHeading = heading;
// ------------------------------------
// Some non bugued code was there
// ------------------------------------
}
// ===========================================================================================================
- (void) dealloc
{
self.myMapView = nil;
self.customAnnotations = nil;
self.locationMgr = nil;
[super dealloc];
}
#end
CustomAnnotation.h
#import <MapKit/MapKit.h>
#interface CustomAnnotation : NSObject<MKAnnotation> {
CLLocationCoordinate2D coordinate;
}
#property(nonatomic, assign) CLLocationCoordinate2D coordinate;
- (id)initWithCoordinate:(CLLocationCoordinate2D) c;
#end
CustomAnnotation.m
#import "CustomAnnotation.h"
#implementation CustomAnnotation
#synthesize coordinate;
// ===========================================================================================================
- (id)initWithCoordinate:(CLLocationCoordinate2D) c
{
self = [super init];
if (!self) return nil;
self.coordinate = c;
return self;
}
// ===========================================================================================================
- (NSString*)subtitle
{
return #"";
}
// ===========================================================================================================
- (NSString*)title
{
return #"";
}
#end
I tried breakpoints in each methods, trying to enable Zombies with environment variables (but as this needs to be ran on the device, I'm not sure they've been really activated), without any start of solution... I still don't have any idea of what's going wrong.
Do you see how to solve that EXC_BAD_ACCESS problem ?
For any answer for the 2nd problem, please use comments and not answers if it don't solve the crash. This problem is not the heart of the question as far as I've seen.
Tip : I've put 2 comments in the code above where I found some start of solution. There are 2 places, that does not seems connected, that can be put out of the code one OR the other to solve the problem. What makes me crazy is the OR.
Runned on an iPhone 4 ios 4.2.1
[self.myMapView removeAnnotation:self.myMapView.userLocation];
This line is causing the crash from stacktrace this is what I've seen.
When I saw exc_bad_access I did a bt in gdb. I have added Guard Malloc breakpoint. Looking at the stack it said about removing annotation.
You can try overriding a method called didReceiveMemoryWarning in your view controller .m file.
What happens sometimes is that memory on the device gets low due to running apps and IOS sends a message to the app. Now the trouble is when you don't override memory warning method, its default action is to remove any subviews from memory which are not visible. This can lead to zombie variables when your application runs.
Check out the apple doc