Thứ Ba, 10 tháng 2, 2015

Creating Simple View Animations in Swift

28 Flares 28 Flares ×

Following the release of iOS 7 (and iOS 8 as well), animation and motion effects became central to the design of apps from both Apple and 3rd party developers. iOS 7 introduced a flat, minimal design to apps which inevitably resulted in some apps having a similar UI. To distinguish their apps from other apps, developers employed such features as animations and motion effects to make their apps stand out.

Not only are animations used to set your app apart, they can improve the overall user experience of your application. For great examples of how animations are used to improve UX, you should look at how Apple uses animations in their apps. For example, in the Photos app, when you select a photo from a collection, the photo expands out from the selected one and on closing it, it shrinks back to the selected photo. This adds to the navigation of the app in that it lets you know exactly where you were, if browsing many pictures.

view-animations-featured

Facebook’s Paper also employs animations beautifully to add to the overall user experience of the app. You select an article to read by flipping it up. This action, the fact that the article expands out from its thumbnail version, suggests that in doing the opposite, i.e. flipping the article downwards, would shrink it back to its thumbnail. Here, animation is used to convey how the app works and even a first time user of the app would soon be able to make assumptions on its use and figure out its navigation without needing a tutorial.

Not only do animations add to the user experience of the app, but they could be what delights and impresses your users guaranteeing repeat usage instead of uninstalling the app for a better one from the App Store.

There are numerous ways of including animations in your apps, some of them being by use of UIKit Dynamics, layer animations, view controller transitions or by using a third party library like the Facebook Pop library or JNWSpringAnimation framework.

In this tutorial we are going to look at simple view animations. You can download the starter project which is a Single View Application with a table view that lists the examples presented in the tutorial tutorial. I will be using this when we get to the examples portion of the tutorial, instead of starting from scratch.

The tutorial starts off with a summary of APIs used to animate views, and ends with some examples which show the use of some of the APIs in an app.

Basic View Animations

Creating animations on your views is a matter of changing properties on them and letting UIKit animate them automatically. The properties we change are the ones marked Animatable.

The following list shows the animatable properties.

  • center
  • alpha
  • frame
  • bounds
  • transform
  • backgroundColor
  • contentStretch

You will find that all animations involve a change of one or more of the above properties.

For simple view animations, UIKit provides the following APIs that can be used to animate views on the screen.

  • UIView.animateWithDuration(_:, animations:)
  • UIView.animateWithDuration(_:, animations:, completion:)
  • UIView.animateWithDuration(_:, delay:, options:, animations:, completion:)

The first one takes two parameters – a value for the duration in seconds of the animation and a closure where you specify the properties you want changed. UIKit will take the original state of the view and create a smooth transition from that state to the end state according to what you specified in the animations closure.

The other two APIs are similar to the first, but they take extra parameters that add more configuration to the animation. The second takes a completion closure which you can use to either specify another animation that you want done after the first or you can do some cleanup of the UI, for example, removing a view from the view hierarchy once another view is animated onto the scene.

The third API takes an additional two parameters – the delay, which is the time to wait before the animation starts and options, a UIViewAnimationOptions constant, which indicates how you want to perform the animations. The following shows the options available.

anim_image001

Spring Animations

Spring animations try to model the behaviour of a real life spring, in that, when a view is moved from one point to another, it will bounce/oscillate towards the end before settling down to position.

Below is the method block we use for spring animations.

  • UIView.animateWithDuration(_:, delay:, usingSpringWithDamping:, initialSpringVelocity:, options:, animations:, completion:)

The above is similar to the methods we looked at earlier except for two new parameters – usingSpringWithDamping and initialSpringVelocity. Damping is a value from 0 to 1 that determines how much the view bounces towards the end of the animation. The closer to 1 the value is, the less bouncy it will be. initialSpringVelocity as the name says, determines the initial velocity of the animation. This determines how strong the animation starts off. If you want it to start vigorously, then set a larger value, if you want a smooth animation, then you can set the value to 0.

Keyframe Animations

Keyframe animations enable you to set different stages of an animation. You can group different animations together that share some common properties, but still be able to control them separately.

Instead of an animation that just moves along a path, UIKit will execute the different stages of the animation.

The Keyframe animation APIs are as follows.

  • UIView.animateKeyframesWithDuration(_:, delay:, options:, animations:)
  • UIView.addKeyframeWithRelativeStartTime(_:, relativeDuration:)

The above two methods are used together, the second getting nested in the first’s animations closure.

