Position on UIImageView - iphone

Me again. I have a simple question. I have an UIImageView like the one shown below.
alt text http://img683.imageshack.us/img683/1999/volumen.png
That UIimageView is supposed to be the knob to control the volume of my iphone project. My question is, how to know the positions of bar on the UIImageView when it is rotated? Because the volume needs to be 0.5 when the little bar on the cercle is vertical.
I got a piece of code which is (in the touchMoved method):
float dx = locationT.x - imgVVolume.center.x;
float dy = locationT.y - imgVVolume.center.y;
CGFloat angleDif = 0.0f;
movedRotationAngle = atan2(dy,dx);
if (beganRotationAngle == 0.0) {
beganRotationAngle = movedRotationAngle;
initialTransform = imgVVolume.transform;
}
else {
angleDif = beganRotationAngle - movedRotationAngle;
CGAffineTransform newTrans = CGAffineTransformRotate(initialTransform, -angleDif);
imgVVolume.transform = newTrans;
}
Help please.

It depends on what input mechanism you want to use to control the rotation.
If the knob is to rotate based on a single finger touch dragging from side to side then you can create a UIPanGestureRecognizer and attach it to the knob UIImageView. The translationInView: method returns a CGPoint which is the amount of X and Y movement from the touch-down point. You can feed that into a formula like the one you post to get an angle of rotation. You'll want to keep track of delta from last position and also check for stop limits (like 0..360) to prevent over-rotation.
OTOH, if you're going to use two finger rotation then you'll want to use a UIRotationGestureRecognizer and look for the rotation value. Just feed that into a CGAffineTransformRotate and set it to the UIImageView transform. That takes care of all of the above for you. Again, you'll want to check for stop limits.

Related

Main view rotates from center point to 360 degree but inner views are also being rotated

I am rotating main view with 360 degrees, and I have subviews added inside main view, everything works correctly, but with one issue.
What I want to do is when I rotate main view, inner views should not lost their frames/position. Right now, when I rotate main view with infinte repeat count and dynamically if I add subview inside main view, it goes into proper position, but it does not retain its frame.
For example, I am implementing orbit, and for that, I have used entire transparent view as orbit and orbit is rotated from center point to 360 degree infinite times, and User can add many planets as he wants onto orbit, so when planets added on orbit, planets do not retain its frame. Can you suggest any idea?
Thanks in advance.
Well it sounds like you need to add a rotating animation for every subview that you add in your main view. If the main view rotates clockwise your subviews will need to rotate around their center in a counter-clockwise direction.
I guess you're trying to keep the subviews' orientations while rotating.
If I were you, I'd use CAAnimation instead of using a view to rotate.
You may add the animation to every subview, try this:
CAKeyframeAnimation* animation;
animation = [CAKeyframeAnimation animation];
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL,imgview.layer.position.x,imgview.layer.position.y);
int p = [self getblank:tag];
float f = 2.0*M_PI - 2.0*M_PI *p/PHOTONUM;
float h = f + 2.0*M_PI *num/PHOTONUM;
float centery = self.view.center.y;
float centerx = self.view.center.x;
float tmpy = centery + RADIUS*cos(h);
float tmpx = centerx - RADIUS*sin(h);
imgview.center = CGPointMake(tmpx,tmpy);
CGPathAddArc(path,nil,self.view.center.x, self.view.center.y,RADIUS,f+ M_PI/2,f+ M_PI/2 + 2.0*M_PI *num/PHOTONUM,0);
animation.path = path;
CGPathRelease(path);
animation.duration = TIME;
animation.repeatCount = 1;
animation.calculationMode = #"paced";
return animation;
I assume you have a stored variable that represents the rotation of the "world", correct? If not, you should.
Then, for each image you add, also store a variable with it that represents its rotation to the world.
For example, if your world is rotated 180°, and you added a cup (which you want to appear right-side up when the world is upside-down) the cup's "offset" to the world rotation would be -180°.
Then, if the world is at 180° and you rotate your world by adding 90°, then the cup's new rotation value would be cup_rotate_offset + world_rotation, or 180°+270°, which is the same as saying 90°, and the top of the world would be facing left and the cup's top would be facing right.
You have to independently track the offset values for each added object.

Logic of using UISlider Xcode

