48

I have an iOS UIView with UIViewAnimationTransitionFlipFromRight. I need it to flip vertically though. The page curl transition won't cut it. I assume this will require something custom.

Any ideas?

Mark Amery
  • 143,130
  • 81
  • 406
  • 459
Christian Schlensker
  • 21,708
  • 19
  • 73
  • 121

8 Answers8

67

Just write your own method for the flip using Core Animation Transforms.

- (void)verticalFlip{
    [UIView animateWithDuration:someDuration delay:someDelay animations:^{
        yourView.layer.transform = CATransform3DMakeRotation(M_PI,1.0,0.0,0.0);
    } completion:^{
        // code to be executed when flip is completed
    }];
}

Make sure you have the Core Animation libraries/frameworks added and included and also have included math.h if you want to use the M_PI notation.

EDIT:

To have it essentially "change" the view when it's halfway flipped do something like this:

- (void)verticalFlip{
    [UIView animateWithDuration:someDuration delay:someDelay animations:^{
        yourView.layer.transform = CATransform3DMakeRotation(M_PI_2,1.0,0.0,0.0); //flip halfway
    } completion:^{
        while ([yourView.subviews count] > 0)
            [[yourView.subviews lastObject] removeFromSuperview]; // remove all subviews
        // Add your new views here 
        [UIView animateWithDuration:someDuration delay:someDelay animations:^{
            yourView.layer.transform = CATransform3DMakeRotation(M_PI,1.0,0.0,0.0); //finish the flip
        } completion:^{
            // Flip completion code here
        }];
    }];
}

This could also work:

- (void)verticalFlip{

    // Do the first half of the flip
    [UIView animateWithDuration:someDuration delay:someDelay animations:^{
        yourView.layer.transform = CATransform3DMakeRotation(M_PI_2,1.0,0.0,0.0); //flip halfway
    } completion:^{
        while ([yourView.subviews count] > 0)
            [[yourView.subviews lastObject] removeFromSuperview]; // remove all subviews
        // Add your new views here 
    }];

    // After a delay equal to the duration+delay of the first half of the flip, complete the flip
    [UIView animateWithDuration:someDuration delay:durationOfFirstAnimation animations:^{
        yourView.layer.transform = CATransform3DMakeRotation(M_PI,1.0,0.0,0.0); //finish the flip
    } completion:^{
        // Flip completion code here
    }];
}

Cheers.

runmad
  • 14,846
  • 9
  • 99
  • 140