The first method sets the overall configuration of the animation, like how long it takes, delay and its options. You then define one or more of the second method(the frames) inside the animations closure to set the different stages of the animation.

The relative start time and relative duration of each frame is a value between 0 and 1 that expresses the percentage time within the total duration of the animation.

View Transitions

View transitions are used when you want to add a new view to your view hierarchy or remove a view from the view hierarchy.

The APIs that are used to create view transitions are

  • UIView.transitionWithView(_:, duration:, options:, animations:, completion:)
  • UIView.transitionFromView(_:, toView:, duration:, options:, completion:)

You use the first one to introduce a view to the view hierarchy. The method takes similar parameters as we have seen in the other animation methods.

The second one is used to take one view from the view hierarchy and and place a new view in its place.

Examples

We’ll now look at a few examples that use some of the above API calls to animate views in the given starter project.

Example I

If you run the project, you’ll see a table view that lists the examples we’ll work through. Select Example I in the list and you should see a Login screen of an app with the username and password fields and login button.

We want these to be animated onto the screen when the app starts.

To get started we’ll hide the views from sight when the view first appears. Before Auto Layout, this would have been a simple matter of changing the specific view’s position in code, but since we set auto layout constraints on the views in the storyboard file, we’ll have to change the constraints in code, which will change the view’s position.

First, we need to get a reference of the constraints that we will change. Open the storyboard file. Locate the following constraints in the Example I Scene.

anim_image002

Open the Assistant Editor, and make sure it is the ExampleIViewController.swift that appears next to the storyboard on the split screen. Drag from the Center X Alignment – View – Username constraint to the ExampleIViewController class. Create an outlet named centerAlignUsername. Do the same for the Center X Alignment – View – Password and set its name to centerAlignPassword. Also create an outlet for the login button named loginButton and an action for the same button and name it login. Make sure you set the Type of the action to UIButton. You should have the following in code.

1
2
3
4
5
6
7
@IBOutlet weak var centerAlignUsername: NSLayoutConstraint!
@IBOutlet weak var centerAlignPassword: NSLayoutConstraint!
@IBOutlet weak var loginButton: UIButton!
   
@IBAction func login(sender: UIButton) {
       
}

In the ExampleIViewController.swift add the following method which is called before the view is presented on screen.

1
2
3
4
5
6
override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    centerAlignUsername.constant -= view.bounds.width
    centerAlignPassword.constant -= view.bounds.width
    loginButton.alpha = 0.0
}

This moves the username and password fields just out of view and sets the alpha value of the button to 0 which makes it invisible.

Add the following method which is called right when the view appears.

1
2
3
4
5
6
7
8
9
10
11
override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
       
    UIView.animateWithDuration(0.5, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
        self.centerAlignUsername.constant += self.view.bounds.width
        self.centerAlignPassword.constant += self.view.bounds.width
        self.loginButton.alpha = 1
        self.view.layoutIfNeeded()
    }, completion: nil)
           
}

Here we use the UIView.animateWithDuration() method that we saw earlier. We include the UIViewAnimationOptions.CurveEaseOut option which makes the animation start fast then slow down at the end. You can experiment with different options here. Command-click on UIViewAnimationOptions to see all the options available.

The animation lasts for 0.5 seconds and starts immediately. You have liberty over the duration, but you shouldn’t set such a large number as to annoy your users when animations on your app seem to take too long. Normally, the duration is set between 0.5 and 0.7 seconds, but as I said, this isn’t set in stone and you have liberty to set it to whatever feels right to you.

The animation does the exact opposite of what we did in viewWillAppear(). layoutIfNeeded() is used to lay out the views immediately they are changed. If you don’t include it, you will not see them get animated onto the screen, instead they will just be shown in their final position. Run the app, and you should see the following.

video001

The above looks more interesting than a static presentation, but the views getting animated at the same time, doesn’t create that great an effect. Modify the method as shown below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
       
    UIView.animateWithDuration(0.5, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
        self.centerAlignUsername.constant += self.view.bounds.width
        self.view.layoutIfNeeded()
    }, completion: nil)
       
    UIView.animateWithDuration(0.5, delay: 0.3, options: .CurveEaseOut, animations: {
        self.centerAlignPassword.constant += self.view.bounds.width
        self.view.layoutIfNeeded()
    }, completion: nil)
       
    UIView.animateWithDuration(0.5, delay: 0.4, options: .CurveEaseOut, animations: {
        self.loginButton.alpha = 1
    }, completion: nil)
       
}

Run the app and the resulting animation has the views animating on screen at different times. Looking at the code, you can see that after the first animation block, we set a delay on subsequent ones.