I'm stuck at the moment controlling UISlider in XCode. I've made a horizontal slider in Interface Builder and manage to code the controls in xcode. Right now I'm just not sure how to code the logic. If the user slides the nib to the left, I'd it like it to rotate my image counter-clockwise and if the user slides it to the right, rotate the image clockwise.
Minimum value of slider I've set is 0 and max is 100. Initial value is 50.
For transformation I'm using CGAffineTransformMakeRotation(myAngle). I've set myAngle to float. As for the condition, here's a snippet of code:
mySlider.value = myAngle; // This is for CGAffine
if(myAngle < 50) {
myAngle -= 0.1;
}
if(myAngle > 50) {
myAngle += 0.1;
}
When I slide the nib, it only rotates anti-clockwise. What's the best logic can I use? Hope someone can help. Thanks.
-Hakimo
Note that the Doc says about the angle as,
The angle, in radians, by which this matrix rotates the coordinate system axes. In iOS, a positive value specifies counterclockwise rotation and a negative value specifies clockwise rotation. In Mac OS X, a positive value specifies clockwise rotation and a negative value specifies counterclockwise rotation.
I think something like the following should work. I am not sure though. (Considering that the minimumValue is 0).
float middleValue = slider.maximumValue / 2;
float value = middleValue - slider.value;
float degrees = (middleValue / 180) * value;
float radians = degrees * (3.14/180);
If you want a fixed position on the slider to correspond to a fixed angle of rotation, then you can do something like this:
float degreesToRadians = M_PI / 180.0;
CGFloat angle = (mySlider.value - 50) * degreesToRadians;
myView.transform = CGAffineTransformMakeRotation(angle);
On the other hand, if want more complex behavior, such as that the slider snaps to the middle except when being dragged, and how far it's dragged determines the speed of rotation of your view, then I would suggest using a timer. Set up an NSTimer to call a given selector, and in that selector do something like this:
- (void)timerFired:(NSTimer *)aTimer {
CGFloat angleDelta = (mySlider.value - 50) / 500;
// I'll assume myAngle is an instance variable.
myAngle += angleDelta;
myView.transform = CGAffineTransformMakeRotation(myAngle);
}
And somewhere else you need to write a method to set mySlider.value = 50 when the user stops dragging it, which you can do by adding a target/action for UIControlEventTouchUpInside and ...Outside on the slider.
If you want, you can make the rotation appear very smooth by using UIView animations each time you set the transform, using an animation duration equal to the NSTimer interval. If you do that, then I would actually suggest using the animation callback in place of the timer method, to avoid any possible crossover between the two events.

Translating a view and the rotating it problem

I have a custom UIImageView, I can drag it around screen by making a translation with (xDif and yDif is the amount fingers moved):
CGAffineTransform translate = CGAffineTransformMakeTranslation(xDif, yDif);
[self setTransform: CGAffineTransformConcat([self transform], translate)];
Let's say I moved the ImageView for 50px in both x and y directions. I then try to rotate the ImageView (via gesture recognizer) with:
CGAffineTransform transform = CGAffineTransformMakeRotation([recognizer rotation]);
myImageView.transform = transform;
What happens is the ImageView suddenly moves to where the ImageView was originally located (before the translation - not from the moved position + 50px in both directions).
(It seems that no matter how I translate the view, the self.center of the ImageView subclass stays the same - where it was originally laid in IB).
Another problem is, if I rotate the ImageView by 30 deg, and then try to rotate it a bit more, it will again start from the original position (angle = 0) and go from there, why wouldn't it start from the angle 30 deg and not 0.
You are overwriting the earlier transform. To add to the current transform, you should do this –
myImageView.transform = CGAffineTransformRotate(myImageView.transform, recognizer.rotation);
Since you're changing the transform property in a serial order, you should use CGAffineTransformRotate, CGAffineTransformTranslate and CGAffineTransformScale instead so that you add to the original transform and not create a new one.

Is there a way to get a position (or center) state from a unfinished UIView animation?

