Table View Animations Tutorial: Drop-In Cards

The standard UITableView is a powerful and flexible way to present data in your apps; chances are that most apps you write will use UITableView in some form. However, one downside is that without some level of customization, your apps will look bland and blend in with the thousands of apps just like it.

To prevent boring table views, you can add some subtle animation to liven up the actions of your app. You may have seen this in the popular app Google Plus, where the cards fly in through the side with a cool animation. If you haven’t seen it yet, download it now to check it out (it’s free)!

In this table view animations tutorial, you’ll be enhancing an existing app to rotate the cells of a table as you scroll through. Along the way, you’ll learn about how transforms are achieved in UIKit, and how to use them in a subtle manner so as not to overwhelm your user with too much happening on-screen at once.

This tutorial assumes you know how to work with a UITableView. If you’re not really familiar with table views, you might want to start with a tutorial such as How to Create a Simple iPhone App that will teach you the basics of a TableView app.

Note from Ray: Full credit for this technique goes to  Orta Therox, the newest member of our new “Code Team” at raywenderlich.com. Orta created the original sample project which Brian then polished and converted into this tutorial.

The Code Team is a group of expert-level coders interested in coming up with particularly cool demos demonstrating advanced techniques. The Tutorial Team then converts the best demos into high quality tutorials. If you are an expert level iOS developer and are interested in joining the Code Team, contact me!

Getting Started

Download the starter project and open it up in Xcode. You’ll find a simple storyboard project with aUITableViewController subclass (CTMainViewController) and a custom UITableViewCell (CTCardCell). The cell has all the properties that you need to set the data for each individual.

Build and run the project in the simulator; you’ll see the following:

A perfectly good design, ready to be spiced up.

The app is off to a good start, but it could use a little more flair. That’s your job; you’ll use some Core Animation tricks to animate your cell.

Defining Your Transformation

You’ll add a rotation effect to your cards to make your app feel a little more dynamic. Core Animation can be used with all elements of UIKit, and some incredibly intricate things can be built using this framework. Although it’s incredibly powerful, there are some portions of it that are tremendously simple to implement.

To get your cards rotating, you’ll apply a transformation to the cell as it is displayed, and then animate it so that it returns to its normal position. Since this transformation will be applied to each row, there’s no sense regenerating the animation for every cell. Instead, you’ll store the animation in a property so that you can reuse it.

Open CTMainViewController.m and add the following property to the @interface block:

@property (assign, nonatomic) CATransform3D initialTransformation;

The code above sets up the property to store your animation.

Next, add the following code to viewDidLoad, immediately after changing the backgroundView:

CGFloat rotationAngleDegrees = -15;
CGFloat rotationAngleRadians = rotationAngleDegrees * (M_PI/180);
CGPoint offsetPositioning = CGPointMake(-20, -20);
 
CATransform3D transform = CATransform3DIdentity;
transform = CATransform3DRotate(transform, rotationAngleRadians, 0.0, 0.0, 1.0);
transform = CATransform3DTranslate(transform, offsetPositioning.x, offsetPositioning.y, 0.0);
_initialTransformation = transform;


The code above first sets up a few CGFloat and CGPoint constants to be used in the transformations. Next, it applies a series of simple transformations and translations to build up the effect, as follows:

  • Start with an identity transform, which is a fancy math term for “do nothing.”
  • Call CATransform3DRotate to apply a rotation of -15 degrees (in radians), where the negative value indicates a clockwise rotation.
  • Apply the rotation around the axis 0.0, 0.0, 1.0; this represents the z-axis, where x=0, y=0, and z=1.
  • Applying just the rotation to the card isn’t enough, as this simply rotates the card about its center. To make it look like it’s tipped over on a corner, add a translation or shift where the negative values indicate a shift up and to the left.

Note: The transformation ultimately is a complicated matrix. If you studied matrix math in school, you may recognize this as multiplication of matricies. Each step multiplies a new transformation until you end up with the final matrix.

Building up transformations to produce the desired effect.

The transformation is now stored in a property; to see it in action, you need to apply it to your cards.

Applying Your Transformation

First, you’ll just apply the initial transformation to the cells to tip them on their side. You’ll worry about the reverse transformation — returning the cells to their original position — in just a bit.

Add the following line to import section at the top of CTMainViewController.m:

#import <QuartzCore/QuartzCore.h>


Next, add the following method to the implementation section of CTMainViewController.m:

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    UIView *card = [(CTCardCell* )cell mainView];
 
    card.layer.transform = self.initialTransformation;
    card.layer.opacity = 0.8;        
}


TableView calls tableView:willDisplayCell:forRowAtIndexPath on its delegate just before each cell is displayed. The above method grabs a reference to the subview named mainView defined in CTCardCell. Thelayer property of the UIView elements hold a reference to the Core Animation layer for the view; in turn, layers have a transform property.

By default, transform is set to the identity transform, but here you override this and set the transform to the one you just stored in the property. Finally you set the initial opacity of the layer to 0.8 to look slightly transparent; as the animation progresses you’ll fade the card in to an opacity of 1.0 — which you’ll take care of a little later.

You’ll notice that you’re transforming a child view of the cell, and not the cell itself. Rotating the actual cell would cause part of it to cover the cell above and below it, which would cause some odd visual effects, such as flickering and clipping of the cell. This is why the cell has a large view (mainView) containing all the other items.

Before you build the project, you’ll need to link the framework into the project to see your hard work in action.

Click the CardTilt target in the navigator section on the left of the XCode window. Click Build Phases, then expand the little triangle next to ‘Link Binary with Libraries’. Click the small ‘+’ at the bottom of that section, and add QuartzCore.framework to the build.