video002

In login screens, when login fails, there is usually an animation that indicates to the user that login has failed. This is sometimes done as a shake of the text fields or the login button, and a message that lets the user know that login has failed. We’ll add such an effect on the login button using springs. Modify the login() function as shown.

1
2
3
4
5
6
7
8
9
@IBAction func login(sender: UIButton) {
       
    let bounds = self.loginButton.bounds
    UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.2, initialSpringVelocity: 10, options: nil, animations: {
        self.loginButton.bounds = CGRect(x: bounds.origin.x - 20, y: bounds.origin.y, width: bounds.size.width + 60, height: bounds.size.height)
        self.loginButton.enabled = false
    }, completion: nil)
       
}

The above changes the size of the login button when pressed and animates the action with a spring animation which will cause the button’s width to expand and bounce a little at the end before settling.

video003

Play around with the damping value. If you set it to 1, the button will expand, with no bouncing at the end. You can also use the same method on the username and password fields. Instead of having them come onto the screen and just stop in place at the end, have them be spring-like and bounce a little before settling down.

Example II

In your app, you might need to replace the background image of a view automatically when some action happens. You could do it in a way where an image immediately replaces another on the view, or you could slowly fade it in to create a nice transition. We’ll create this effect here. Open ExampleIIViewController.swift and add the following.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
override func viewWillAppear(animated: Bool) {
       
    let firstImageView = UIImageView(image: UIImage(named: "bg01.png"))
    firstImageView.frame = view.frame
    view.addSubview(firstImageView)
       
    imageFadeIn(firstImageView)
       
}
   
func imageFadeIn(imageView: UIImageView) {
       
    let secondImageView = UIImageView(image: UIImage(named: "bg02.png"))
    secondImageView.frame = view.frame
    secondImageView.alpha = 0.0
       
    view.insertSubview(secondImageView, aboveSubview: imageView)
       
    UIView.animateWithDuration(2.0, delay: 2.0, options: .CurveEaseOut, animations: {
        secondImageView.alpha = 1.0
        }, completion: {_ in
            imageView.image = secondImageView.image
            secondImageView.removeFromSuperview()
    })
       
}

Here we create an image view and add it to the main view. We then call imageFadeIn() which creates a second view with a different image. We add this view above the first image view and set its alpha to 0. In the animation block, we animate its alpha value, making it visible. We then use a completion closure to set the image view’s image to the second image and we remove the second image view from the view hierarchy since it is no longer needed. I’ve added a long delay so that the animation doesn’t happen right at the moment we select Example II from the table view. The duration is also a bit long so we can see what’s going on in the demo.

The following is the effect.

videoeg02

Example III

Next we’ll look at keyframe animations. Open ExampleIIIViewController.swift and add the following variable and functions to the file. I’ve commented the code to explain each step.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var alertView: UIView!
   
func createView() {
       
    // Create a red view
    let alertWidth: CGFloat = view.bounds.width
    let alertHeight: CGFloat = view.bounds.height
    let alertViewFrame: CGRect = CGRectMake(0, 0, alertWidth, alertHeight)
    alertView = UIView(frame: alertViewFrame)
    alertView.backgroundColor = UIColor.redColor()
       
    // Create an image view and add it to this view
    let imageView = UIImageView(frame: CGRectMake(0, 0, alertWidth, alertHeight/2))
    imageView.image = UIImage(named: "bike_traveler.png")
    alertView.addSubview(imageView)
       
    // Create a button and set a listener on it for when it is tapped. Then the button is added to the alert view
    let button = UIButton.buttonWithType(UIButtonType.System) as UIButton
    button.setTitle("Dismiss", forState: UIControlState.Normal)
    button.backgroundColor = UIColor.whiteColor()
    let buttonWidth: CGFloat = alertWidth/2
    let buttonHeight: CGFloat = 40
    button.frame = CGRectMake(alertView.center.x - buttonWidth/2, alertView.center.y - buttonHeight/2, buttonWidth, buttonHeight)
       
    button.addTarget(self, action: Selector("dismissAlert"), forControlEvents: UIControlEvents.TouchUpInside)
       
    alertView.addSubview(button)
    view.addSubview(alertView)
}
   
func dismissAlert() {
       
}

In viewDidLoad() call the createView() function. Add the following to the bottom of viewDidLoad().

1
createView()

On running the app and selecting Example III from the table view, you should see a red view with an image view at the top and a button in the middle.

anim_image003

