CABasicAnimation is ignored during rotation - ipad

I have a UIView that in the layoutSubviews method repositions its subviews based on the orientation of the iPad. Within the layoutSubviews method I have a CABasicAniamtion that is supposed to animate the repositioning of the subviews. The animations are set to specific duration but this duration is being ignored and repositioning happens immediately. I know the animations are firing because I am seeing the AnimationDidStart and AnimationDidStop methods being fired. I know this has something to do with the CALayers of the UIView but I can not find anything on the web to explain how to fix this. Any help would be appreciated.
if([[UIDevice currentDevice] orientation] == UIDeviceOrientationPortrait || [[UIDevice currentDevice] orientation] == UIDeviceOrientationPortraitUpsideDown)
{
NSLog(#"Orientation: Portrait");
//Hide icon
CABasicAnimation *iconAnimation = [CABasicAnimation animationWithKeyPath:#"position"];
iconAnimation.fromValue = [NSValue valueWithCGPoint:[iconImageView center]];
iconAnimation.toValue = [NSValue valueWithCGPoint:iconThinPosition];
iconAnimation.duration = 2.7f;
iconAnimation.autoreverses = NO;
iconAnimation.repeatCount = 1;
iconAnimation.delegate = self;
[iconImageView.layer addAnimation:iconAnimation forKey:#"position"];
//[iconImageView setCenter:iconThinPosition];
[iconImageView.layer setPosition:iconThinPosition];
//[iconImageView setTransform: CGAffineTransformIdentity];
CABasicAnimation *textAnimation = [CABasicAnimation animationWithKeyPath:#"position"];
textAnimation.fromValue = [NSValue valueWithCGPoint:[textImageView center]];
textAnimation.toValue = [NSValue valueWithCGPoint:textThinPosition];
textAnimation.duration = 2.7f;
textAnimation.autoreverses = NO;
textAnimation.repeatCount = 1;
textAnimation.delegate = self;
[textImageView.layer addAnimation:textAnimation forKey:#"position"];
[textImageView.layer setPosition:textThinPosition];
}
else if([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeLeft || [[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeRight)
{
NSLog(#"Orientation: Landscape");
// Show Icon
CABasicAnimation *iconAnimation = [CABasicAnimation animationWithKeyPath:#"position"];
iconAnimation.fromValue = [NSValue valueWithCGPoint:[iconImageView center]];
iconAnimation.toValue = [NSValue valueWithCGPoint:iconShownPosition];
iconAnimation.duration = 2.7f;
iconAnimation.autoreverses = NO;
iconAnimation.repeatCount = 1;
iconAnimation.delegate = self;
[iconImageView.layer addAnimation:iconAnimation forKey:#"position"];
[iconImageView.layer setPosition:iconShownPosition];
CABasicAnimation *textAnimation = [CABasicAnimation animationWithKeyPath:#"position"];
textAnimation.fromValue = [NSValue valueWithCGPoint:[textImageView center]];
textAnimation.toValue = [NSValue valueWithCGPoint:textShownPosition];
textAnimation.duration = 2.7f;
textAnimation.autoreverses = NO;
textAnimation.repeatCount = 1;
textAnimation.delegate = self;
[textImageView.layer addAnimation:textAnimation forKey:#"position"];
[textImageView.layer setPosition:textShownPosition];
}
}

I wonder if it isn't being ignored so much as there is already an animation transaction in progress when your layoutSubviews gets called. One thing you could try to confirm this is to override -didRotateFromInterfaceOrientation and call your layoutSubviews from there. See if your views animate then.
- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// [self layoutSubviews];
// Give it a second to settle after the rotation and then call
// layoutSuviews explicitly.
[self performSelector:#selector(layoutSubviews) withObject:nil afterDelay:1.0f];
}
Another thing to think about is that since you are only animating the position of the UIView's layer, you could use UIView animations instead of explicit animation. Something like:
if([[UIDevice currentDevice] orientation] == UIDeviceOrientationPortrait || [[UIDevice currentDevice] orientation] == UIDeviceOrientationPortraitUpsideDown)
{
NSLog(#"Orientation: Portrait");
//Hide icon
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:2.7f];
[iconImageView setCenter:iconThinPosition];
[textImageView setCenter:textThinPosition];
[UIView commitAnimations];
}
else if([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeLeft || [[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeRight)
{
NSLog(#"Orientation: Landscape");
// Show Icon
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:2.7f];
[iconImageView setCenter:iconShownPosition];
[textImageView setCenter:textShownPosition];
[UIView commitAnimations];
}

Related

scale image and button orientation correctly for landscape and portrait mode ipad

Developed a ipad application which looks good in portrait mode.Like following:
But, not looking good in landscape mode.
How to make this correct? I have autolayout option enabled...
Note: I'm adding back ground iamge as
self.view.backgrounfcolor = [UIColor colorwithparttenimage:[UIImage imageNamed:#"ems.png"]];
Here is just for reference . just copied and pasting from my repository . Change accordingly .
-(void)viewWillAppear:(BOOL)animated{
[[self navigationController] setNavigationBarHidden:YES animated:NO];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[self orientationChanged:(UIInterfaceOrientation)[[UIDevice currentDevice]orientation ]];
}
-(BOOL) supportedInterfaceOrientations{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
-(BOOL) shouldAutorotate{
return YES;
}
-(void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
[self orientationChanged:toInterfaceOrientation];
}
-(void) orientationChanged: (UIInterfaceOrientation)orientation{
if(orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown){
NSLog(#"Changed Orientation To Portrait");
// self.viewPortrait.hidden = NO;
// self.viewLandscape.hidden = YES;
[self portraitOrientation];
}
else if(orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight){
NSLog(#"Changed Orientation To Landscape");
//self.viewPortrait.hidden = YES;
//self.viewLandscape.hidden = NO;
[self landscapeLeftOrientation];
/*
if(deviceOrientation ==UIInterfaceOrientationLandscapeLeft){
NSLog(#"Changed Orientation To Landscape left");
[self landscapeLeftOrientation];
}else{
NSLog(#"Changed Orientation To Landscape right");
[self landscapeRightOrientation];
}
*/
}
}
-(void)landscapeLeftOrientation{
// Rotates the view.
NSLog(#"LandscapeLeft");
CGAffineTransform transform = CGAffineTransformMakeRotation(0);
self.view.transform = transform;
// Repositions and resizes the view.
CGRect contentRect = CGRectMake(0, 0, 480, 320);
self.view.bounds = contentRect;
self.view.bounds = contentRect;
self.img.center = CGPointMake(256, 23);
self.btnHome.center = CGPointMake(437.0f, 23.0f);
self.messageList.frame = CGRectMake(54, 54, 405,200);
self.txtMessage.frame = CGRectMake(26, 262, 355, 30);
self.btnSendMsg.frame = CGRectMake(399, 262,61,32);
}
-(void)portraitOrientation{
// Rotates the view.
CGAffineTransform transform = CGAffineTransformMakeRotation(0);
self.view.transform = transform;
// Repositions and resizes the view.
CGRect contentRect = CGRectMake(0, 0, 320, 480);
self.view.bounds = contentRect;
self.img.center = CGPointMake(160.0f, 24.0f);
self.btnHome.center = CGPointMake(277.0f, 24.0f);
self.messageList.frame = CGRectMake(3,49, 341,321);
self.txtMessage.frame = CGRectMake(3, 378, 235, 30);
self.btnSendMsg.frame = CGRectMake(256, 378,61,32);
}

UIview Flip darkens the Views

I've been implementing a simple FlipView in iOS : A UIView that contains two subviews, displaying one at a time, and when you click on it, it flips them.
I'm using the following to animate the flipping.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
#synchronized(self){
if(!self.flipping){
self.flipping = YES;
UIView *toView = self.currentView == self.primaryView ? self.secondaryView : self.primaryView;
[UIView transitionFromView:self.currentView toView:toView duration:self.speed options:UIViewAnimationOptionTransitionFlipFromLeft|UIViewAnimationOptionCurveEaseInOut completion:^(BOOL finished) {
[self.currentView removeFromSuperview];
self.currentView = toView;
self.flipping = NO;
}];
}
}
}
Pretty straight forward, right ?
But what bugs me is that, while the views are flip, the flipped content is darkened. Which shows, against a light background.
Would anyone knows a solution to have the exact same animation, but without the darkening (<= is that even a word ?)
Thanks in advance !
PS : I'm targeting IOS 5 and above.
I recently had a problem with similar symptoms and I was adding a subview over and over again else where in my code whenever I committed a certain action. Maybe you are doing something similar? When your touches end, are you doing something else to your flipped content? You probably need to remove the subviews being added IF that is your problem.
I succeeded, getting inspiration in the code I found here http://www.mycodestudio.com/blog/2011/01/10/coreanimation/ (and he, himself, took inspiration from http://www.mentalfaculty.com/mentalfaculty/Blog/Entries/2010/9/22_FLIPPIN_OUT_AT_NSVIEW.html)
Anyway, what I do spin between two views.
- (void)flip{
#synchronized(self){
if(!self.flipping){
self.flipping = YES;
UIView *bottomView = self.currentView == self.primaryView ? self.secondaryView : self.primaryView;
CALayer *top = self.currentView.layer;
CALayer *bot = bottomView.layer;
CAAnimation *topAnimation = [self flipAnimationWithDuration:self.speed/2.0 forLayerBeginningOnTop:YES scaleFactor:1];
CAAnimation *bottomAnimation = [self flipAnimationWithDuration:self.speed/2.0 forLayerBeginningOnTop:NO scaleFactor:1];
CGFloat zDistance = 1500.0f;
CATransform3D perspective = CATransform3DIdentity;
perspective.m34 = -1. / zDistance;
top.transform = perspective;
bot.transform = perspective;
topAnimation.delegate = self;
[CATransaction setCompletionBlock:^{
[top removeAllAnimations];
[self.currentView removeFromSuperview];
self.currentView = bottomView;
[self addSubview:bottomView];
[CATransaction setCompletionBlock:^{
self.flipping = NO;
[bot removeAllAnimations];
}];
[CATransaction begin];
[bot addAnimation:bottomAnimation forKey:#"flip"];
[CATransaction commit];
}];
[CATransaction begin];
[top addAnimation:topAnimation forKey:#"flip"];
[CATransaction commit];
}
}
}
-(CAAnimation *)flipAnimationWithDuration:(NSTimeInterval)aDuration forLayerBeginningOnTop:(BOOL)beginsOnTop scaleFactor:(CGFloat)scaleFactor
{
// Rotating halfway (pi radians) around the Y axis gives the appearance of flipping
CABasicAnimation *flipAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.y"];
CGFloat startValue = beginsOnTop ? 0.0f : M_PI/2;
CGFloat endValue = beginsOnTop ? -M_PI/2 : 0.0f;
flipAnimation.fromValue = [NSNumber numberWithDouble:startValue];
flipAnimation.toValue = [NSNumber numberWithDouble:endValue];
// Shrinking the view makes it seem to move away from us, for a more natural effect
// Can also grow the view to make it move out of the screen
CABasicAnimation *shrinkAnimation = nil;
if (scaleFactor != 1.0 ) {
shrinkAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
shrinkAnimation.toValue = [NSNumber numberWithFloat:scaleFactor];
// We only have to animate the shrink in one direction, then use autoreverse to "grow"
shrinkAnimation.duration = aDuration * 0.5;
shrinkAnimation.autoreverses = YES;
}
// Combine the flipping and shrinking into one smooth animation
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
animationGroup.animations = [NSArray arrayWithObjects:flipAnimation, shrinkAnimation, nil];
// As the edge gets closer to us, it appears to move faster. Simulate this in 2D with an easing function
animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:beginsOnTop?kCAMediaTimingFunctionEaseIn:kCAMediaTimingFunctionEaseOut];
animationGroup.duration = aDuration;
// this really means keep the state of the object at whatever the anim ends at
// if you don't do this then it reverts back to the original state (e.g. brown layer)
animationGroup.fillMode = kCAFillModeForwards;
animationGroup.removedOnCompletion = NO;
return animationGroup;
}
The two views are named primaryView and secondaryView. You can use any view, (ImageView, text view...)

set the frame size when orientation is changed in ios

I want to change the orientation of a view in ios 6. When the orientation is changed, I want to set the frame size of the views.
My Sample Code for orientation change:
-(BOOL)shouldAutorotate
{
return YES;
}
-(NSInteger)supportedInterfaceOrientations
{
NSInteger mask = 0;
intOrientation = [[UIApplication sharedApplication] statusBarOrientation];
if(intOrientation == UIInterfaceOrientationPortrait || intOrientation == UIInterfaceOrientationPortraitUpsideDown)
{
mask |= UIInterfaceOrientationMaskPortrait;
} else
if(intOrientation == UIInterfaceOrientationLandscapeLeft)
{
mask |= UIInterfaceOrientationMaskLandscapeLeft;
}
}
NSLog (#"mask %d",mask);
return mask;
}
// Here now mask value is 2 because my default mode is portrait.
Now when I change my orientation to landscape, I want to set the frame size. Please guide me on how to do that.
ok..Can I set the frame size for the view like dis by taking the version and comparing it in viewdidLoad:
orientation = [[UIDevice currentDevice] orientation];
NSString *reqSysVer = #"6.0";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
NSLog (#"ios version %#",currSysVer);
if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending)
{
if (orientation == UIInterfaceOrientationMaskPortrait) {
self.view.frame = CGRectMake(0, 0, 1000, 800);
self.tableView1.frame = CGRectMake(1, 0, 764,510);
}
else if (orientation == UIInterfaceOrientationMaskLandscapeLeft)
{
self.view.frame = CGRectMake(0, 0, 1300, 370);
self.tableView1.frame = CGRectMake(0, 0, 1300, 370);
self.view.backgroundColor = [UIColor blackColor];
}
}
you can get a notification when the interface orientation changes:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];
and add a function like:
- (void)didRotate:(NSNotification *)notification {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (orientation == UIDeviceOrientation...)
{
// add some code
}
}
The best solution would be to use Auto Layout or Constraints, I think.
But you can also check for the device orientation using
[UIDevice currentDevice].orientation
or check the size (e.g. for iPhone 5 support) using
[UIScreen mainScreen].bounds.size.height
// Update:
Consider that on iOS 8 width and height were switched in landscape. So witdh and height are always depending on the orientation now.

Continue rotating an image after rotation gesture has ended

I have an image that i can rotate using a rotation gesture but i want to continue rotating the image based on the velocity of the rotation.
Its rotating after i have released my fingers but when it stops it doesn't stay at the rotated angle. It sets the rotation angle to the last angle after the finger has been released.
- (void)rotateCoaster:(UIRotationGestureRecognizer *)recognizer {
if(recognizer.numberOfTouches >= 2) {
twoTouchesDetected = YES;
}
else {
twoTouchesDetected = NO;
}
if(recognizer.state == UIGestureRecognizerStateBegan ||
recognizer.state == UIGestureRecognizerStateChanged)
{
recognizer.view.transform = CGAffineTransformRotate(recognizer.view.transform,
recognizer.rotation);
_player.rotationVelocity = recognizer.velocity;
_player.rotation = recognizer.rotation;
[recognizer setRotation:0];
}
if( !twoTouchesDetected ) {
[self rotationCalculations:nil];
rotating = YES;
}
if( recognizer.state == UIGestureRecognizerStateEnded ||
recognizer.state == UIGestureRecognizerStateCancelled ) {
[self dropDownCoaster];
}
_player.rotationVelocity = recognizer.velocity;
}
- (void)rotationCalculations:(NSTimer *) dt {
// dTime += dt.timeInterval;
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:2.0] forKey:kCATransactionAnimationDuration];
CABasicAnimation *animation;
animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
// animation.fromValue = [NSNumber numberWithFloat:0.0];
// animation.toValue = [NSNumber numberWithFloat:_player.rotationVelocity * M_PI];
animation.fromValue = nil;
animation.toValue = nil;
animation.byValue = [NSNumber numberWithFloat:_player.rotationVelocity * M_PI];
animation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut];
animation.delegate = self;
[_coasterImageView.layer addAnimation:animation forKey:#"rotationAnimation"];
[CATransaction commit];
}
I believe your problem here is that, as WWDC 2011 Session 421 - Core Animation Essentials #~ 26:00 says, "Explicit animations do not effect the model values of your layer hierarchy".
Basically, in addition to using Core Animation to animate a rotation you need to change the property on the model object (UIView or CALayer) as well. So that when the animation finishes and the view renders normally again it is at the correct position and at the correct rotation.
Likely something like:
_coasterImageView.view.transform = CGAffineTransform... //rotation with toValue
If you are going to be doing much work with Core Animation I highly suggest you watch the video.

My modal view does not rotate

I encounter an issue with a modal view display. My first view rotate very well. The controller of the modal view implements the "ShouldAutorotationToInterfaceRotation" method but when I rotate the device or simulator the modal view doesn't rotate.
I've added some comments to check if the first view detects the rotation events instead of the modal view but no, it doesn't get the event.
Do you have a clue?
You will need to subscribe to NSNotificationCenter to receive rotation events for your modal view. Then, if you want to keep the modal view orientated correctly relative to the bottom view, one solution would be to transform the view. Here's one implementation:
Put this in the viewDidLoad and save the original orientation as an angle:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:)
name:UIDeviceOrientationDidChangeNotification object:nil];
int initialOrientation = [[UIApplication sharedApplication] statusBarOrientation];
if (initialOrientation == UIDeviceOrientationPortrait)
initialAngle = 0.0;
else if (initialOrientation == UIDeviceOrientationLandscapeRight)
initialAngle = 90.0;
else if (initialOrientation == UIDeviceOrientationPortraitUpsideDown)
initialAngle = 180.0;
else if (initialOrientation == UIDeviceOrientationLandscapeLeft)
initialAngle = 270.0;
currentAngle = initialAngle;
Then define the function orientationChanged:(NSNotification *)notification with code to rotate correctly relative to the initial orientation:
int newOrientation = [[notification object] orientation];
if (newOrientation == UIDeviceOrientationPortrait)
desiredAngle = 0.0;
else if (newOrientation == UIDeviceOrientationLandscapeRight)
desiredAngle = 90.0;
else if (newOrientation == UIDeviceOrientationPortraitUpsideDown)
desiredAngle = 180.0;
else if (newOrientation == UIDeviceOrientationLandscapeLeft)
desiredAngle = 270.0;
if(desiredAngle != currentAngle)
{
CGAffineTransform rotate = CGAffineTransformMakeRotation(M_PI * (-desiredAngle+initialAngle) / 180.0);
CGAffineTransform translate = CGAffineTransformMakeTranslation(0, 0);
[[self releaseView] setTransform:CGAffineTransformConcat(translate, rotate)];
}
currentAngle = desiredAngle;

Resources