Question

I have a method to perform a small shake animation. This animation works somewhat, but it rotates every time I call it from landscape position. When the animation is called while the simulator is in landscape, the entire view rotates itself to portrait, and then performs the animation. The rotation itself is not animated, it just changes suddenly and with no delay. Everything in the view shifts as well, all buttons, text fields, image views, etc.

Animation Code:

- (void)shakeView
{   
    CGFloat t = 8.0;
    CGAffineTransform translateRight = CGAffineTransformTranslate(CGAffineTransformIdentity, 0.0, t);
    CGAffineTransform translateLeft = CGAffineTransformTranslate(CGAffineTransformIdentity, 0.0, -t);

    self.view.transform = translateLeft;

    [UIView animateWithDuration:0.07 delay:0.0 options:UIViewAnimationOptionAutoreverse|UIViewAnimationOptionRepeat animations:^{[UIView setAnimationRepeatCount:3.0];

        self.view.transform = translateRight;
    }
        completion:^(BOOL finished){
        if (finished)
            {
                [UIView animateWithDuration:0.05 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{self.view.transform = CGAffineTransformIdentity;
            }
        completion:NULL];
        }
    }];
}

Honestly, I don't do much with animations, so I have no idea what I am doing wrong here, much less what else I should try or where I should look for an answer.

I am looking to keep the animation while also keeping the orientation.

Was it helpful?

Solution

The issue is that the view has a non identity transform on it already. In order for a view to display properly in landscape, the system applies a transform on it that rotates the view. The transform of the view is CGAffineTransformIdentity ONLY when the device is in UIDeviceOrientationPortrait. This orientation rotation ONLY applies to the root child of the application, the first subview of the window. When you are initializing your translation animation with:

CGAffineTransform translateRight = CGAffineTransformTranslate(CGAffineTransformIdentity, 0.0, t);

You are telling the rotated view to ignore its rotation and snap back to its orientation in portrait mode. You can solve this issue by doing one of two things:

  1. Make the animated view a subview of the root view. This has the advantage that the system will still handle rotations for free, and there isn't any chance that your animation will screw up the the orientation changes. Furthermore, the code will be simpler and easier to maintain in the end.

  2. Store the initial transformation of the view, and apply all transformations to it.

I'll try to do #2 here.

- (void)shakeView
{   
    CGFloat t = 8.0;

    //store the initial transform and use it to create the animated transforms
    CGAffineTransform initialTransform = self.view.transform;
    CGAffineTransform translateRight = CGAffineTransformTranslate(initialTransform, 0.0, t);
    CGAffineTransform translateLeft = CGAffineTransformTranslate(initialTransform, 0.0, -t);

    self.view.transform = translateLeft;

    [UIView animateWithDuration:0.07 
                          delay:0.0 
                        options:UIViewAnimationOptionAutoreverse|UIViewAnimationOptionRepeat 
                     animations:^{
        [UIView setAnimationRepeatCount:3.0];

        self.view.transform = translateRight;
                                }
                     completion:^(BOOL finished){
        if (finished)
        {
            [UIView animateWithDuration:0.05 
                                  delay:0.0 
                                options:UIViewAnimationOptionBeginFromCurrentState 
                             animations:^{
                //restore the initial transform
                self.view.transform = initialTransform;
                                         }
                             completion:NULL];
        }
    }];
}

Note - As of iOS 8, the root view of the UIWindow is no longer transformed on rotation. Instead the bounds of the UIWindow itself changes. For another question about this, see here: UIWindow frame in ios 8 different from ios 7 in Landscape

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top