I'm currently testing new iOS 7 views controller transition.
What i want is a custom modal presenting transition that present your next view cut into several strip from top off screen. Each strip should appear after an incremental delay to give the desired effect.
So my code looks like this :
- (void)presentModalWithContext:(id<UIViewControllerContextTransitioning>)context
{
UIView *inView = [context containerView];
UIView *fromView = [context viewControllerForKey:UITransitionContextFromViewControllerKey].view;
UIView *toView = [context viewControllerForKey:UITransitionContextToViewControllerKey].view;
NSTimeInterval stripTime = 1.0;
NSTimeInterval stripDelay = 1.0;
NSInteger stripCount = 10;
CGFloat stripHeight = toView.frame.size.height / stripCount;
for (NSInteger i = 0; i < stripCount; i++)
{
CGFloat offsetY = i*stripHeight;
CGRect snapRect = CGRectMake(0, offsetY, toView.frame.size.width, stripHeight);
UIView *view = [toView resizableSnapshotViewFromRect:snapRect afterScreenUpdates:YES withCapInsets:UIEdgeInsetsZero];
CGRect stripRect = CGRectMake(0, -(stripCount-i)*stripHeight, snapRect.size.width, snapRect.size.height);
view.frame = stripRect;
[inView insertSubview:view aboveSubview:fromView];
NSTimeInterval interval = stripDelay*(stripCount-i);
[UIView animateWithDuration:stripTime delay:interval options:0 animations:^{
CGPoint center = view.center;
center.y += stripCount*stripHeight;
view.center = center;
} completion:^(BOOL finished) {
NSLog(#"complete");
if (i == stripCount-1)
[context completeTransition:YES];
}];
}
}
I've already checked initial and final position of each strip and already is OK. My interval variable is also properly set at each loop.
But it seems that this is not delayed at all. All strips are moving together, giving the impression that the complete view is moving.
A quick look to basic log shows that all animations are performed at the same time :
2013-09-20 01:11:32.908 test_transition[7451:a0b] complete
2013-09-20 01:11:32.909 test_transition[7451:a0b] complete
2013-09-20 01:11:32.910 test_transition[7451:a0b] complete
2013-09-20 01:11:32.910 test_transition[7451:a0b] complete
2013-09-20 01:11:32.911 test_transition[7451:a0b] complete
2013-09-20 01:11:32.911 test_transition[7451:a0b] complete
2013-09-20 01:11:32.912 test_transition[7451:a0b] complete
2013-09-20 01:11:32.912 test_transition[7451:a0b] complete
2013-09-20 01:11:32.913 test_transition[7451:a0b] complete
2013-09-20 01:11:32.913 test_transition[7451:a0b] complete
Do someone is able to spot what's wrong here ?
EDIT :
It seems this is the following line that cancel the delay of any animations, even if those are not concerning the view being snapshotted :
UIView *view = [toView resizableSnapshotViewFromRect:snapRect afterScreenUpdates:YES withCapInsets:UIEdgeInsetsZero];
If i set the parameter afterScreenUpdates to NO, the view snapshot is null and i get the following error log :
Snapshotting a view that has not been rendered results in an empty snapshot. Ensure your view has been rendered at least once before snapshotting or snapshot after screen updates.
How do i render the view before snapshotting ? I tried [toView setNeedsDisplay] but with no success ...
Here's a solution.
Although this question is 2 years old, it's still a pertinent one as it still exists on iOS9. I realize it miiight not be as much help to the asker seeing it's been 2 years, but I only just came across this. So here's a solution.
When you want to transition between view controllers, you're probably gonna be using an Animator Object to run your custom UIView block animation code. This might be sophisticated with multiple animation blocks and some using a delay value. But if during your transition you want to capture or screenshot a portion of something (whether it's by UIView.drawViewHierarchyInRect, UIView.snapshotViewAfterScreenUpdates, or UIView.resizableSnapshotViewFromRect), using any of these methods will disengage the delays in your animation. For the last 2 methods, if you pass false for afterScreenUpdates, then it won't disengage the delays, but it also won't capture anything; it has to be true to capture something, but setting it to true will disengage your delay.
Using any of these methods will disengage the delay in your animation block, and generally mess things up. If you tell UIKit the transition is gonna be 3 secs and you have an animation block (UIView.animateWithDuration...) that has a 2 sec delay and 1 sec animation, if your delay gets disengaged then the animation runs instantly and the transition lasts just 1 sec, which throws UIKit out of sync and stuff gets generally messed up cuz it was expecting it to be 3 secs.
Here's a solution:
Say you're transition from view controller A to view controller B. In your Animator Object's code file (an object that inherits from NSObject and conforms to UIViewControllerAnimatedTransitioning protocol), you put your animation code in the animateTransition(transitionContext: UIViewControllerContextTransitioning) { ...} delegate method. If you're transitioning from VC A to VC B, say you want to screenshot something from VC B, and show it and do something with it during the transition. One way that works perfectly, is to use the drawViewHierarchyInRect method in View Controller B's viewDidLoad method, to capture and store the image (or create a UIImageView from that image and store the UIImageView) in an instance property of View Controller B. You need to use the drawViewHierarchyInRect method and not any of the other two because those require the content to be visible on screen (i.e. already rendered); the drawViewHiearchyInRect method captures offScreen content, even if it's not added to the view.
As soon as you commence a transition, the 'to view controller' gets initialized and it's viewDidLoad gets called. So in your transition animation code, you can grab the image you screenshotted (or the imageView) by referring to the view controller's property and do whatever you want with it in your transition. Your delays will not be disengaged.
Main Point: Don't screenshot stuff during the transition. Instead, put the screenshot code in the viewDidLoad of the view controller, and store its output in an instance variable and refer to that in your transition.
Hope this helps, I only just came across this problem today and just came across a solution.
After working on your code for a bit, and comparing it to mine, where the delay parameter was honored correctly, I still can't figure out why yours doesn't work. In any case, I found another way that does work. I break the animation into two parts. In the first part, I create the slices of the view using your code, add them to the inView, and also to a mutable array. In the second part, I call the animation block recursively, with no delay, until the last strip is displayed. One limitation to this approach, is that each strip animation has to complete before the next one begins (since the next one is called from the completion block), so you don't have independent control over the duration and delay. Anyway, here is what I did. In the presenting view controller, I just do this:
-(IBAction)presntBlue:(id)sender {
BlueViewController *blue = [self.storyboard instantiateViewControllerWithIdentifier:#"Blue"];
blue.modalTransitionStyle = UIModalPresentationCustom;
blue.transitioningDelegate = self;
[self presentViewController:blue animated:YES completion:nil];
}
-(id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
RDPresentationAnimator *animator = [RDPresentationAnimator new];
animator.isPresenting = YES;
return animator;
}
And in the RDPresentationAnimator class, I have this:
#interface RDPresentationAnimator () {
NSInteger stripCount;
CGFloat stripHeight;
NSMutableArray *stripArray;
}
#end
#implementation RDPresentationAnimator
#define ANIMATION_TIME .3
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return ANIMATION_TIME;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)context {
UIView *inView = [context containerView];
UIView *toView = [context viewControllerForKey:UITransitionContextToViewControllerKey].view;
stripCount = 10;
stripHeight = toView.frame.size.height / stripCount;
stripArray = [NSMutableArray new];
for (NSInteger i = 0; i < stripCount; i++)
{
CGFloat offsetY = i*stripHeight;
CGRect snapRect = CGRectMake(0, offsetY, toView.frame.size.width, stripHeight);
UIView *view = [toView resizableSnapshotViewFromRect:snapRect afterScreenUpdates:YES withCapInsets:UIEdgeInsetsZero];
CGRect stripRect = CGRectMake(0, -(stripCount-i)*stripHeight, snapRect.size.width, snapRect.size.height);
view.frame = stripRect;
[inView addSubview:view];
[stripArray addObject:view];
}
[self animateStrip:stripCount - 1 withContext:context];
}
-(void)animateStrip:(NSInteger) index withContext:(id <UIViewControllerContextTransitioning>) context{
[UIView animateWithDuration:ANIMATION_TIME animations:^{
UIView *view = stripArray[index];
CGPoint center = view.center;
center.y += stripCount*stripHeight;
view.center = center;
} completion:^(BOOL finished) {
if (index >0) {
[self animateStrip:index - 1 withContext:context];
}else{
[context completeTransition:YES];
};
}];
}
I thought I'd add another answer that does give you the independent control over the stripTime and stripDelay. I never did find a way to make it work using the new UIViewControllerContextTransitioning methods. This way uses normal UIView animations, followed by a no animation presentViewController. This method should work correctly in either portrait or landscape orientation (notice that I use self.view.bounds to calculate stripHeight and snapRect, so that those values will be correct for either orientation).
#interface ViewController () {
NSInteger stripCount;
CGFloat stripHeight;
NSMutableArray *stripArray;
}
#end
#implementation ViewController
-(IBAction)presntBlue:(id)sender {
BlueViewController *blue = [self.storyboard instantiateViewControllerWithIdentifier:#"Blue"];
[self animateView:blue];
}
-(void)animateView:(UIViewController *) toVC; {
UIView *toView = toVC.view;
toView.frame = self.view.bounds;
[self.view addSubview:toView];
NSTimeInterval stripDelay = 0.2;
NSTimeInterval stripTime = 1.0;
stripCount = 10;
stripHeight = self.view.bounds.size.height / stripCount;
stripArray = [NSMutableArray new];
for (NSInteger i = 0; i < stripCount; i++) {
CGFloat offsetY = i*stripHeight;
CGRect snapRect = CGRectMake(0, offsetY, self.view.bounds.size.width, stripHeight);
UIView *view = [toView resizableSnapshotViewFromRect:snapRect afterScreenUpdates:YES withCapInsets:UIEdgeInsetsZero];
CGRect stripRect = CGRectMake(0, -(stripCount-i)*stripHeight, snapRect.size.width, snapRect.size.height);
view.frame = stripRect;
[self.view addSubview:view];
[stripArray addObject:view];
}
[toView removeFromSuperview];
for (NSInteger i = 0; i < stripCount; i++) {
NSTimeInterval interval = stripDelay*(stripCount-i);
UIView *view = stripArray[i];
[UIView animateWithDuration:stripTime delay:interval options:0 animations:^{
CGPoint center = view.center;
center.y += stripCount*stripHeight;
view.center = center;
} completion:^(BOOL finished) {
if (i == 0){
[self presentViewController:toVC animated:NO completion:nil];
}
}];
}
}
Added note:
In the animateView: method, I add the toView to self.view,, and then remove it after making the strips. I do this to make sure it works correctly in portrait and landscape -- if I omit those two statements, there's a slight glitch in the landscape animation when the animation finishes. If I have those two lines in, I occasionally get a glitch at the beginning where you can see the whole toView for a brief flash. I don't know why this only happens occasionally, and I haven't updated my phone yet, so I don't know if this happens on the device as well.
Related
I am trying to animate UITabBar items by using a UIImageView and setting its frame in the same frame as my UITabBarItem's when the view first renders to the user. In a different project, it works fine on viewDidAppear, logs showing that the frame is in the same frame as the one in my original project (I have 5 items, and the 5th item is always at 333, 1). On my original project, viewDidAppear will return 0, 0 and returns 333, 1 on viewDidLayoutSubviews. I moved the code to animate on viewDidLayoutSubviews and discovered that it gets called twice, and returns 2 values, 0, 0 on the first call, and 333, 1 on the second call.
Is there a way to start the animation after viewDidLayoutSubviews is finished with the second call?
I have tried moving around the animation code in viewWillAppear, viewDidAppear, and viewDidLayoutSubviews.
viewWillAppear and viewDidAppear resulted in the converted CGPoint as the original CGPoint of the tab bar (0, 617).
viewDidLayoutSubviews resulted in the converted CGPoint as the new CGPoint of the Tab Bar (0, 687) on the first call and starts the animation, and the actual frame after the animation is finished on the second call.
The following code is what I have on my viewDidLayoutSubviews:
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
if(self.fromTabBar) {
NSInteger index = 5;
UIImageView *imageView = [[self.tabBar.subviews lastObject].subviews firstObject];
[self startAnimation:imageView withTag:index];
}
}
The following code is for startAnimation:withTag: :
-(void)startAnimation:(UIImageView *)imageView withTag:(NSInteger)tag {
CGPoint point = [imageView.superview convertPoint:imageView.frame.origin toView:self.view];
UIImageView *hexagon = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"vector-profile"]];
[hexagon setFrame:CGRectMake(point.x, point.y, imageView.frame.size.width, imageView.frame.size.height)];
hexagon.alpha = 0;
[self.view addSubview:hexagon];
[UIView animateWithDuration:0.5 animations:^{
imageView.transform = CGAffineTransformMakeScale(0.45, 0.45);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.5 animations:^{
imageView.alpha = 0;
hexagon.alpha = 1;
} completion:nil];
}];
}
the tag parameter is no longer being used in this method.
I expect the output UIImage to be on top of where the intended UITabBarItem image is (In this case, the 5th position in a UITabBar). This has worked on a different project, but my current project seems to process the rendering differently.
When a view controller's view is first shown I want to run an animation in which all elements in the view controller slide from outside the bottom of the screen to their natural positions. To achieve this, I do subview.frame.origin.y += self.view.frame.size.height in viewDidLayoutSubviews. I also tried viewWillAppear, but it doesn't work at all. Then I animate them up to their natural positions with subview.frame.origin.y -= self.view.frame.size.height in viewDidAppear.
The problem is that viewDidLayoutSubviews is called several times throughout the view controller's lifespan. As such, when things like showing the keyboard happen all my content gets replaced outside the view again.
Is there a better method for doing this? Do I need to add some sort of flag to check whether the animation has already run?
EDIT: here's the code. Here I'm calling prepareAppearance in viewDidLayoutSubviews, which works, but viewDidLayoutSubviews is called multiple times throughout the controller's life span.
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[self prepareAppearance];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self animateAppearance];
}
- (NSArray *)animatableViews
{
return #[self.createAccountButton, self.facebookButton, self.linkedInButton, self.loginButton];
}
- (void)prepareAppearance
{
NSArray * views = [self animatableViews];
NSUInteger count = [views count];
for (NSUInteger it=0 ; it < count ; ++it) {
UIView * view = [views objectAtIndex:it];
CGRect frame = view.frame;
// Move the views outside the screen, to the bottom
frame.origin.y += self.view.frame.size.height;
[view setFrame:frame];
}
}
- (void)animateAppearance
{
NSArray * views = [self animatableViews];
NSUInteger count = [views count];
for (NSUInteger it=0 ; it < count ; ++it) {
__weak UIView * weakView = [views objectAtIndex:it];
CGRect referenceFrame = self.view.frame;
[UIView animateWithDuration:0.4f
delay:0.05f * it
options:UIViewAnimationOptionCurveEaseOut
animations:^{
CGRect frame = weakView.frame;
frame.origin.y -= referenceFrame.size.height;
[weakView setFrame:frame];
}
completion:^(BOOL finished) {
}];
}
}
If you need to animate something when view will appear and then not touch subviews later, I would suggest the following:
Don't change/touch viewDidLayoutSubviews
Add logic to move elements outside the screen (to their initial position before animation) in viewWillAppear
Add logic to animate elements into their proper position in viewDidAppear
UPDATE:
If you're using auto-layout (which is very good thing), you can't animate views by changing their frames directly (because auto-layout would ignore that and change them again). What you need to do is to expose outlets to constraints responsible for Y-position (in your case) and change that constraints rather then setting frames.
Also don't forget to include call to [weakView layoutIfNeeded] after you update constraints in the animation method.
I did both things in viewDidAppear:. It seems that when viewDidAppear: is called, the view is not actually visible, but about to. So the UI elements never show in their initial position if they are replaced there.
I am trying to make a custom expand viewcontroller animation in iOS.
It's a iPad application and has a big square button of size 160*160 in the home screen. I have connected a modal segue to the next viewcontroller scene. What I am trying to achieve is when I tap on the UIButton the destination viewcontroller should expand from the button and shall be animated to full screen with animation completion.
I have subclassed NSObject and created ExpandAnimationController, implemented transitionDuration and animateTransition methods. But the I am stuck at a point to determine the initial frame for the destination viewcontroller's view.
I know for one button I can set frame statically but where in a scenario appears that there are multiple buttons and each button has to a modal segue to a different view controller than how can I pass the buttons frame to the animation controller to set the initial view frame.
I am doing this only for learning purpose after trying raywanderlich's custom view controller transition tutorial. So the problem now is passing a frame object from "Source controller to Animation controller". I've already searched, internet is not much populated with custom transition issues. Any help will be appreciated.
Here is the code I've tried, in ExpandAnimationController,
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
// 1. Fetch all the required objects.
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIView *containerView = [transitionContext containerView];
CGRect finalFrame = toVC.view.frame;
// 2. Set initial frames.
// This is where I am stuck.
CGRect initialFrame = CGRectZero;
toVC.view.frame = initialFrame;
[containerView addSubview:toVC.view];
// 3. Do the animation.
NSTimeInterval duration = [self transitionDuration:transitionContext];
[UIView animateWithDuration:duration animations:^{
fromVC.view.alpha = 0.5;
toVC.view.alpha = 0.5;
toVC.view.frame = finalFrame;
} completion:^(BOOL finished) {
fromVC.view.alpha = 1.0;
toVC.view.alpha = 1.0;
toVC.view.frame = finalFrame;
[transitionContext completeTransition:YES];
}];
}
In the source controller I've adopted UIViewControllerTransitioningDelegate, created a ExpandAnimationController instance and used like below,
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
return _expandAnimationController;
}
But after - prepareForSegue is called I want to pass the sender button frame to the transition animation controller.
Thanks in advance, Happy coding !!
There are many approaches on how you could do this, like an iVar to your transition or transitioning delegate that you could pass in the button rect, but I'm going to suggest a simpler approach (although with a caveat):
First assign a unique Tag to your UIButton either by code, or in Interface Builder.
In this example I'm using the 111 tag:
In your - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext you can get a reference to that button from your ViewController:
CGRect myButtonRect = CGRectZero;
UIButton *myButton = (UIButton*)[fromVC.view viewWithTag:111];
if (myButton) {
myButtonRect = myButton.frame;
NSLog(#"button Frame:%#",NSStringFromCGRect(myButtonRect));
}
What's the caveat?
If you are using the same animation to transition back, the From and To View controllers are going to be reversed.
Also if you use the same Transition on another View Controller and that View Controller has the same tag number for another object, you will get that object and not your Button.
So if you use this approach make sure to use a globally Unique Tag for your entire App, like 32565 (random number here that is big enough so you won't re-use it again)
Now if you have multiple buttons, you cannot use the above approach, so use an iVar to your custom transition:
#interface MyCustomTransition : NSObject <UIViewControllerAnimatedTransitioning>
#property (nonatomic, assign) CGRect initialFrame;
#end
Then in the view controller you are using your custom transition, just pass in the frame:
MyCustomTransition *myTransition = [MyCustomTransition new];
myTransition.initialFrame = myButton.frame;
And finally in your - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext get the iVar value from self.initialFrame
You can convert CGRect to NSValue and put it into sender.
// Send it like this
[self performSegueWithIdentifier:#"someIdentifier" sender:[NSValue valueWithCGRect:yourFrame]];
Also you can retrieve it like this
// Retrieve it
CGRect frame = [sender CGRectValue];
I'd like to implement a custom UIDynamicBehavior that makes a view "burst". To do that I need to fade it out and scale it to 2x its size.
I do this by setting the view's alpha and bounds in the action block. However, how do I know how often the action block is called? The docs say "on each tick", but how many?
I added a counter. With no other animations, the block is called 30 times. With a few gravity and dynamic behaviors, it is called 500 times.
I also don't see how the UIDynamicAnimator knows when its behaviors are "done" moving stuff around. Can anyone shed some light on this?
The code below works sometimes, but other times the behavior stops before the view is animated completely (i.e. it is still visible).
self.action = ^{
static NSInteger count = 0;
NSLog(#"animation tick: %d", count);
count++;
UIView *view = (UIView*)[weakSelf.items lastObject];
view.alpha = view.alpha - 0.1;
CGRect bounds = view.bounds;
bounds.size.width += 1;
bounds.size.height += 1;
view.bounds = bounds;
};
For detecting when the animation has finished you could try this:
__weak Entity *weakSelf = self;
self.behavior.action = ^{
if (weakSelf.center.x == weakSelf.lastPosition.x && weakSelf.center.y == weakSelf.lastPosition.y) {
NSLog(#"end of dynamic movement");
}
weakSelf.lastPosition = weakSelf.center;
};
Or check another value that you are changing.
I want to create a View outside of the visible screen and push in it (like the default pushViewController animation), but I cannot create the UIView outside. I was trying this code here, but it doesn't work. The View gets always created and displayed in the current UIScreen bounds. That means instead of both views, the one to get pushed out and the one to get pushed in, only the view that goes out "moves", the new view just sits at it's place.
In the the .m of the view to show:
- (void)viewDidLoad
{
[super viewDidLoad];
// set the views frame off the screen
self.view.frame = CGRectMake(320, 0, 320, 460);
}
In the method that actually does the transition:
-(void)showOverview:(UIViewController *)sender //sender = mapviewController
{
NSLog(#"overview");
// current view (frame) = mapviewCOntroller.view
CGRect outFrame = self.view.frame;
if (!self.overviewViewController) {
self.overviewViewController = [[UCOverviewViewController alloc] init];
self.overviewViewController.transitionDelegate = self;
// create a new View for the overview
[self.overviewViewController.view setCenter:self.view.center];
[self.view.window addSubview:self.overviewViewController.view];
[self.view.window bringSubviewToFront:self.mapViewController.view];
}
CGRect inFrame = self.overviewViewController.view.frame;
outFrame.origin.x = outFrame.origin.x-outFrame.size.width;
inFrame.origin.x = self.view.frame.origin.x;
[UIView animateWithDuration:0.5f animations: ^{
[self.view setFrame:outFrame];
}];
}
EDIT: this is my final code, at least the important part. Now, the view thats currently visible gets slider off screen at the same time the off-screen view gets slide in.
-(void)showOverview
{
if (!self.overviewViewController) {
NSLog(#"OverviewViewController created!");
self.overviewViewController = [[UCOverviewViewController alloc] init];
self.overviewViewController.transitionDelegate = self;
// add the subView
[self.view addSubview:self.overviewViewController.view];
[self.view sendSubviewToBack:self.overviewViewController.view];
}
CGRect outFrame = self.mapViewController.view.frame;
CGRect inFrame = self.overviewViewController.view.frame;
outFrame.origin.x -= outFrame.size.width;
inFrame.origin.x = self.view.frame.origin.x;
self.isOverviewViewVisible = YES;
[UIView animateWithDuration:0.5f animations: ^{
[self.mapViewController.view setFrame:outFrame];
[self.overviewViewController.view setFrame:inFrame];
}];
}
your problem is this line.
self.overviewView = self.overviewViewController.view;
You're overriding the view you created with the view you already have.
the following is not a great way to do it but it should work with what I think you have.
- (void)viewDidLoad {
self.overviewViewController.view.frame = CGRectMake(320, 0, 320, 460);
self.overviewViewController.view.backgroundColor = [UIColor blueColor];
}
Then you want another action to do the animations.
[UIView animateWithDuration:0.5f animations: ^{
self.overviewViewController.view.frame = CGRectMake(0,0,320,460);
}];
There are weird and incorrect things in your code that make it confusing to understand what is actually going on, but... forget about using the window anywhere. The only thing you need to worry about, is this. Say View A is your main view. View B is the view you want outside and to come in over A. Then, create B either from nib or manually, add it as a subview to view A, and before you enter the animation block to move it into place, make sure that the current frame is set to {ViewA.bounds.size.width, 0, ViewB.bounds.size.width, ViewB.bounds.size.height}. Assuming you want it to come in from the top right.