I have a UIView doing a simple animation. It adjusts its y position from 100 to 130 and then reverses. I want it to keep repeating so I have the repeat counter set to 999. Upon user input, I want to take the same UIView and adjust the x position. This is done by means of another UIView animation. The problem is that when the 2nd animation starts, the 1st animation (that goes from 100 to 130 in the y direction) just ends abruptly (as I read that it should). Is there any way to get the final position of the y coordinate of that UIView before it was ended? I would ideally like to have the UIView stay in the same y position that it was in while I translate the x coordinates.
Summary: UIView moves in the y direction from 100-130, reverses and repeats until user input is received. Once received, animation is cut short and UIView jumps to y=130. I would like a way to find out what the final y value was before the animation was cut short, so when new animation with x translation is used, the UIView will not jump to 130, but remain the same position it was in when the 1st animation ended.
I can't seem to see anything that would let you do that. It appears to me that once you set the animation in motion with UIView, then it (and all current state changes) are out of your hands and will only be "returned" to your control and availability once the animation is done and at the designated end point. Is this correct? Any insight would be appreciated. Thank you for your time.
You're looking for the "presentation layer".
Each UIView is rendered using a Core Animation layer, which is accessible from UIView's layer property.
CALayer has a presentationLayer method, which returns a CALayer that represents "a close approximation to the version of the layer that is currently being displayed".
So, to get the current position of your view:
CGRect currentViewFrame = [[myView.layer presentationLayer] frame];
I'm not sure if this is exactly what you want, but UIView's setAnimationBeginsFromCurrentState: used inside a beginAnimations block will cause the x animation to start wherever the y is in progress.
Perhaps for what you are trying to accomplish it would benefit to exercise a little more control over the animation. For this task I would suggestion using an NSTimer scheduled at whatever interval works best for you (1/30 to 1/60) and just adjust the UIView position every time the timer fires. This way you can always access the X and Y components of the view's position.
Something like:
- (void)timerFired:(NSTimer *)timer {
CGPoint p = view.center;
if (p.y >= 130.0) {
positiveMovement = NO;
}
else if (p.y <= 100.0) {
positiveMovement = YES;
}
if (positiveMovement)
p.y++;
else
p.y--;
view.center = p;
}
positiveMovement would just be a BOOL instance variable. You could also then just directly adjust the X value of the view's position elsewhere with user input and it would update accordingly.

How to rotate image around center point automatically with finger touch

On iPhone, how to implement rotating image around the center point using finger touch ?
Just like wheel, if you put finger on the iPhone screen , then move suddenly, then the image becoming rotating around center point just like the wheel, after a while, it becomes more and more slow , finally stop.
Who can help to give some pieces of codes (Object-C) or some suggest ?
I was working with a "spin the bottle"-app yesterday. On the window I have a ImageView with an bottle that's suppose to response to touches and rotate the way the user swipes his finger. I struggled to get my ImageView to rotate during the touch-events (TouchesBegan, Touchesoved, TouchesEnd). I used this code in TouchesMoved to find out the angle in witch to rotate the image.
public override void TouchesMoved (NSSet touches, UIEvent evt)
{
PointF pt = (touches.AnyObject as UITouch).LocationInView(this);
float x = pt.X - this.Center.X;
float y = pt.Y - this.Center.Y;
double ang = Math.Atan2(x,y);
// yada yada, rotate image using this.Transform
}
THIS IS IMPORTANT! When the ImageView rotates, even the x & y-coordinates changes. So touching the same area all the time would give me different values in the pt and prePt-points. After some thinking, googeling and reading I came up with an simple solution to the problem. The "SuperView"-property of the ImageView.
PointF pt = (touches.AnyObject as UITouch).LocationInView(this.SuperView);
Having that small change in place made it alot easier, no i can use the UITouch-metohs LocationInView and PreviousLocationInView and get the right x & y coordinates. Her is parts of my code.
float deltaAngle;
public override void TouchesMoved (NSSet touches, UIEvent evt)
{
PointF pt = (touches.AnyObject as UITouch).LocationInView(this.Superview);
float x = pt.X - this.Center.X;
float y = pt.Y - this.Center.Y;
float ang = float.Parse(Math.Atan2(dx,dy).ToString());
//do the rotation
if (deltaAngle == 0.0) {
deltaAngle = ang;
}
else
{
float angleDif = deltaAngle - ang;
this.Transform = CGAffineTransform.MakeRotation(angleDif);
}
}
Hope that helped someone from spending hours on how to figure out how to freaking rotate a bottle! :)
I would use the affine transformations - yuou can assign a transformation to any layer or UI element using the transform property.
You can create a rotation transform using CGAffineTransform CGAffineTransformMakeRotation( CGFloat angle) which will return a transformation that rotates an element. The default rotation should be around the centerpoint.
Be aware, the rotation is limited to 360 degrees, so if you want to rotate something more than that (say through 720 degrees) - you have to break the rotation into several sequences.
You may find this SO article useful as well.
The transform property of a view or layer can be used to rotate the image displayed within. As far as the spinning part goes, you just track the location and movement of touches in your view with touchesBegan, touchesMoved, and touchesEnded.
Use the distance and time between the touches updates to calculate a speed, and use that to set a rotational velocity. Once you start the image spinning, update the position periodically (with an NSTimer, maybe), and reduce the rotational velocity by some constant.