Can only rotate UIImageView once - iphone

I have some code which causes a UIImageView to rotate 180 degrees when the user touches the image. Unfortunately, when the user touches it again nothing happens. I'm assuming that it is determined that the image has already been rotated 180 degrees and that might be why it's not moving again. How do i solve this problem, thanks.
-(void) spinSun
{
[UIView animateWithDuration:2.0f animations:^{
imgSun.transform = CGAffineTransformMakeRotation(-3.142);
} completion:^(BOOL finished) {
}];
}

You have to create a new transformation matrix:
imgSun.transform = CGAffineTransformRotate(imgSun.transform, -3.142)

Related

Transform resets(redraws) all my former moved views

I'm moving around image views with timers and animation blocks. At the same time a user can click on a few of these views(buttons) and then I have some more animation blocks that start moving/resizing/rotating different image views.
Changing alpha of views and giving out new CGpoint to views works very well for me but calling resizing or rotating with the uiview.transform function seems to reset all my views to their starting position(from the storyboard).
I really would love to be able to use this transform function inside an animation block but if I can't somehow bypass this reset it is not really a viable option for me.
example code:
- (void)moveGust:(NSTimer*) timer
{
if(centerCloud1.x >= 1100)
{
centerCloud1.x = -79;
gustOutlet.center = centerCloud1;
}
if(centerCloud2.x >= 1100)
{
centerCloud2.x = -79;
cloudOutlet.center = centerCloud2;
}
centerCloud1 = gustOutlet.center;
centerCloud1.x = round(centerCloud1.x+10);
centerCloud2 = cloudOutlet.center;
centerCloud2.x = round(centerCloud2.x+8);
[UIView animateWithDuration:2 delay:0 options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionCurveLinear animations:^(void){
gustOutlet.center = centerCloud1;
cloudOutlet.center = centerCloud2;
}completion:^(BOOL Finished){ }];
}
- (IBAction)signAction:(id)sender {
[UIView animateWithDuration:0 animations:^(void) {
signTop.transform = CGAffineTransformScale(signTop.transform, 1.1, 1.1);
}];
}
Any ideas?
Thanks!
Successive transforms need to be concatenated using CGAffineTransformConcat().
When you set the view's transform equal to a new transform, you effectively "undo" previous transforms. To do a series of transforms, concatenate them with the above method.

Current value of rotation during a animateWithDuration

I'm using animateWithDuration to have one of my UIVIew rotating endlessly. Now, I need to get the current value of my UIView's rotation at a specific time.
I've tried to use the following function :
-(float)getCurrentRotation{
CGAffineTransform transform = ((UIImageView*)[self.subviews objectAtIndex:0]).transform;
return (atan2(transform.b, transform.a));
}
but it always returns the same value (M_PI/2) as it's the value I've specified in my initial call to animateWithDuration :
[UIView animateWithDuration:4 delay:0 options:( UIViewAnimationOptionRepeat|UIViewAnimationOptionCurveLinear) animations:^{
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI/2);
((UIImageView*)[self.subviews objectAtIndex:0]).transform = transform;
}
completion:^(BOOL finished){
}];
Is there a way to have the current value of the rotation ?
Thanks.
When using CoreAnimation based animation, your view properties (bounds, transform...) never actually change. What changes are the properties of the layer displayed on screen while animating (see this documentation page), which is accessible using view.layer.presentationLayer.
Thus, the following line will get you the actual transform at the time you ask for it:
CGAffineTransform transform = CATransform3DGetAffineTransform([(CALayer*)[[[self.subviews objectAtIndex:0] layer] presentationLayer] transform]);
You must link against and include the QuartzCore framework, which defines CALayer, for this to work.
#import <QuartzCore/QuartzCore.h>
I'm not sure if it's possible to watch the "value of the rotation" while it is shown on screen. But I'd say it is not, because your view object does have the new property value immediately and the animation takes place somewhere deep inside the framework on objects you're not able to reach.
To fulfill your goal I'd separate the rotation into single steps with a granularity sufficient for your needs. Then just count the steps and make the current step available. Could look a bit like this (not tested, just a scribble):
//step and rotator declared as class members
//rotator is the one which is referred to as ((UIImageView*)[self.subviews objectAtIndex:0]) in the original question
-(CGFloat)currentRotation
{
return step*(M_PI/16);
}
-(void)turnturnturn
{
[UIView animateWithDuration:0.2
animations:^
{
rotator.transform = CGAffineTransformConcat(CGAffineTransformMakeRotation(M_PI/16), rotator.transform);
step++;
}
completion:^(BOOL finished)
{
if(step == 32)
{
step = 0;
rotator.transform = CGAffineTransformIdentity;
}
[self turnturnturn];
}];
}

Rotate in z-axis like core-animation slot machine