Since you don’t want to leave the cells with this crooked alignment, go ahead with the next section before you run the project.

Things Fall Into Place

Core Animation provides a great mechanism to create simple animations in your applications. All you have to do is provide a starting point, as you did above with your initial transform, and an ending point, which you’ll do below, and Core Animation figures out the steps along the way.

To define the end point for your animation, add the following code totableView:willDisplayCell:forRowAtIndexPath in CTMainViewController.m file just after the point where you set the initial layer properties:

[UIView animateWithDuration:0.4 animations:^{
        card.layer.transform = CATransform3DIdentity;
        card.layer.opacity = 1;
    }];


In the code above, the animateWithDuration parameter represents the length of the transition in seconds. To make the animation run faster or slower, simply change this value.

When you’re debugging animations, it’s helpful to make your animation really long, like four or five seconds; this way you can catch little things that escape your notice when the animation runs at normal speed, such as clipping issues with the cell frame — or perhaps to grab a screenshot when writing a tutorial! :]

Next, you apply the CATransform3DIdentity transformation to bring your cell back to its original position. Finally, you animate the opacity of the cell back to 1.0; this will provide the “fade-in” effect you started earlier when you set the initial opacity to 0.8.

Note: Core Animation provides a block syntax where you declare the final condition of your animation sequence and how long it should take. The framework figures out the details for you. If you are new to blocks, see How To Use Blocks in iOS 5 (part 1 and part 2).

If you’re interested in the “why” and “how” of block syntax, Nils Hayat wrote a really great article which explains how to get from C declarators to Objective-C block syntax.

Build and run your application; scroll through the list and the cells of your table now rotate into place as they appear on the screen.

Not all properties support animation; the Core Animation Programming Guide provides a list of animatable properties for your reference.

Adding Some Limits to Your Transformation

Although the animation effect is neat, you’ll want to use it sparingly. If you’ve ever suffered through a presentation that overused sound effects or animation effects, then you know what effect overload feels like!

In your project, you only want the animation to run the first time the cell appears — as it scrolls in from the bottom. When you scroll back toward the top of the table, the cells should scroll without animating.

You need a way to keep track of which cards have already been displayed so they won’t be animated again. To do this, you’ll use a collection called a set.

Note: A set is an unordered collection of unique entries with no duplicates, while an array is an ordered collection that does allow duplicates. The Foundation collection classes NSSet and NSArray handle these two collections for you.

In your case, you just need a collection to hold the cells that have already been displayed; using an array leads to a more complicated implementation as you’d need to look up every card to see it it was already in the array, or you’d need to insert cards multiple times as the user scrolled up and down the table.

The general disadvantage of a set is that it doesn’t guarantee an order, but the ordering of your cells is already handled by the table datasource, so it isn’t an issue in this case.

Add the following code to the interface definition section of CTMainViewController.m:

@property (nonatomic, strong) NSMutableSet *shownIndexes;


Next, add the following code to viewDidLoad in CTMainViewController.m:

_shownIndexes = [NSMutableSet set];


Finally, modify tableView:willDisplayCell:forRowAtIndexPath: in CTMainViewController.m as follows:

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (![self.shownIndexes containsObject:indexPath]) {
        [self.shownIndexes addObject:indexPath];
 
        UIView *card = [(CTCardCell* )cell mainView];
 
        card.layer.transform = self.initialTransformation;
        card.layer.opacity = 0.8;
 
        [UIView animateWithDuration:0.4 animations:^{
            card.layer.transform = CATransform3DIdentity;
            card.layer.opacity = 1;
        }];        
    }
}


In the code above, instead of animating every cell each time it appears as you scroll up and down the table, you check to see if indexPath is contained in the set you defined earlier. If the indexPath is not in the set, this is the first time the cell has been displayed; therefore you transform the initial position, run the animation, then add the indexPath to the set. If it was already in the set, then you don’t need to do anything at all.

Build and run your project; scroll up and down the tableview and you’ll only see the cards animate the first time they appear on-screen.

Where To Go From Here?

Now that you’ve covered the basics of adding animation to cells, try changing the values of your transform to see what other effects you can achieve. Some suggestions are:

  1. Faster or slower animation
  2. Larger rotation angle
  3. Different offsets; if you change the rotation angle, you will likely need to change the offset to make the animation look right. What does the animation look like if you drop the offset entirely and use 0,0 for the parameters?
  4. Go nuts and create some whacked-out transforms.
  5. Advanced:Can you get the card to rotate along the horizontal or vertical axis? Can you make it look like it flips over completely?
  6. Advanced:Add an else clause to tableView:willDisplayCell:forRowAtIndexPath: and perform a different animation when cells are displayed a second time.
Table View Animations Tutorial: Drop-In Cards_第1张图片

Crazy rotations that you can (but maybe shouldn’t) apply to your cells. See if you can duplicate these!

A great exercise is to try and identify animations in your favorite apps. Even with the simple animation from this tutorial, there are countless variations you can produce on this basic theme. Animations can be a great addition to user actions, such as flipping a cell around when selected, or fading in or out when presenting or deleting a cell.)

If you want to see this technique in a slightly different context, check out UIView Tutorial for iOS: How To Use UIView Animation.

Again, a big thank you goes to Orta Therox for the original implementation of this code. Anything clever in the animation is from Orta, the sloppy parts are from myself.

From: http://www.raywenderlich.com/49311/advanced-table-view-animations-tutorial-drop-in-cards


你可能感兴趣的:(Table View Animations Tutorial: Drop-In Cards)