Should a UIViewController control a UIView's animations? - ios

I've been pushing myself to learn more MVCS styled programming and am attempting to organize my code better. My main question, is I have a UIViewController that brings up a view when an event occurs. When the view is created and when the view is destroyed, I'd like to run some animations on the view appearing and disappearing. I can do this both in the UIView class I have and in the UIViewController. Once these animations have been established, they do not need to be changed. Should I do this within the UIViewController or the UIView to stay MVC compliant?
The code is currently in my UIView as such:
- (IBAction)removeView
{
NSLog(#"Remove");
if (self.completionBlock != nil) {
[UIView animateWithDuration:1.0f delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
self.transform = CGAffineTransformMakeTranslation(self.frame.origin.x, self.frame.origin.y - self.superview.frame.size.height);
self.alpha = 0; // also fade to transparent
}completion:^(BOOL finished)
{
if (finished) {
[self removeFromSuperview];
}
}];
self.completionBlock(YES);
}
}

Based on your description, you can legitimately do them in either place because the animation does not depend on your application's model. If, however, the animation depended on data from your model, then the controller should perform the animation because the controller sees the model data (but the view does not).

The Views of the ViewController should be the ViewController's minons. You're going to have an easier time having the animation maths happen in the ViewController and have the ViewController update the View and this is the best practice according to MVC.
Also, I think it will be easier for you because you can make the logic based on an action as opposed to based on a class. I find that easier.

Related

How to create an animation like in Facebook Pages top menu?

In facebook'a new app, Pages, when you swipe down the menu appears at the top. The animation I am trying to do is the animation when you tap the settings button which is when each cell moves to the left one at a time, but very smoothly. How can I achieve such an animation without using Facebook's pop engine in Objective C ?
Bear in mind that each cell is probably a custom subclass of UIView (i.e. not UITableViewCell or other apple class) so you will have to design your UI carefully.
The animation, however, is easily achievable using the UIView class. Apple's documentation is a good place to start to learn about animation.
What you want is something like:
NSTimeInterval delay = 0.2;
for (UIView *myView in myViewArray) {
[UIView animateWithDuration:0.5 delay:delay options:UIViewAnimationOptionCurveEaseInOut animations:^{
/*
* This is where you set the properties you want to animate.
* If you're using AutoLayout (i.e. NSLayoutConstraint) you need to set the constant property of the relevant constraint, not the view's frame property.
*/
} completion:nil];
delay += 0.2;
}
This will provide you with animations that occur one after the other (0.2 seconds apart).

How to bounce child view controller's UICollectionView when the child view controller is presented?

I want to present a child view controller by dropping it from top to the bottom. The child view controller is a UICollectionViewController with several cells in it. I can use the iOS7 UIViewControllerContextTransitioning for the dropping down view controller transition. But if I want only the collection view to bounce (like a ball hit on the ground) when the child view controller is presented, how should I do?
I have try to use UIKit Dynamics and create some UIAnimatorBehavior on the UICollectionView after the transition, like UIGravityBehavior and UIPushBehavior. But they don't seem to work. Maybe I am using them in the wrong way. Is there anyone can give me some hints?
Update
After tried several solutions, I finally came out a solution which is pretty close to what I want. This video shows the result: http://youtu.be/tueXDBMsdt0
But I think there should be a better solution for that. And here is my solution's steps:
Create a UIViewControllerAnimatedTransitioning object, which animate the view controller transition from top to bottom.
The child view controller is a UICollectionViewController. At the end of transition animation, I set child view controller's scrollview content offset to (0, -30), and then complete the transition.
In child view controller's viewDidAppear, animate the content offset back to (0, 0).
Besides, I also follow the instructions in the article: http://www.teehanlax.com/blog/implementing-a-bouncy-uicollectionviewlayout-with-uikit-dynamics/ to set UIKit dynamics animator in cells. When the content offset is changed, then the cells will look like bouncing.
The transition animation code looks like this:
- (void) animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
CGRect frame = [[transitionContext containerView] frame];
CGRect startFrame = frame;
startFrame.origin.y -= CGRectGetHeight(transitionContext.containerView.frame);
[transitionContext.containerView addSubview:fromViewController.view];
[transitionContext.containerView addSubview:toViewController.view];
toViewController.view.frame = startFrame;
[UIView animateWithDuration:0.4 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
toViewController.view.frame = frame;
}
completion:^(BOOL finished) {
((UICollectionViewController*)toViewController).contentOffset = CGPointMake(0, -30);
[transitionContext completeTransition:YES];
}];
}
And in child view controller viewDidAppear:
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.collectionView setContentOffset:CGPointMake(0, 0) animated:YES];
}
But I would still want the cell to bounce more naturally. Any other better solutions?
Important: this can now be done in iOS in one simple line of code:
https://stackoverflow.com/a/23514653/294884
Detailed answer when fuller control is needed:
If you want the same effect between the Screen lock and the Camera on the iPhone, you can use UIViewControllerContextTransitioning
There are a good tutorial here http://www.objc.io/issue-5/view-controller-transitions.html
and here http://www.teehanlax.com/blog/custom-uiviewcontroller-transitions/
if you have a Apple developer account, there are a video about View controller transition : https://developer.apple.com/tech-talks/videos/
The video is named "Architecting Modern Apps, Part 1"
This way work only on iOS7!