I was to able to move or animate my UIView by this code down here:
- (void) makeAnim1{
//downward animation
[UIView animateWithDuration:1.5
delay:0.15
options: UIViewAnimationCurveLinear
animations:^{
carousel.frame = CGRectOffset(carousel.frame, 0, 650);
}
completion:^(BOOL finished){ //task after an animation ends
[self performSelector:#selector(makeAnim1_1) withObject:nil afterDelay:2.0];
NSLog(#"Done!");
}];
}
- (void) makeAnim1_1{
//upward animation
[UIView animateWithDuration:1.5
delay:0.1
options: UIViewAnimationCurveLinear
animations:^{
carousel.frame = CGRectOffset(carousel.frame, 0, -650);
}
completion:^(BOOL finished){
NSLog(#"Done!");
}];
}
But it only move the UIView up and down. How can I make it spin like a Slot machine but only contains one image or view. Like rotate in z axis. But make it look like it contains more than one image.
Thanks for the help.
Instead of changing the frame inside the animation block you change the transform. The transform can be used to scale, rotate and translate (move) the view. You can only rotate around the z-axis but that is what you asked for. The transform property on the view takes a CGAffineTransform, like this:
// rotate pi/2 degrees clockwise
carousel.transform = CGAffineTransformMakeRotation(M_PI_2);
If you need to do more advanced transforms like rotating around another axis then you would need to use a little bit of Core Animation and to set the transform property of the views layer (which takes a CATransform3D instead of a CGAffineTransform).
As with all Core Animation code you need to import QuartzCore.framework and include QuartzCore/QuartzCore.h in your code.
The above animations you are doing is UIView animations which are only meant to animate views but the animation you are asking for requires more advanced animations of the views layer. I suggest that you look at the documentation for CABasicAnimation and also take a look at the Core Animation Programming Guide for iOS to learn more.
You can animate the x rotation of a views layer like this:
CABasicAnimation *slotAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.x"];
[slotAnimation setToValue:[NSNumber numberWithFloat:M_PI_2]];
// animation customizations like duration and timing
// that you can read about in the documentation
[[carousel layer] addAnimation:slotAnimation forKey:#"mySlotAnimation"];
The above code will indeed rotate the view around the x axis but will look very silly without perspective (search SO for "perspective Core Animation", it has been asked about before). There is probably a lot of tweaking to get the correct look but this should be enough to get you started.

CGAffineRotation Skews Image Photos

My iOS has registered for device orientation change notifications via NSNotificationCenter, and has a method that responds to the different orientation changes. The following code is called to change the orientation and location of a view when rotating the phone to "landscape-left".
camAngle = [(NSNumber *)[_cameraToggleButton valueForKeyPath:#"layer.transform.rotation.z"] floatValue];
camDegrees = (RADIANS_TO_DEGREES(camAngle));
if (!(camDegrees == 90)) {
_cameraToggleButton.alpha = 0.0;
_cameraToggleButton.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(90));
_cameraToggleButton.frame = CGRectMake(274, 350, 66, 54);
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.4];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
_cameraToggleButton.alpha = 1.0;
[UIView commitAnimations];
}
And my equations to convert radians and degrees:
#define RADIANS_TO_DEGREES(radians) ((radians) * (180.0 / M_PI))
#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)
The view rotates correctly and assumes the correct position on screen, but the sides of the image are suddenly squashed, making the image look condensed. This only happens when rotating the phone into landscape position (left or right). Rotating to upside down or back to portrait translates and rotates the view fine, with essentially the same code. What could cause this skewing of the images in landscape position?
You should not set the frame of the _cameraToggleButton after you set the rotation transform on it. To re-position the _cameraToggleButton after you set the rotation, you should change it's center like so:
_cameraToggleButton.center = CGPointMake(274, 350);
Also, as someone else suggested, use UIViewContentModeScaleAspectFit instead of UIViewContentModeScaleToFill.

CGRect not changing during animation

I'm animating an UIView and want to check if its frame intersects with another UIView's frame. This is how I "spawn" one of the UIViews:
- (void) spawnOncomer
{
oncomer1 = [[Oncomer alloc] initWithType:#"car"];
[self.view addSubview:oncomer1];
//make the oncomer race across the screen
[UIView beginAnimations:nil context:NULL]; {
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:3.0];
[UIView setAnimationDelegate:self];
CGRect f = oncomer1.frame;
f.origin.y = self.view.frame.size.height+oncomer1.frame.size.height;
oncomer1.frame = f;
[UIView setAnimationDidStopSelector:#selector(decountCar)];
}
[UIView commitAnimations];
}
So far so good. Now I want to check if this UIView and my other UIView collide by doing this:
- (void) checkCollision {
bool collision = CGRectIntersectsRect(car.frame, oncomer1.frame);
if (collision) {
NSLog(#"BOOOOOOOM");
} else {
NSLog(#"Oncomer: %#", NSStringFromCGRect(oncomer1.frame));
}
}
However, they never collide. Although I see oncomer1 moving across the screen, loggin oncomer1.frame never changes: it keeps outputting Oncomer: {{50, 520}, {30, 60}}(which are the post-animation values).
Does anyone know why this is?
P.s. Both methods are called directly or indirectly with a NSTimer and are thus performed in the background
UIView geometry updates apply immediately to their CALayer, even in an animation block. To get a version of a layer with animations applied, you can use -[CALayer presentationLayer], like this (warning - untested code):
- (void) checkCollision {
CGRect oncomerFrame = oncomer1.layer.presentationLayer.frame;
bool collision = CGRectIntersectsRect(car.frame, oncomerFrame);
if (collision) {
NSLog(#"BOOOOOOOM");
} else {
NSLog(#"Oncomer: %#", NSStringFromCGRect(oncomerFrame));
}
}
From your code:
CGRect f = oncomer1.frame;
f.origin.y = self.view.frame.size.height+oncomer1.frame.size.height;
oncomer1.frame = f;
A logical explanation of the frame never changing is that you are only changing the y of the frame, and you are always setting it to the same value determined by two heights.