We want to have the view(which I’ll refer to as the alertView from now onwards) to shrink in size and fall downwards out of view.

When we created the button, we added a listener to it. When the button is tapped, dismissAlert() is called. Modify the function as shown.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func dismissAlert() {
       
    let bounds = alertView.bounds
    let smallFrame = CGRectInset(alertView.frame, alertView.frame.size.width / 4, alertView.frame.size.height / 4)
    let finalFrame = CGRectOffset(smallFrame, 0, bounds.size.height)
       
    UIView.animateKeyframesWithDuration(4, delay: 0, options: .CalculationModeCubic, animations: {
           
        UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 0.5) {
            self.alertView.frame = smallFrame
        }
           
        UIView.addKeyframeWithRelativeStartTime(0.5, relativeDuration: 0.5) {
            self.alertView.frame = finalFrame
        }
    }, completion: nil)
       
}

In the above code, we create frames that represent what we want for the two stages of animating the view. smallFrame shrinks to half the size of alertView, maintaining the center point and finalFrame has a position at the bottom of the screen, out of view.

We use a Keyframe animation with two keyframes. The first sets alertView’s frame to smallFrame and the second to finalFrame. The result will be that the alertView will shrink to half its size and then fall out of view. Notice I have put such a large number for the duration – 4 seconds. You can change this, I just wanted the animation running in slow-motion for the demo. Run the app and select Example III.

video004

The animation isn’t quite what we expected. You can see the red alertView animate as expected, but the scale of its children doesn’t change. Changing the parent’s frame, doesn’t automatically change its children’s frames.

We’ll use a feature introduced in iOS 7 called UIView snapshots to fix the animation. This allows you to take a snapshot of a UIView together with its hierarchy and render it into a new UIView.

In dismissAlert() add the following right before the Keyframe animation code.

1
2
3
4
let snapshot = alertView.snapshotViewAfterScreenUpdates(false)
snapshot.frame = alertView.frame
view.addSubview(snapshot)
alertView.removeFromSuperview()

Here, we create a snapshot view and add it to the main view. We then remove the alertView from view, as the snapshot will replace it.

Replace the Keyframe animation with the following.

1
2
3
4
5
6
7
8
UIView.animateKeyframesWithDuration(4, delay: 0, options: .CalculationModeCubic, animations: {
    UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 0.5) {
        snapshot.frame = smallFrame
    }
    UIView.addKeyframeWithRelativeStartTime(0.5, relativeDuration: 0.5) {
        snapshot.frame = finalFrame
    }
}, completion: nil)

Run the application and on tapping Dismiss, the view(snapshot, really) should animate as expected.

video005

Example IV

We’ve looked at how you can animate simple views. In this example we’ll look at how you can animate a table view.

If you select Example IV from the table view, you will find another table view with a list of items in it. When the table view is presented, the list items are already positioned onto the table. We want to animate them onto the view to create a more interesting effect.

video006

Open ExampleIVViewController.swift and add the following methods.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
override func viewWillAppear(animated: Bool) {
    animateTable()
}
   
func animateTable() {
    tableView.reloadData()
       
    let cells = tableView.visibleCells()
    let tableHeight: CGFloat = tableView.bounds.size.height
       
    for i in cells {
        let cell: UITableViewCell = i as UITableViewCell
        cell.transform = CGAffineTransformMakeTranslation(0, tableHeight)
    }
       
    var index = 0
       
    for a in cells {
        let cell: UITableViewCell = a as UITableViewCell
        UIView.animateWithDuration(1.5, delay: 0.05 * Double(index), usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: nil, animations: {
            cell.transform = CGAffineTransformMakeTranslation(0, 0);
        }, completion: nil)
           
        index += 1
    }
}

Here, when the view appears, the animateTable() function is called. We reload the table view data and loop through the cells that are currently visible on the screen and move each of them to the bottom of the screen. We then iterate over all the cells that we moved to the bottom of the screen and animate them back to position with a spring animation.

Run the app and you should see the following.

video007

Conclusion

We have looked at various APIs that you can use to add animations to your app’s views. This is not an exhaustive guide on animation in iOS but by using the simple APIs we have looked at, you can create various animations in your apps. To dive further into iOS animations, you could look at UIKit Dynamics, layer animations, view controller transitions and even motion effects. You could also look at third party libraries like Facebook Pop and JNWSpringAnimation framework which could help you build intricate animations more easily.

You can download the completed project here.

As always, leave me comment and share your thought about the tutorial.

Source : appcoda[dot]com
post from sitemap

Không có nhận xét nào:

Đăng nhận xét