Animation in transitionFromViewController:toViewController: not showing - ios

I am trying to build a custom container view controller and am trying to create my own animation. I'm starting with something simple of moving the tapped cell to the center of the screen before making it zoom in by scaling and showing the new view. I'm starting with this:
- (void)showDetailViewController:(UIView *)viewToAnimate {
UIImageView *anImageView = [[UIImageView alloc] initWithImage:[self screenShot:_homeViewController.view]];
CGRect windowBounds = [[UIScreen mainScreen] bounds];
_detailViewController.view.frame = windowBounds;
[self addChildViewController:_detailViewController];
// get a screenshot of the old view before pushing the new view controller
_detailViewController.view.alpha = 0.0;
NSLog(#"center: %#", NSStringFromCGPoint(viewToAnimate.center));
[self transitionFromViewController:_homeViewController toViewController:_detailViewController duration:0.5 options:0 animations:^{
CGPoint windowCenter = CGPointMake(CGRectGetMidX(windowBounds), CGRectGetMidY(windowBounds));
viewToAnimate.center = windowCenter;
viewToAnimate.layer.transform = CATransform3DMakeScale(2, 2, 2);
NSLog(#"center 2 : %#", NSStringFromCGPoint(viewToAnimate.center));
_detailViewController.view.alpha = 1.0;
}completion:^(BOOL finished) {
[anImageView removeFromSuperview];
}];
[_detailViewController didMoveToParentViewController:self];
}
What I don't understand is, if I comment out the _detailViewController.view.alpha = 0 and _detailViewController.view.alpha = 1, I don't get any animation. It just jumps to the new childViewController. But with those two lines, I do see an animation. Why is that? Thanks.
Edit:
This is how I set up a container controller:
In viewDidLoad of ContainerViewController:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPad" bundle:nil];
_homeViewController = [storyboard instantiateViewControllerWithIdentifier:#"HomeViewController"];
_homeViewController.homeViewControllerDelegate = self;
_detailViewController = [storyboard instantiateViewControllerWithIdentifier:#"DetailViewController"];
[self setSubViewControllers:#[ _homeViewController, _detailViewController]];
_selectedViewController.view.frame = [[UIScreen mainScreen] bounds];
[self addChildViewController:self.selectedViewController];
[self.view addSubview:_selectedViewController.view];
[self didMoveToParentViewController:self];

In the code you posted, you show yourself adding _detailViewController as a child view controller, but don't show the step of adding _homeViewController as a child view controller of self. At the point when the code you posted is executed, is _homeViewController a child of self (the view controller that is running the transition)? It needs to be.

Related

How to remove the from container view without destroying a complex layout?

I have a scene which is built as on the attached screen
Scene
Depending on the selection of a button from the top bar, I'm loading a specific view controller inside a container view. When selection is changed or the user is tapping the 'Back' button at controller then should be removed an intrinsic content of the container.
Code for adding into container view:
if (self.containerVC != nil) {
[self removeController];
}
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
FirstViewController* firstVC = [storyboard instantiateViewControllerWithIdentifier:#"First"];
[self addChildViewController:firstVC];
[self.containerVieww addSubview:firstVC.view];
firstVC.view.frame = CGRectMake(0, 0, self.containerVieww.bounds.size.width, self.containerVieww.bounds.size.height);
[firstVC.view layoutSubviews];
[firstVC didMoveToParentViewController:self];
firstVC.modalPresentationStyle = UIModalPresentationCurrentContext;
[UIView animateWithDuration:0.5 animations:^{
self.logViewHeight.constant = self.contentView.frame.size.height * 0.9;
[firstVC.view layoutSubviews];
}];
firstVC.delegate = self;
self.containerVC = firstVC;
Code for removing from container view:
[self.containerVC dismissViewControllerAnimated:YES completion:nil];
[UIView animateWithDuration:0.5 animations:^{
self.logViewHeight.constant = 0;
[self.view layoutIfNeeded];
}];
[self willMoveToParentViewController:nil];
[self removeFromParentViewController];
[self.containerVC.view removeFromSuperview]; // Caused layout destoying !!!
self.containerVC = nil;
After calling 'removeFromSuperview' is disappeared a 'Blue View' in my layout.
How to remove from the container view without destroying a complex layout?

Load View Controller from storyboard with a particular size

I want to know if it is possible to load/present modally a UIViewController with a "free form" size like the image below
I want to load the red one allways on top (allways visible) and interact with the ViewController below (blue one) at the same time.
EDIT: what i want is to show and hide FROM APP DELEGATE a small view controller to controll the music in my app.
I was trying with a xib file with a custom UIViewController as the file owner (see image below). The xib is being showed right the way i want, but the UIViewController associated to it, id doesn't load and i don't know why...
So i thought to do it with a UIViewController free formed but im not making it and i don't know wich is the best way
EDIT2:
With the xib file way i did the next:
In AppDelegate i have a function showPlayer that is called with local notification to show the xib
-(void)showPlayer:(int)tag andObjetoCancion:(NSArray *)objetoCancion
{
ZocaloPlayerViewController *vc = [[ZocaloPlayerViewController alloc] initWithNibName:#"ZocaloPlayerView" bundle:nil];
[vc setObjectoCancion:[objetoCancion mutableCopy]];
if (globals.isPlaying) {
[vc setCambiar:YES];
}else{
[vc setCambiar:NO];
}
//here playerView is a UIView initiated in didFinishLaunchingWithOptions
if (playerView == nil) {
playerView = [[UIView alloc] initWithFrame:CGRectMake(0, self.window.frame.size.height, self.window.frame.size.width, altoPlayer)];
}
//keyWindow is UIWindow
//NSLog(#"Abro view %ld", (long)viewPlayer.tag);
keyWindow = [[[UIApplication sharedApplication] delegate] window];
[keyWindow addSubview:playerView];
[keyWindow bringSubviewToFront:playerView];
NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:#"ZocaloPlayerView" owner:vc options:nil];
mainView = [subviewArray objectAtIndex:0];
mainView.translatesAutoresizingMaskIntoConstraints = NO; //This part hung me up
[playerView addSubview:mainView];
//needed to make smaller for iPhone 4 dev here, so >=200 instead of 748
[playerView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|-0-[mainView]-0-|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:NSDictionaryOfVariableBindings(mainView)]];
[playerView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|-0-[mainView]-0-|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:NSDictionaryOfVariableBindings(mainView)]];
[UIView animateWithDuration:0.3 animations:^{
playerView.center = CGPointMake(self.window.frame.size.width / 2, self.window.frame.size.height - mitadAltoPlayer);
[self.window layoutIfNeeded];
}];
}
ZocaloPlayerViewController *vc is being instantiated but the viewDidLoad function inside it is not being called.... That is my first problem...
With the UIViewController free formed way i don't know how to do it...
Ok, i solved my problem with this post Loaded nib but the view outlet was not set - new to InterfaceBuilder
(i was missing the UIView outlet)
and i changed my code in the showPlayer function to this:
-(void)mostrarPlayer:(int)tag andObjetoCancion:(NSArray *)objetoCancion
{
ZocaloPlayerViewController *vc = [[ZocaloPlayerViewController alloc] initWithNibName:#"ZocaloPlayerView" bundle:nil];
[vc setObjectoCancion:[objetoCancion mutableCopy]];
if (globals.isPlaying) {
[vc setCambiar:YES];
}else{
[vc setCambiar:NO];
}
if (playerView == nil) {
playerView = [[UIView alloc] initWithFrame:CGRectMake(0, self.window.frame.size.height, self.window.frame.size.width, altoPlayer)];
}
//NSLog(#"Abro view %ld", (long)viewPlayer.tag);
keyWindow = [[[UIApplication sharedApplication] delegate] window];
[keyWindow addSubview:playerView];
[keyWindow bringSubviewToFront:playerView];
[playerView addSubview:vc.view];
[UIView animateWithDuration:0.3 animations:^{
playerView.center = CGPointMake(self.window.frame.size.width / 2, self.window.frame.size.height - mitadAltoPlayer);
[self.window layoutIfNeeded];
}];
}

Tapping on cell in childviewcontroller accessing tableview delegate of parent's parent view controller in a nested environment

I am in a weird scenario. I have created a nestedViewController in which clicking on a row in tableview launches a another nestedviewcontroller just below the header of the parent nestedview's section header by adjusting frames. Now, To add a different functionality to second nested view , I added a UIViewController (which has custom cells in a tableview) as childviewcontroller to it. Now , when I click on the cell of the viewController , somehow the TableView Delegate of first NestedViewController is getting called!!! However, the view that is present is the UIViewController that I added as childViewController to second NestedViewController.
The way I added UIViewController to Second NestedViewController is :
DRProductWriteRatingViewController *writeRatingVC = [[DRProductWriteRatingViewController alloc] initWithNibName:#"DRProductWriteRatingViewController" bundle:nil];
writeRatingVC.productDict = [productDict mutableCopy];
newVC = [[DRRatingNestedViewController alloc] initWithFrame:CGRectMake(0,0,320,[UIScreen mainScreen].applicationFrame.size.height- COLLAPSED_HEADER_HEIGHT*self.depth) andEntity:childEntity andListing:_listing];
newVC.category = self.category;
// self.depth = self.depth+1;
dispatch_async(dispatch_get_main_queue(), ^{
writeRatingVC.tableView.frame = CGRectMake(0,0,320,[UIScreen mainScreen].applicationFrame.size.height- COLLAPSED_HEADER_HEIGHT*self.depth-40);
[newVC.tableView removeFromSuperview];// removing tableview of nestedVC as it is irrelevant
[newVC addChildViewController:writeRatingVC];
[newVC.view addSubview:writeRatingVC.view];
[writeRatingVC didMoveToParentViewController:newVC];
});
Also the way I push second NestedViewController Over First one goes like this:
- (void)pushViewController:(UIViewController *)viewController {
//1. The current controller is going to be removed
// [self willMoveToParentViewController:nil];
[viewController willMoveToParentViewController:self];
//2. The new controller is a new child of the container
[self addChildViewController:viewController];
//3. Setup the new controller's frame depending on the animation you want to obtain
CGRect containerRect = CGRectMake(0, self.view.bounds.size.height ,
320.,
self.view.bounds.size.height - COLLAPSED_HEADER_HEIGHT*(self.depth));
viewController.view.frame = containerRect;
[self.view addSubview:viewController.view];
// viewController.view.alpha = 0;
//assume that the new view controller is positioned at the bottom of the screen
viewController.view.transform = CGAffineTransformIdentity;
// CGFloat travelDistance = self.view.bounds.size.height - COLLAPSED_HEADER_HEIGHT - NAVIGATION_BAR_HEIGHT;
CGFloat travelDistance = self.view.bounds.size.height - COLLAPSED_HEADER_HEIGHT - NAVIGATION_BAR_HEIGHT;
if(self.depth>1) {
travelDistance += NAVIGATION_BAR_HEIGHT;
}
travelDistance += NAVIGATION_BAR_HEIGHT;
if (self.depth >= 2) {
travelDistance -=NAVIGATION_BAR_HEIGHT;
}
if (self.depth == 5 ) {
travelDistance -=NAVIGATION_BAR_HEIGHT;
}
CGAffineTransform travel = CGAffineTransformMakeTranslation ( 0, -travelDistance);
[UIView animateWithDuration:1 delay:0 usingSpringWithDamping:kDamping initialSpringVelocity:kInitialSpringVelocity options:0x00 animations:^{
viewController.view.transform = travel;
viewController.view.alpha = 1;
} completion:^(BOOL finished) {
// self.view.transform = CGAffineTransformIdentity;
[viewController didMoveToParentViewController:self];
//mayanktest [self.tableView reloadData];
}];
}
The Depth here only decides the number of NestedVCs added.
First Nested VC: DRRatingNestedViewController: 0xd3a9520
Second Nested VC: DRRatingNestedViewController: 0xd0b4f50
Child View to Second Nested VC : DRProductWriteRatingViewController: 0xd0c15b0
So the tableViewDelegate with object 0xd3a9520 is called , which is my problem.
What should be the issue here?
The issue was with the childviewcontroller frame. Since the frame was smaller it was able to access the view behind. Hence,
Modifications had to be done in this:
CGRect containerRect = CGRectMake(0, self.view.bounds.size.height ,
320.,
self.view.bounds.size.height - COLLAPSED_HEADER_HEIGHT*(self.depth));
viewController.view.frame = containerRect;
[self.view addSubview:viewController.view];

Custom Animations with UIViewController

I'm using a custom animation to present my view controllers. Here's the code:
-(void)launchCustomModal:(id)sender
{
UIButton *buttonClicked = (UIButton *)sender;
int selection;
selection = buttonClicked.tag;
[ticker removeFromSuperview];
ticker = nil;
if (selection == 3)
{
MyViewController *myVC = [[MyViewController alloc]initWithNibName:nil bundle:nil];
modalViewController= [[UINavigationController alloc]initWithRootViewController:myVC];
modalViewController.navigationBarHidden = YES;
[modalViewController setToolbarHidden:YES];
CGRect result = self.view.bounds;
result.origin.y = -result.size.height;
modalViewController.view.frame=result;
[self.view addSubview:modalViewController.view];
[UIView animateWithDuration:.375
animations:^{
modalViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
}
completion:^(BOOL finished) {
NSLog(#"Finished");
}];
}
return;
}
I've noticed this method makes for a very laggy transition. If I launch the VC in a normal modal, it works quite smoothly. Also, if I animate just a view independent of a view controller, it also works perfectly smoothly. I'm wondering if there is something about the VC that might be causing it to animate so poorly? If its a symptom of something I'm doing or if view controllers are just not meant to be handled this way, etc. Any input is greatly appreciated. Thanks!
It was the CALayer shadows. removed them and it worked fine.

wrong orientation on UIViewController while animating it

In order to use a UISplitViewController, I'm replacing my window root controller when navigating from one view controller to the other.
In order to have some nice transition while doing so, I'm using a zooming effect like this:
MyOtherViewController *controller = [[MyOtherViewController alloc] initWithNibName:#"MyOtherView" bundle:nil];
UIWindow *window = ((MyAppDelegate *)[[UIApplication sharedApplication] delegate]).window;
controller.view.frame = [window frame];
controller.view.transform = CGAffineTransformMakeScale(0.01,0.01);
controller.view.alpha = 0;
[window addSubview:controller.view];
[UIView animateWithDuration:0.2 animations:^{
controller.view.transform = CGAffineTransformMakeScale(1,1);
controller.view.alpha = 1.0;
} completion:^(BOOL finished) {
if (finished) {
[self.view removeFromSuperview];
window.rootViewController = controller;
}
}];
and this works pretty well, except that while doing the animation, the new view is always oriented as if in portrait mode, regardless of the current device orientation. When the animation is finished, the view orients itself correctly.
What am I missing?
Things I've tried:
putting my new controller view as the sole subview of the UIWindow
making my new controller the root view controller before the animation begins
A curious thing is that, if I do a recursiveDescription on the window at the beginning of my method, the window frame is defined as having a size of 768x1024 (i.e., portrait), and the view inside it of 748x1024 but with a transform of [0, -1, 1, 0, 0, 0] (does this mean a rotation or what? Shouldn't it be the identity transform?)
UIWindow doesn't rotate. It has a rotated view inside of it (as you've seen). In this case, though, I think the problem is likely that your view has a transform on it already at this point, and you need to concatenate with it rather than replace it as you're doing in your setTransform: calls.
You shouldn't be asking the app delegate for the window, you should be getting the window from the view (self.view.window).
If at any point you're attaching your view to the window itself, rather than putting it inside the rotation view, then you'll need to know the effective transform of the view you want to match by walking the hierarchy:
- (CGAffineTransform)effectiveTransform {
CGAffineTransform transform = [self transform];
UIView *view = [self superview];
while (view) {
transform = CGAffineTransformConcat(transform, [view transform]);
view = [view superview];
}
return transform;
}
I finally figured out what was wrong. Since the frame is not a real property but a sort of calculated one, based on the values of the view bounds and the view transform, I needed to set the frame after setting the same transform as the current view, and before setting the transform again to set up the initial state of the animation. Also, the frame I need to set is the same one as the current view is currently using, as it is taking into account the window orientation (or lack thereof, as Rob Napier pointed)
So, without further ado, here's the working code:
MyOtherViewController *controller = [[MyOtherViewController alloc] initWithNibName:#"MyOtherView" bundle:nil];
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
CGAffineTransform t = self.view.transform;
controller.view.transform = t;
controller.view.frame = self.view.frame;
controller.view.transform = CGAffineTransformScale(t,.01,.01);;
[window addSubview:controller.view];
controller.view.alpha = 0;
[UIView animateWithDuration:0.2 animations:^{
controller.view.transform = t;
controller.view.alpha = 1.0;
} completion:^(BOOL finished) {
if (finished) {
[self.view removeFromSuperview];
window.rootViewController = controller;
[controller release];
}
}];

Resources