Brenton Morse
  • 2,567
  • 4
  • 21
  • 16
  • I've gotten the view to flip but I need someway of showing the "backside" of the view. Is there a way to hide it's sub views halfway though this animation, so it simulates that the view is "empty" on the backside? – Christian Schlensker Mar 22 '11 at 15:25
  • 49
    For a 3D transform, you need to do yourView.layer.transform instead of yourView.transform – Chris Garrett Jun 28 '11 at 12:24
  • 5
    The completion block should have a bool. completion:^(BOOL finished){ – whitneyland Sep 04 '11 at 21:06
  • 1
    you should also use call + (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion in the code after "EDIT:" mark - options: parameter is missing in the original comment – Kostiantyn Sokolinskyi Sep 09 '11 at 15:42
  • you should rotate on 2 * M_PI not to get you view upside-down in the second half of the flip in the seconds code chunk – Kostiantyn Sokolinskyi Sep 10 '11 at 18:17
  • I've created similar solution, but also applying perspective while flipping views http://stackoverflow.com/questions/5144446/uiview-flip-vertical-animation/7463047#7463047 – xxcv Sep 18 '11 at 17:23
  • This is obsolete; `UIViewAnimationOptionTransitionFlipFromBottom`, added in iOS 5.0, is easier to understand than this. – Mark Amery Aug 11 '13 at 11:38
  • How can we make it on touch move in ios? – Hardik Mamtora May 05 '16 at 05:50
35

As of iOS 5.0, there's no need to write your own Core Animation transformation to do vertical flips. Just use UIKit's UIViewAnimationOptionTransitionFlipFromTop and UIViewAnimationOptionTransitionFlipFromBottom transitions, and all this stuff becomes a single method call.

These behave analagously to UIViewAnimationOptionTransitionFlipFromLeft and UIViewAnimationOptionTransitionFlipFromRight (which have been around since iOS 2.0).

Example usage:

[UIView transitionFromView:viewToReplace
                    toView:replacementView
                  duration:1
                   options:UIViewAnimationOptionTransitionFlipFromBottom
                completion:nil];

The above code will vertically flip the superview of viewToReplace. At the halfway point in the animation, at the instant when the superview is perpendicular to the screen and thus invisible, viewToReplace gets replaced by replacementView.

It's that easy.

Mark Amery
  • 143,130
  • 81
  • 406
  • 459
  • Note: Insert both views in their superview before calling the animation. The "viewToReplace" must be in front of "replacementView". Both views have to be set to hidden=false. – artkoenig Aug 16 '13 at 13:26
  • @Artjom What you claim differs from my experience. This works perfectly fine for me without inserting the `replacementView` into the superview before calling `transitionFromView:...`. Perhaps there's something else not obviously relevant that we're also doing differently leading to the difference in what we observe? Can you provide an example project demonstrating what you've described? – Mark Amery Aug 16 '13 at 13:33
  • In my case both views are initialized from nib as class members. But without inserting both in the flipping superview your method does not work. – artkoenig Aug 16 '13 at 13:49
  • just set the views as properties and then insert only one view, flipping handles the replacement afterwards – dreampowder Dec 01 '13 at 14:41
28

The code from Brenton didn't work for me so I did a little more digging through the apple docs and found this piece of code for an horizontal flip:

- (IBAction)toggleMainViews:(id)sender {
    [UIView transitionFromView:(displayingPrimary ? primaryView : secondaryView)
                        toView:(displayingPrimary ? secondaryView : primaryView)
                      duration:1.0
                       options:(displayingPrimary ? 
                                    UIViewAnimationOptionTransitionFlipFromRight :
                                    UIViewAnimationOptionTransitionFlipFromLeft)
                    completion:^(BOOL finished) {
                                   if (finished) {
                                       displayingPrimary = !displayingPrimary;
                                   }
                              }
    ];
}

You can do a vertical flip by changing the options from UIViewAnimationOptionTransitionFlipFromRight : UIViewAnimationOptionTransitionFlipFromLeft to UIViewAnimationOptionTransitionFlipFromTop : UIViewAnimationOptionTransitionFlipFromBottom.

Worked like a charm.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Miha Hribar
  • 5,776
  • 3
  • 26
  • 24
  • 1
    it's not a vertical flip. it's system provided horizontal flip – Kostiantyn Sokolinskyi Sep 09 '11 at 15:33
  • What's with the downvotes? This code is shorter and clearer than the other examples. Is there a hidden gotcha? – Russell Mull Apr 06 '12 at 11:21
  • 1
    Someone must have down voted it b/c the original question was about a vertical flip, not a horizontal one. The horizontal ones are easier b/c of the UIViewAnimationOptionTransition constants that Apple provides. Hence, the questioner's original query about a vertical transition effect. – idStar Apr 11 '12 at 17:39
  • 6
    You can do a vertical flip by changing the options from `....options:isDetailTileView ? UIViewAnimationOptionTransitionFlipFromRight : UIViewAnimationOptionTransitionFlipFromLeft)` to `....options:isDetailTileView ? UIViewAnimationOptionTransitionFlipFromTop : UIViewAnimationOptionTransitionFlipFromBottom)` – Seega May 10 '12 at 09:44
  • This is just what I was looking for. Much better to use built-in stuff than re-inventing the wheel. Cheers. – hktegner Sep 12 '12 at 09:41
  • 3
    One more thing. If you try to flip subviews and you don't want the superview to flip. Put your two subviews inside a container view. Then it will not flip your fullscreen view! – wolfrevo Mar 15 '13 at 23:14
  • -1 for being a horizontal flip, when a vertical one was desired. The question asker already told you in the question that he knew how to use `UIViewAnimationTransitionFlipFromRight`; this is totally irrelevant. – Mark Amery Aug 08 '13 at 17:59
  • Can we make it based on TouchMove event in ios? – Hardik Mamtora May 05 '16 at 05:49
  • @Cœur well, sure... but in doing so you've essentially made it a duplicate of my answer from 5 years earlier, so it's still adding no value that I can see. – Mark Amery Apr 16 '18 at 08:04
8

UIViewAnimationOptionTransitionFlipFromTop is easy to use, but we can not create an interactive transition using UIViewAnimationOptionTransitionFlipFromTop. We need change layer’s transform to create an interactive transition.

Just create a transform using CATransform3DMakeRotation is not enough, no light effect, no perspective. I write an sample to add these effect. You can change it to an interactive transition easily.

Demo:

Flip effect

Sample code:

CALayer *sideALayer = sideAView.layer;
CALayer *sideBLayer = sideBView.layer;
CALayer *containerLayer = containerView.layer;

sideALayer.opacity = 1;
sideBLayer.opacity = 0;
sideBLayer.transform = CATransform3DMakeRotation(M_PI, 0, 1, 0);
containerLayer.transform = CATransform3DIdentity;

CATransform3D perspectiveTransform = CATransform3DIdentity;
perspectiveTransform.m34 = -1.0 / containerViewWidth;
[UIView animateKeyframesWithDuration:1 delay:0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{

    [UIView addKeyframeWithRelativeStartTime:0 relativeDuration:0.5 animations:^{
        sideALayer.opacity = 0;
        containerLayer.transform = CATransform3DConcat(perspectiveTransform,CATransform3DMakeRotation(M_PI_2, 0, 1, 0));
    }];
    [UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.5 animations:^{
        sideBLayer.opacity = 1;
        containerLayer.transform = CATransform3DConcat(perspectiveTransform, CATransform3DMakeRotation(M_PI, 0, 1, 0));
    }];
} completion:nil];

sideAView and sideBView are subviews of containerView.

The containerView is set a black background.

BB9z
  • 2,432
  • 1
  • 30
  • 36
  • 1
    There is something wrong with this animation. As you can see, in second half of rotation the perspective is completely removed and it looks weird. – Tankista Sep 21 '15 at 23:07
3

Swift 4.0 Version 100% Working Solution

// view1: represents view which should be hidden and from which we are starting
// view2: represents view which is second view or behind of view1
// isReverse: default if false, but if we need reverse animation we pass true and it
// will Flip from Left

func flipTransition (with view1: UIView, view2: UIView, isReverse: Bool = false) {
    var transitionOptions = UIView.AnimationOptions() // Swift 4.2
    transitionOptions = isReverse ? [.transitionFlipFromLeft] : [.transitionFlipFromRight] // options for transition
    
    // animation durations are equal so while first will finish, second will start
    // below example could be done also using completion block.
    
    UIView.transition(with: view1, duration: 1.5, options: transitionOptions, animations: {
        view1.isHidden = true
    })
    
    UIView.transition(with: view2, duration: 1.5, options: transitionOptions, animations: {
        view2.isHidden = false
    })
}

Call of the function:

anim.flipTransition(with: viewOne, view2: viewTwo)
anim.flipTransition(with: viewTwo, view2: viewOne, isReverse: true)

Best practice will be to create UIView extension and hold this function to that extension so it will be accessible to any UIView child object. This solution also can be written using completionBlock.

C0mrade
  • 1,215
  • 1
  • 10
  • 23
0

To flip into UIView:

  -(void) goToSeconVC
 {
  SecondVC *secondVCObj = [[SecondVC alloc] init];
  [UIView beginAnimation: @”View Flip” context: nil];
  [UIView setAnimationDuration: 1.0];
  [UIView setAnimationCurve: UIViewAnimationCurveEaseInOut];
  [UIView setAnimationTransition: UIViewAnimationTransitionFlipFromRight forView: self.navigationController.view cache: NO];
  [sef.navigationController pushViewController: seconVCObj animated: YES];
  [UIView commitAnimation];
 }
**To flip into a view controller:**
          viewController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
         [self presentViewController:viewController animated:YES completion:nil];
**To flip out of it:**

[self dismissViewControllerAnimated:YES completion:nil];
Mannam Brahmam
  • 2,225
  • 2
  • 24
  • 36
0

Swift 5 version of @C0mrade

func flipTransition (with view1: UIView, view2: UIView, isReverse: Bool = false) {
    var transitionOptions = UIView.AnimationOptions()
    transitionOptions = isReverse ? [.transitionFlipFromLeft] : [.transitionFlipFromRight]

    UIView.transition(with: view1, duration: 1.5, options: transitionOptions, animations: {
        view1.isHidden = true
    })

    UIView.transition(with: view2, duration: 1.5, options: transitionOptions, animations: {
        view2.isHidden = false
    })
}
Alok C
  • 2,787
  • 3
  • 25
  • 44
  • I used this code, but it doesn't look smooth. What I expect is that in the middle of animation hiding first view, the will become hidden. Currently it is hidden at the beginning. – Marcin Żmigrodzki Apr 01 '21 at 14:11
-3
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.75];
// checks to see if the view is attached
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight
                       forView:flipLabel
                         cache:YES];

 flipLabel.backgroundColor = [UIColor yellowColor];



[UIView commitAnimations];

You can make any modification which you want while flipping view, Here I have change the background color

Crazy Developer
  • 3,464
  • 3
  • 28
  • 62
  • -1 because a) This is a horizontal flip, and b) I don't think this adds anything new that my answer doesn't already offer. – Mark Amery May 24 '14 at 10:32