Storyboard to design off screen subview

I want to use Storyboards to design content for a slider, and it seems like an easy way to design offscreen content is to use a childViewController. So I've done this
myViewController = [[UIStoryboard storyboardWithName:#"ipad" bundle:NULL] instantiateViewControllerWithIdentifier:#"keyPadOffScreen"];
[self addChildViewController:myViewController];
[myViewController didMoveToParentViewController:self];
newView = myViewController.view;
[self.view addSubview:newView];
And that adds the entire view controller over top of my root view. The problem is, I only want one of the subviews to show up, not the whole view. I can handle the animation, as long as I know how to add the root view. I tried this to just add the subview (sliderView is the name of the subview I want) instead of the whole view, but that did nothing
newView = myViewController.sliderView;
[self.view addSubview:newView];
Should I be using a different strategy?
EDIT: this DOES work, but it seems silly - setting the views size to just be the size of the subview.
newView.frame = CGRectMake(newView.frame.origin.x, newView.frame.origin.y, newView.frame.size.width, **myViewController.sliderView.frame.size.height**);
It does seem a bit overkill for just a view. Once you start doing a lot of custom view/animation/transition stuff it's often easier to implement in code, or at least it is for me since I've been doing it that way for a long time.
But maybe you want to stick with Storyboards. I respect that. And if you have a few developers working on this then it's important to keep some uniformity to how you set up your UI.
Instead of keeping it in a separate view controller and adding it when you need it to animate on-screen, simply add it to your existing view controller and either set it to hidden, or set it's alpha to 0.0 in IB. Then your animation can undo that and make it visible.
you can use custom segue here, for instance:
#implementation FRPresentEnteringPopupSegue
- (void)perform
{
FirstVC *toVC = self.destinationViewController;
SecondNavigationController *fromVC = self.sourceViewController;
toVC.view.frame = CGRectMake(0.0, 0.0, 300.0, 135.0);
toVC.view.center = CGPointMake(fromVC.view.bounds.size.width/2, fromVC.view.bounds.size.height + toVC.view.bounds.size.height/2);
[fromVC.view addSubview:toVC.view];
[toVC viewWillAppear:YES];
[UIView animateWithDuration:0.5
delay:0.0
usingSpringWithDamping:0.7
initialSpringVelocity:0.5
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
toVC.view.center = CGPointMake(fromVC.view.bounds.size.width/2, fromVC.view.bounds.size.height/2);
}completion:^(BOOL finished) {
[toVC viewDidAppear:YES];
}];
}
#end
make your UIStoryboardSegue subclass
override - (void)perform method with your custom view appearance code
use segue usual way

Animate exit of UIView

I want to make a UIView animate when it's being closed. I tried reading the following:
http://felipe.sabino.me/ios/2012/05/10/ios-uiview-transition-effects/
iPhone UIView Animation Best Practice
iOS UIView Animation CATransform3DMakeRotation confusion
However, I'd like to make it transition from the side of the screen, as per the image in the Google Chrome app.
Is there another animation that is set for this? I was not able to find it... I'm assuming it has to do with animateWithDuration or a CATransform...can somebody point me in the right direction for this?
[EDIT]
I used the below post for an answer as well as this post:
Setting a rotation transformation to a UIView or its layer doesn't seem to work?
I was able to add multiple animations as per below:
[UIView animateWithDuration: .2
delay: 0
options: (UIViewAnimationOptionCurveLinear | UIViewAnimationOptionAllowUserInteraction)
animations:^{self.view.center = CGPointMake(self.view.frame.origin.x * 3, self.view.frame.origin.y * 2), self.view.transform = CGAffineTransformMakeRotation(M_PI_4/2);}
completion:nil];
Previously I was not aware you can add multiple animations so easily. That adds rotation as well as the linear movement together.
Animate your view so it moves offscreen/shrinks/expands/fades, then do the actual removal when the animation ends.
You can do this by altering the properties of the view (position/size/offset) between a beginAnimations/commitAnimations block. UIKit will then animate these properties over the time specified.
E.g something like;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.30f];
view.transform =
CGAffineTransformMakeTranslation(
view.frame.origin.x,
480.0f + (view.frame.size.height/2) // move the whole view offscreen
);
background.alpha = 0; // also fade to transparent
[UIView commitAnimations];
In the animation end notification you can then remove the view.
I've never run Chrome on iOS, so I have to try to guess what your screenshot is showing.
Does the animation go off the screen while shrinking and turning to one side?
And do you mean you want to animate a UIViewController, or a UIView? Are you closing a view controller?
If it's a view controller, how are you managing your view controllers? Are you using a navigation controller, or are you presenting a set of modal view controllers, or some other method?

