What are the ranges of the accelerator on the iPhone? - iphone

I can't seem to find any documentation online about this, and what I am googling is giving me a lot of conflicting information...

From iphonedevsdk.com:
The accelerometers used in the first
and second generation iPhones (and I
assume also the iPod touches) operate
in two modes: +/- 2 g, and +/- 8 g.
(Actually, as you observed, they
report accelerations somewhat outside
the nominal range. Accuracy is not
spec'ed outside that range, though.)
Apple operates them in the +/- 2 g
mode. There is a tradeoff: The current
resolution is nominally 0.018 g,
according to the datasheet (though my
first generation iPhone uses
0.018168604, according to a modified version of AccelerometerGraph). In the
+/- 8 g mode, the resolution would be four times cruder.
I assume Apple decided that the finer
resolution would be more useful than
the wider range. (I'd rather see finer
resolution than 0.018 g. So neither of
us is fully satisfied.)
You cannot change the mode with any
published feature of the APIs. Since
they are passing acceleration as a
double, they could theoretically allow
a change in mode, and simply look at
the mode when rescaling the A/D value,
before reporting acceleration. (The
obvious place to set the mode would be
in the call which sets up the
application to receive accelerometer
information.) However, for backward
compatibility, the OS would have to
set the accelerometer mode to +/- 2 g
at the beginning of the application.
And none of the background processes
could be allowed to set the
accelerometer mode.

The aGauge app displays this raw data coming from the accelerometer as you move the device. It can also help you find the "flip" threshold for your device.

I created the following application to try to test out the ranges...
UIAccelerometer *objAccelerometer;
UILabel *lblxmin, *lblxmax, *lblymin, *lblymax, *lblzmin, *lblzmax;
UILabel *lblxnow, *lblynow, *lblznow;
float xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, zmin = 0.0, zmax = 0.0, xnow = 0.0, ynow = 0.0, znow = 0.0;
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
//NSLog (#"%f, %f, %f", acceleration.x, acceleration.y, acceleration.z);
xnow = acceleration.x;
ynow = acceleration.y;
znow = acceleration.z;
if (xnow < xmin) { xmin = xnow; }
if (ynow < ymin) { ymin = ynow; }
if (znow < zmin) { zmin = znow; }
if (xnow > xmax) { xmax = xnow; }
if (ynow > ymax) { ymax = ynow; }
if (znow > zmax) { zmax = znow; }
lblxmin.text = [NSString stringWithFormat:#"%f", xmin];
lblymin.text = [NSString stringWithFormat:#"%f", ymin];
lblzmin.text = [NSString stringWithFormat:#"%f", zmin];
lblxmax.text = [NSString stringWithFormat:#"%f", xmax];
lblymax.text = [NSString stringWithFormat:#"%f", ymax];
lblzmax.text = [NSString stringWithFormat:#"%f", zmax];
lblxnow.text = [NSString stringWithFormat:#"%f", xnow];
lblynow.text = [NSString stringWithFormat:#"%f", ynow];
lblznow.text = [NSString stringWithFormat:#"%f", znow];
}
-(void) invokeAccelerometer {
objAccelerometer = [UIAccelerometer sharedAccelerometer];
objAccelerometer.delegate = self;
objAccelerometer.updateInterval = (1.0 / 10.0);
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
lblxmin = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 100, 40)];
lblxnow = [[UILabel alloc] initWithFrame:CGRectMake(100, 10, 100, 40)];
lblxmax = [[UILabel alloc] initWithFrame:CGRectMake(200, 10, 100, 40)];
lblymin = [[UILabel alloc] initWithFrame:CGRectMake(10, 60, 100, 40)];
lblynow = [[UILabel alloc] initWithFrame:CGRectMake(100, 60, 100, 40)];
lblymax = [[UILabel alloc] initWithFrame:CGRectMake(200, 60, 100, 40)];
lblzmin = [[UILabel alloc] initWithFrame:CGRectMake(10, 110, 100, 40)];
lblznow = [[UILabel alloc] initWithFrame:CGRectMake(100, 110, 100, 40)];
lblzmax = [[UILabel alloc] initWithFrame:CGRectMake(200, 110, 100, 40)];
[self.view addSubview:lblxmin];
[self.view addSubview:lblxnow];
[self.view addSubview:lblxmax];
[self.view addSubview:lblymin];
[self.view addSubview:lblynow];
[self.view addSubview:lblymax];
[self.view addSubview:lblzmin];
[self.view addSubview:lblznow];
[self.view addSubview:lblzmax];
[self invokeAccelerometer];
[super viewDidLoad];
}
The problem is, when I rotate my iPod along the horizontal/vertical axes and then flip it over, I get values like:
xmin -1.271802
xmax 1.180959
ymin -1.344477
ymax 1.108285
zmin -2.30713
zmax 2.325581
If I take the iPod and shake the heck out of it, I get...
x -2.325581 to 2.307413
y -2.325581 to 2.307413
z -2.307413 to 2.325581
Any ideas what it's measuring?
The best I've come up with is:
vertical axis
x = -1 if tilted to the left ( <| )
x = +1 if tilted all the way to the right ( |> )
where < is the way the screen faces, and | is the bottom of the iPod
y ~ -1 if screen is facing you, perpendicular to floor ("standing up")
y ~ 1 if facing away from you (and upside down)

Related

Using quaternion instead of roll, pitch and yaw to track device motion

Please bear with my long question, I am trying to make it as clear as possible.
What i am trying to do is, get the attitude(roll pitch and yaw) when a picture is taken using camera and then save the attitude values to nsuserdefaults. After saving, the orientation is changed and then try to bring the phone to the same attitude the picture was taken by constantly comparing the attitude values(saved and current).
For the purpose of interface, the user interface has 3 dots (one for each attitude parameter)on screen which guide to the correct orientation the picture was taken. On reaching the correct attitude the match flag is shown on screen.
I have been finding the roll, pitch and yaw values as:
CMQuaternion quat = self.motionManager.deviceMotion.attitude.quaternion;
myRoll = radiansToDegrees(atan2(2*(quat.y*quat.w - quat.x*quat.z), 1 - 2*quat.y*quat.y - 2*quat.z*quat.z)) ;
myPitch = radiansToDegrees(atan2(2*(quat.x*quat.w + quat.y*quat.z), 1 - 2*quat.x*quat.x - 2*quat.z*quat.z));
myYaw = radiansToDegrees(2*(quat.x*quat.y + quat.w*quat.z));
When i noticed there is some disparity in the yaw values, i searched and could find from here:link, that
yaw, pitch and roll from a quaternion you will have the same
problem as if you were using just yaw, pitch and roll. You have to use
quaternions EVERYWHERE in your code and forget about yaw, pitch and
roll
So now i guess i have to code everything again... Please could you be so kind to point me to a sample code for using Quaternion for this purpose?
Here is the code i am working on:
In the ViewController.m, within the image:didFinishSavingWithError:
[self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
CMQuaternion quat = self.motionManager.deviceMotion.attitude.quaternion;
double tempYaw = radiansToDegrees(asin(2*(quat.x*quat.y + quat.w*quat.z)));
double tempRoll = radiansToDegrees(atan2(2*(quat.y*quat.w - quat.x*quat.z), 1 - 2*quat.y*quat.y - 2*quat.z*quat.z)) ;
double tempPitch = radiansToDegrees(atan2(2*(quat.x*quat.w + quat.y*quat.z), 1 - 2*quat.x*quat.x - 2*quat.z*quat.z));
if (savingGyroOrientation == YES) {
NSLog(#"Roll = %f degrees",tempRoll);
NSLog(#"Pitch = %f degrees",tempPitch);
NSLog(#"Yaw = %f degrees",tempYaw);
[self.deviceStatus setDouble:tempRoll forKey:#"DeviceRoll"];
[self.deviceStatus setDouble:tempPitch forKey:#"DevicePitch"];
[self.deviceStatus setDouble:tempYaw forKey:#"DeviceYaw"];
[self.deviceStatus synchronize];
savingGyroOrientation = NO;
checkingGyroOrientation = YES;
self.savingLabel.hidden = YES;
self.startTimerButton.hidden = NO;
}
savingGyroOrientation = NO;
checkingGyroOrientation = YES;
self.savingLabel.hidden = YES;
self.startTimerButton.hidden = NO;
}
if (timerRunning == YES) {
if (checkingGyroOrientation == YES) {
self.takePicButton.hidden = YES;
int xRoll, yPitch, xYaw, yYaw;
// Roll Checking
if (tempRoll >= [self.deviceStatus doubleForKey:#"DeviceRoll"]-1 && tempRoll <= [self.deviceStatus doubleForKey:#"DeviceRoll"]+1 ) {
[self.rollDot setFrame:CGRectMake(150, 195, 20, 20)];
self.rollToR.hidden = YES;
self.rollToL.hidden = YES;
self.rollDot.hidden = NO;
rollOk = YES;
}else{
rollOk = NO;
self.rollDot.hidden = YES;
self.rollToR.hidden = NO;
self.rollToL.hidden = NO;
if (tempRoll - [self.deviceStatus doubleForKey:#"DeviceRoll"] < 0) {
xRoll = 150 + (tempRoll - [self.deviceStatus doubleForKey:#"DeviceRoll"]);
self.rollToR.hidden = YES;
if (xRoll <= 0) {
[self.rollToL setFrame:CGRectMake(0, 195, 20, 20)];
}else if (xRoll>= 300){
[self.rollToL setFrame:CGRectMake(300, 195, 20, 20)];
}else{
[self.rollToL setFrame:CGRectMake(xRoll, 195, 20, 20)];
}
}
if (tempRoll - [self.deviceStatus doubleForKey:#"DeviceRoll"] > 0){
xRoll = 150 + (tempRoll - [self.deviceStatus doubleForKey:#"DeviceRoll"]);
self.rollToL.hidden = YES;
if (xRoll <= 0) {
[self.rollToR setFrame:CGRectMake(0, 195, 20, 20)];
}else if (xRoll>=300){
[self.rollToR setFrame:CGRectMake(300, 195, 20, 20)];
}else{
[self.rollToR setFrame:CGRectMake(xRoll, 195, 20, 20)];
}
}
if (tempRoll - [self.deviceStatus doubleForKey:#"DeviceRoll"] > 180){
xRoll = 150 + (tempRoll - [self.deviceStatus doubleForKey:#"DeviceRoll"]-360);
self.rollToR.hidden = YES;
if (xRoll <= 0) {
[self.rollToL setFrame:CGRectMake(0, 195, 20, 20)];
}else if (xRoll>=300){
[self.rollToL setFrame:CGRectMake(300, 195, 20, 20)];
}else{
[self.rollToL setFrame:CGRectMake(xRoll, 195, 20, 20)];
}
}
if (tempRoll - [self.deviceStatus doubleForKey:#"DeviceRoll"] < -180){
xRoll = 150 + (tempRoll - [self.deviceStatus doubleForKey:#"DeviceRoll"]+360);
self.rollToL.hidden = YES;
if (xRoll <= 0) {
[self.rollToR setFrame:CGRectMake(0, 195, 20, 20)];
}else if (xRoll >= 300){
[self.rollToR setFrame:CGRectMake(300, 195, 20, 20)];
}else{
[self.rollToR setFrame:CGRectMake(xRoll, 195, 20, 20)];
}
}
}
//Pitch Checking
if (tempPitch >= [self.deviceStatus doubleForKey:#"DevicePitch"]-1 && tempPitch <= [self.deviceStatus doubleForKey:#"DevicePitch"]+1) {
[self.pitchDot setFrame:CGRectMake(150, 195, 20, 20)];
self.pitchDot.hidden = NO;
self.pitchToDown.hidden = YES;
self.pitchToUp.hidden = YES;
pitchOk = YES;
}else{
pitchOk = NO;
self.pitchDot.hidden = YES;
self.pitchToDown.hidden = NO;
self.pitchToUp.hidden = NO;
if (tempPitch - [self.deviceStatus doubleForKey:#"DevicePitch"] < 0) {
yPitch = 195+(tempPitch - [self.deviceStatus doubleForKey:#"DevicePitch"]);
// NSLog(#"tempPitch is %0.02f Difference is %0.02f, yPitch is %0.02f",tempPitch, tempPitch-[self.deviceStatus doubleForKey:#"DevicePitch"],195+(tempPitch - [self.deviceStatus doubleForKey:#"DevicePitch"]));
self.pitchToDown.hidden = YES;
if (yPitch <= 0) {
[self.pitchToUp setFrame:CGRectMake(150, 0, 20, 20)];
}else if (yPitch >= 390) {
[self.pitchToUp setFrame:CGRectMake(150, 390, 20, 20)];
}else{
[self.pitchToUp setFrame:CGRectMake(150, yPitch, 20, 20)];
}
}
if (tempPitch - [self.deviceStatus doubleForKey:#"DevicePitch"] > 0){
yPitch = 195+(tempPitch - [self.deviceStatus doubleForKey:#"DevicePitch"]);
// NSLog(#"tempPitch is %0.02f Difference is %0.02f, yPitch is %0.02f",tempPitch, tempPitch-[self.deviceStatus doubleForKey:#"DevicePitch"],195+(tempPitch - [self.deviceStatus doubleForKey:#"DevicePitch"]));
self.pitchToUp.hidden = YES;
if (yPitch <= 0) {
[self.pitchToDown setFrame:CGRectMake(150, 0, 20, 20)];
}else if (yPitch >= 390) {
[self.pitchToDown setFrame:CGRectMake(150, 390, 20, 20)];
}else{
[self.pitchToDown setFrame:CGRectMake(150, yPitch, 20, 20)];
}
}
if (tempPitch - [self.deviceStatus doubleForKey:#"DevicePitch"] < -180){
yPitch = 195+tempPitch - [self.deviceStatus doubleForKey:#"DevicePitch"] + 360;
// NSLog(#"tempPitch is %0.02f Difference is %0.02f, yPitch is %0.02f",tempPitch, tempPitch-[self.deviceStatus doubleForKey:#"DevicePitch"],195+(tempPitch - [self.deviceStatus doubleForKey:#"DevicePitch"]));
// NSLog(#"*yPitch is %d",yPitch);
self.pitchToUp.hidden = YES;
self.pitchToDown.hidden = NO;
if (yPitch <= 0 ) {
[self.pitchToDown setFrame:CGRectMake(150, 0, 20, 20)];
}else if (yPitch >= 390) {
[self.pitchToDown setFrame:CGRectMake(150, 390, 20, 20)];
}else{
[self.pitchToDown setFrame:CGRectMake(150, yPitch, 20, 20)];
}
}
}
if (tempYaw >= [self.deviceStatus doubleForKey:#"DeviceYaw"]-2 && tempYaw <= [self.deviceStatus doubleForKey:#"DeviceYaw"]+2) {
[self.yawDot setFrame:CGRectMake(150, 195, 20, 20)];
self.yawDot.hidden = NO;
self.rotateRight.hidden = YES;
self.rotateLeft.hidden = YES;
yawOk = YES;
}else{
yawOk = NO;
self.yawDot.hidden = YES;
self.rotateRight.hidden = NO;
self.rotateLeft.hidden = NO;
if (tempYaw - [self.deviceStatus doubleForKey:#"DeviceYaw"] < 0 ) {
xYaw = 150+(tempYaw - [self.deviceStatus doubleForKey:#"DeviceYaw"]);
yYaw = 195-1.3*(tempYaw - [self.deviceStatus doubleForKey:#"DeviceYaw"]);
NSLog(#"current yaw is %0.02f Difference is %0.02f",tempYaw, tempYaw-[self.deviceStatus doubleForKey:#"DeviceYaw"]);
NSLog(#"saved Yaw is %0.02f",[self.deviceStatus doubleForKey:#"DeviceYaw"]);
NSLog(#"xYaw is %d, yYaw is %d",xYaw,yYaw);
self.rotateRight.hidden = YES;
if (xYaw <=0 && yYaw >=390) {
[self.rotateLeft setFrame:CGRectMake(0, 390, 20, 20)];
}else{
[self.rotateLeft setFrame:CGRectMake(xYaw, yYaw, 20, 20)];
}
}if (tempYaw - [self.deviceStatus doubleForKey:#"DeviceYaw"] > 0){
xYaw = 150+(tempYaw - [self.deviceStatus doubleForKey:#"DeviceYaw"]);
yYaw = 195-1.3*(tempYaw - [self.deviceStatus doubleForKey:#"DeviceYaw"]);
NSLog(#"current yaw is %0.02f Difference is %0.02f",tempYaw, tempYaw-[self.deviceStatus doubleForKey:#"DeviceYaw"]);
NSLog(#"saved Yaw is %0.02f",[self.deviceStatus doubleForKey:#"DeviceYaw"]);
NSLog(#"*xYaw is %d, yYaw is %d",xYaw,yYaw);
self.rotateLeft.hidden = YES;
if (xYaw >=300 && yYaw <=0) {
[self.rotateRight setFrame:CGRectMake(300, 0, 20, 20)];
}else{
[self.rotateRight setFrame:CGRectMake(xYaw, yYaw, 20, 20)];
}
}
}
if (rollOk == YES && pitchOk == YES && yawOk ==YES) {
self.orientationOkay.hidden = NO;
self.centerCircle.hidden = YES;
self.rollDot.hidden = YES;
self.pitchDot .hidden =YES;
self.yawDot.hidden = YES;
[self.clickTimer invalidate];
self.clickTimer = nil;
self.takePicButton.hidden = NO;
timerRunning = NO;
[self.motionManager stopDeviceMotionUpdates];
[self.deviceStatus removeObjectForKey:#"DeviceRoll"];
[self.deviceStatus removeObjectForKey:#"DevicePitch"];
[self.deviceStatus removeObjectForKey:#"DeviceYaw"];
[self.deviceStatus removeObjectForKey:#"DeviceAngle"];
}else{
self.orientationOkay.hidden = YES;
if (flagger == YES) {
self.centerCircle.hidden = NO;
}
else{
self.centerCircle.hidden = YES;
}
}
}
}else{
self.rotateRight.hidden = YES;
self.rotateLeft.hidden = YES;
self.rollToL.hidden = YES;
self.rollToR.hidden = YES;
self.pitchToDown.hidden = YES;
self.pitchToUp.hidden = YES;
self.rollDot.hidden = NO;
self.pitchDot .hidden =NO;
self.yawDot.hidden = NO;
[self.yawDot setFrame:CGRectMake(0, 390, 20, 20)];
[self.rollDot setFrame:CGRectMake(0, 195, 20, 20)];
[self.pitchDot setFrame:CGRectMake(150, 0, 20, 20)];
}
}];
Please let me know if any further details are needed on this.
Any suggestions or advice is always welcome, :) I am a noob to programming and to ios.
Thanks!!
I think the following things are necessary to manage to task:
First of all you need a good understanding of quaternions (skip this if you already made friends with them). I recommend OpenGL:Tutorials:Using Quaternions to represent rotation or The Matrix and Quaternions FAQ. It helps to bear in mind that (x, y, z) represent the axis to rotate around (not normalised) and w = cos (alpha/2) i.e. stands approximately for the amount of rotation.
As CMQuaternion is just a struct thus it's hard to do all the calculations. Use a full functional quaternion class instead like cocoamath (you need at least Quaternion.h, .m and QuaternionOperation.m from trunk).
Now the basic considerations:
The difference (or sometimes stated as division) between two quaternions being defined as the angular displacement from one orientation to another might be the way to go. It is defined as
d = a-1 * b
So this expresses the delta from the current position to the target position.
Having this delta you need to define the conditions to met for considering the target orientation as reached. My first idea is to use the delta angle. This can be easily retrieved from the w component of the above calculated d quaternion by:
alpha = 2 * arccos (w)
The domain of arccos is restricted but this shouldn't be a problem in this case as we are especially interested in small values.
Maybe it's worth to emphasize that every 3D rotation has two unit quaternion representations, q and -q. This might be confusing but doesn't matter.
Update:
So a bit of pseudo code would look something like:
CMQuaternion cmQ = attitude.quaternion;
// Get an instance of cocoamath's Quaternion for our currently reported quaternion
Quaternion current = [Quaternion initWithRe:(double)cmQ.w i:(double)cmQ.x j:(double)cmQ.y k:(double)cmQ.z];
// the complex conjugate and normalised to be on the safe side
Quaternion inverse = [current inverse];
// build the delta, assuming you have your stored direction as class member targetQuaternion
Quaternion diff = [inverse multiply:targetQuaternion];
float alpha = 2 * acos (diff.Re);
// maxDeltaAngle is your class member variable defining the angle from which you assume the position as restored (radians)
if (fabs (alpha) < maxDeltaAngle) {
// do my fancy camera things
}
Yes it's not that trivial at the beginning. As Ali stated in his answer, visualisation is an important issue as well. My solution just solves the raw maths part.
I am not saying that the following is the solution to your problem, I have no idea how user-friendly this will be but I believe it is worth a shot. I see two issues: how you store the attitude and how you visualize them.
Storage. I would save the attitude either as a quaternion or as a rotation matrix; CMAttitude provides both. (I personally prefer rotation matrices to quaternions as rotation matrices are easier to understand in my opinion. It is just a personal preference.)
Visualization. You are using 3 markers to bring the phone to the same attitude as the saved one. These 3 markers came from yaw, pitch and roll, effectively ruining the stability of your application. Instead of these 3 markers I would visualize the rotation between the saved attitude and the current one. One way of doing it is to project the rotated 3 basis vectors onto the phone's screen. The nice thing about rotation matrices is that they give you exactly this without any extra computation. For example, to visualize the
| 1 0 0 |
| 0 1 0 |
| 0 0 1 |
rotation this rotation matrix represents, I only need to plot 3 arrows from [0, 0, 0] to (i) [1, 0, 0], (ii) [0, 1, 0] and (iii) [0, 0, 1]. That is, I only need to plot either the rows or the columns of the matrix (also depends on what you want whether the rows or the columns are better).
To get the rotation between a saved rotation matrix S and the current rotation matrix C you need to compute CTS (in words: C transpose times S). You have aligned the phone with the saved attitude if your rotation matrix is the one shown above.
An excellent tutorial on rotation matrices is the Direction Cosine Matrix IMU: Theory manuscript.
Another way of visualizing the rotation between the saved and the current attitude is to transform it to angle-axis form. Then, I would project the axis to the phone's screen. The user first aligns the phone with the axis, than needs to rotate around the axis to match the saved attitude. Quaternions basically represent the axis-angle form, although in a nontrivial way.
It requires significant further work on your part to alter this idea according to your needs; this is definitely not a complete answer. Nevertheless, I hope this help a bit.

To make 16 uilabels and align them in circular path.

NSMutableArray *views = [[NSMutableArray alloc]initWithCapacity:0];
for (NSInteger i = 0; i<16; i++)
{
UIView *circle = [[UIView alloc]init];
circle.backgroundColor = [UIColor clearColor];
UIImageView *circleImage = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 80, 80)];
circleImage.image = [UIImage imageNamed:#"circle"];
[circle addSubview:circleImage];
UILabel *labelInsideCircle = [[UILabel alloc]initWithFrame:CGRectMake(10, 10, 40, 40)];
labelInsideCircle.backgroundColor = [UIColor clearColor];
labelInsideCircle.textColor = [UIColor greenColor];
labelInsideCircle.font = [UIFont fontWithName:#"Helvetica" size:30.0];
labelInsideCircle.center = circleImage.center;
NSInteger int_ = [self getRandomNumber:0 to:(arrOfOptions.count-1)];
labelInsideCircle.text = [NSString stringWithFormat:#"%#",[arrOfOptions objectAtIndex:int_]];
labelInsideCircle.textAlignment = NSTextAlignmentCenter;
[arrOfOptions removeObjectAtIndex:int_];
[circle addSubview:labelInsideCircle];
[labelInsideCircle release];
[views addObject:circle];
[circle release];
[circleImage release];
}
/* Rotating circles with angles */
float curAngle = 0;
float incAngle = ( 360.0/(views.count) )*3.14/180.0;
CGPoint circleCenter = CGPointMake(380, 580); /* given center */
float circleRadius = 250; /* given radius */
for (UIView *view in views)
{
CGPoint viewCenter;
viewCenter.x = circleCenter.x + cos(curAngle)*circleRadius;
viewCenter.y = circleCenter.y + sin(curAngle)*circleRadius;
view.transform = CGAffineTransformRotate(view.transform, curAngle);
view.center = viewCenter;
[self.view addSubview:view];
curAngle += incAngle;
}
The problem is here the text of UILabel is also getting transformed, which is obvious. What I want is 16 circular views with labels on them without the label's text transformed. Can anyone please help me out with this ?
In this case, you just need to change their location coordinates, not rotate them.
NSMutableArray *views = [[NSMutableArray alloc] initWithCapacity:0];
for (NSInteger i = 0; i<16; i++)
{
UIView *circle = [[UIView alloc]init];
circle.backgroundColor = [UIColor clearColor];
UIImageView *circleImage = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 80, 80)];
circleImage.image = [UIImage imageNamed:#"circle"];
[circle addSubview:circleImage];
UILabel *labelInsideCircle = [[UILabel alloc]initWithFrame:CGRectMake(10, 10, 40, 40)];
labelInsideCircle.backgroundColor = [UIColor clearColor];
labelInsideCircle.textColor = [UIColor greenColor];
labelInsideCircle.font = [UIFont fontWithName:#"Helvetica" size:30.0];
labelInsideCircle.center = circleImage.center;
NSInteger int_ = arc4random()%[arrOfOptions count];
labelInsideCircle.text = [NSString stringWithFormat:#"%#",[arrOfOptions objectAtIndex:int_]];
labelInsideCircle.textAlignment = NSTextAlignmentCenter;
[arrOfOptions removeObjectAtIndex:int_];
[circle addSubview:labelInsideCircle];
[labelInsideCircle release];
[views addObject:circle];
[self.view addSubview:circle];
[circle release];
[circleImage release];
}
/* Rotating circles with angles */
float curAngle = 0;
float incAngle = ( 360.0/(views.count) )*3.14/180.0;
CGPoint circleCenter = CGPointMake(380, 580); /* given center */
float circleRadius = 250; /* given radius */
for (UIView *view in views)
{
CGPoint viewCenter;
viewCenter.x = circleCenter.x + cos(curAngle)*circleRadius;
viewCenter.y = circleCenter.y + sin(curAngle)*circleRadius;
//view.transform = CGAffineTransformRotate(view.transform, curAngle);
view.center = viewCenter;
[self.view addSubview:view];
curAngle += incAngle;
}
Some coding suggestions:
You can use arc4random() function if you don't have anything special in your own random number generator.
You can turn on ARC in xCode!
In a loop set their centers accordingly, don't rotate them nor their super view.
The following is not directly related to your question but may be helpful:
Aparaently you have got all the math available. I suggest to measure how much cpu time gets lost on the cos and sin etc. calls. If you find that significant then think about your algorithm. You will (most probably) find out that you call cos and sin hundrets or thousands of times for a limited number of angles.
You may then try it and find out that possible pre-calculations or just "caching and reusing" earlier results may save significant processing time.
Plus pre-calculating or caching of sinus would do. You can derrive cosinus values from sinus (and vice versa) by adding (or substracting respectively) an offset of pi/2 (or 90 degrees respectively) to the argument.
For similar tasks I was working with degrees (not radiants) and found out that I could not predict the angles but that a significant of full degrees (1°, 2°, 3°, ... and nothing in between) was exact enough for the job. Then I maintained an array of 360 sinus values and used that instead of calling the real function again and again. (Plus I only had to calculate 90 of them and mirrored the results of the other 270 degrees according to the nature of the sinus function) 180 floats is not too much of memory compared to the speed that I gained. Something like that can be suitable for you too. In your case your potenital agruments to sin and cos are limited to full-number multipilers of incAngle.
Which means there are only [views count] number of potential values each.

How to change the XAxis used on my chart ShinobiCharts

I'd like to have the xAxis of my chart to look like this :
Jan, Feb, Mar, Apr, ..... Nov, Dec
Right now it's following it's default, numbering xAxis according to the number of Data Points.
How can i achieve this change to this axis ?
I've tried using Category Axis and setting an NSMutableArray containing these strings ("Jan", "Feb"...) as categories and with a numberRange going from 1 to 12 but it didn't work.
chart = [[ShinobiChart alloc] initWithFrame:chartEmbaixo.frame withPrimaryXAxisType:SChartAxisTypeCategory withPrimaryYAxisType:SChartAxisTypeNumber];
NSMutableArray * monthNames = [[NSMutableArray alloc] initWithObjects:#"Jan", #"Fev", #"Mar", #"Abr", #"Mai", #"Jun", #"Jul", #"Ago", #"Set", #"Out", #"Nov", #"Dez", nil];
SChartNumberRange * numberRange = [[SChartNumberRange alloc] initWithMinimum:[NSNumber numberWithInt:1]andMaximum:[NSNumber numberWithInt:12]];
SChartCategoryAxis *xAxis = [[SChartCategoryAxis alloc] initWithRange:numberRange];
xAxis.categories = monthNames;
chart.xAxis = xAxis;
first i use as my x axis
Edit how i make my x axis:
SChartNumberRange *r1 = [[SChartNumberRange alloc] initWithMinimum:[NSNumber numberWithInt:0] andMaximum:[NSNumber numberWithInt:2]];
SChartCategoryAxis *xAxis = [[SChartCategoryAxis alloc] initWithRange:r1];
xAxis.title = #"";
//xAxis.enableGesturePanning = YES;
xAxis.enableGesturePanning = YES;
xAxis.style.gridStripeStyle.showGridStripes = NO;
xAxis.style.majorGridLineStyle.showMajorGridLines = NO;
when you make you data points it should use the xValue as the x axis point.
like this:
dp.yValue = 1000;
dp.xValue = #"Jan";
the xValue should be set as the x point for that particular data point. This should work, but if it doesn't or you want to do something more complex you can extend this method from SChartDelegate protocol:
-(void)sChart:(ShinobiChart *)chart alterTickMark:(SChartTickMark *)tickMark beforeAddingToAxis:(SChartAxis *)axis
in this method you have the tickMark.tickLabelis the axis label for that given point where you can do your editing. Don't forget to verify what axis your on.
Hope this helps. If not tomorrow i can post you some code from my project (currently i don't have access to it from where i am)
Edit: currently i have this code:
- (void)sChart:(ShinobiChart *)chart alterTickMark:(SChartTickMark *)tickMark beforeAddingToAxis:(SChartAxis *)axis {
if (chart.yAxis == axis ) return;
for (UIView *i in tickMark.tickMarkView.subviews)
[i removeFromSuperview];
tickMark.tickMarkView.frame = CGRectMake(0, 0, 170, 75);
//center the marker at the right place because the size was changed
tickMark.tickMarkX = tickMark.tickMarkX - (tickMark.tickMarkView.frame.size.width/2) ;
tickMark.tickMarkY = 10;
//img
UIImageView *img = [[UIImageView alloc] initWithImage:[UIImage imageNamed: #"graph_bar_tag_2#2x.png"]];
img.frame = CGRectMake( 0, 0, tickMark.tickMarkView.frame.size.width, tickMark.tickMarkView.frame.size.height);
[tickMark.tickMarkView addSubview:img];
//label with the markView's size with 7px padding on the left and on the right
UILabel *label = [[UILabel alloc] initWithFrame: CGRectMake( 7, 5, tickMark.tickMarkView.frame.size.width-14, 15)];
label.backgroundColor = [UIColor clearColor];
//tikMark.tickLabel has an pair of indexes so that i can easily find the data for this particular data point and series.
label.text = [_dataSource getNameFor: tickMark.tickLabel.text];
label.textAlignment = UITextAlignmentCenter;
//color_other_light is a UIColor var
[label setTextColor: color_other_light];
[tickMark.tickMarkView addSubview:label];
...
}

Create UITextViews in a loop - Objective C

I'm working on an app in which I need to scan through a string to populate some UITextViews. Basically the data is like this:
Time Period: 10am-12pm
Temperature: 45F
Wind: 123 degrees # 5mph
Time Period: 1am-3pm
Temperature: 53F
Wind: 133 degrees # 2mph
Time Period: 4am-5pm
Temperature: 50F
Wind: 110 degrees # 7mph
The problem is that there is not a set number of time periods that are available at any given time. So I just have to loop through until I reach the end. Is there a way to create a textview inside a loop?
UITextView *textField1 = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
UITextView *textField2 = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
UITextView *textField3 = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
.....
So could I create the textfields at runtime until it reaches the end of the string or would I have to create the max number that it could possibly need then just use them if needed?
Thanks,
Andrew Boos
Sure you can.
First divide your data string using either [NSString componentsSeparatedByString] or [NSString componentsSeparatedByCharactersInSet] to get an NSArray of substrings.
//assuming your data is separated by newlines
NSArray * substrings = [data_input componentsSeparatedByString:#"\n"];
//loop over the substrings creating textfields
for (int i = 0; i < [substrings count]; i++)
{
CGRect frame = CGrectMake(0, i * 40, 100, 30);
UITextField * tf = [[UITextField alloc] initWithFrame: frame];
tf.text = [substrings objectAtIndex:i];
//add as subview
[view addSubview: tf];
//if you are not using ARC release the textfield
}
//disclaimer: written this on the go, may contain some spelling mistakes etc, but should be enough to get you going.

Y-axis opposite in iphone's view , how to adjust it like Y-axis mathematics?

I wanna shoe two point in a iphone's view
This is my code
UIImageView *center;
center = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"point.png"]];
[center setFrame:CGRectMake(0, 0, 10, 10)];
float r_rect_center_x = [r_img frame].size.width/2;
float r_rect_center_y = [r_img frame].size.height/2;
[center setCenter:CGPointMake(r_rect_center_x,r_rect_center_y)];
[r_img addSubview:center];
[center release];
This is center, I got result center point is (X:50,Y:60)
float point1_x = 60.0f;
float point1_y = 60.0f;
UIImageView *point1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"point.png"]];
[point1 setFrame:CGRectMake(0, 0, 10, 10)];
[point1 setCenter:CGPointMake(point1_x,point1_y)];
[r_img addSubview:point1];
[point1 release];
This is point1 , I set the center is X:60,Y:60
It should be looks like this
But it run on the simulator is like this...
HOW TO ADJUST THE POINT1 CENTER IN Y-AXIS ???
Many Thanks
Webber.
There's a setFlipped: method on the view. Just call it and pass YES.
[view setFlipped:YES]