Leveled UITableView without NavigationController

I have data structured as a tree (and showed in UITableView) and I would like to show it the way it is usually done using UINavigationController and pushing child views on to the stack. The problem is that my UITableView (in an iPad app) takes only 1/6 of the screen (there is a UINavigationController that handles other fullscreen iPad screens in this app, so using NavigationController to control the levels of table is the good way).
Is there a simple way to get a good visual effect of changing the levels of TableView without using NavigationController? Right now I just change the data source and reload data, but that does something like a flicker on the screen (the user can't really see, that the structure of the data is tree-like).
I thought of creating a few TableViews and then just animating the resize (from full width to 0 the TableView that we are leaving, and from 0 to width at the same time of the another one) of a table just to make something like segue effect - but I am not sure if this is a good approach.
You can use reloadSections:withRowAnimation: with UITableViewRowAnimationLeft and UITableViewRowAnimationRight instead of a default reloadData. This will look like the push you are looking for.
Reference: http://developer.apple.com/library/ios/documentation/uikit/reference/UITableView_Class/Reference/Reference.html#//apple_ref/occ/instm/UITableView/reloadSections:withRowAnimation:
You can try this code (after you set the proper values:))
__block CGRect frame = tableView.frame;
[UIView animateWithDuration:0.5 animations:^{
frame.origin.x -=tableView.frame.size.width;
} completion:^(BOOL finished) {
frame.origin.x = yourViewThatContainsTheTable.frame.size.width
tableView.frame = frame;
[tableView reloadData];
[UIView animateWithDuration:0.3 animations:^{
frame.origin.x = 0;
tableView.frame = frame;
}];
}];
Basically if you are using subclasses of UIView you can use [UIView animateWith...] methods to perform simple animations or you can use CAAnimations/CABasicAnimation/CAAnimationGroup/CAKeyframeAnimation for more complex animations on subclasses of UIView.
If you want a custom transition for a UIViewController you can use CATransition.

Resources