Animating Views

We talked about how important views are on iOS, but apps are also known for their smooth animations. Perhaps one reason slick transitions and niceties are so pervasive in iOS apps is that they’re so darn easy to implement. And we’re going to add that to our little box app right now.

Let’s add another button, Remove, which will fade out the most recently added view and slide all others to new positions in its place. That might sound complicated, and on other platforms or frameworks it might be, but the iOS animation APIs make it painless. All we do is tell the system what properties of our views to animate and how long that animation should take.

We will add yet another button to our AppDelegate and wire its target/action callbacks for removing a view.

 @add_button.addTarget(
  self, action​:"add_tapped"​, forControlEvents​:UIControlEventTouchUpInside​)
 @remove_button = UIButton.buttonWithType(UIButtonTypeSystem)
 @remove_button.setTitle(​"Remove"​, forState​:UIControlStateNormal​)
 @remove_button.sizeToFit
 @remove_button.frame = CGRect.new(
  [@add_button.frame.origin.x + @add_button.frame.size.width + 10,
  @add_button.frame.origin.y],
  @remove_button.frame.size)
 @window.addSubview(@remove_button)
 @remove_button.addTarget(
  self, action​:"remove_tapped"​,
  forControlEvents​:UIControlEventTouchUpInside​)

Pretty easy, right? Basically, all we did was set a frame and add a subview; that’s nothing new. Now we need to implement that remove_tapped callback. It’s going to be longer than our add_tapped method, so we’ll take it slow. First, we need to find the objects we’re interested in.

 def​ remove_tapped
  other_views = @window.subviews.reject { |view|
  view.is_a?(UIButton)
  }
  last_view = other_views.last
 return​ ​unless​ last_view && other_views.count > 1

Because our buttons are also subviews of the window, we need to prune them and make sure we deal only with the blue boxes. There are better ways to architect this (such as storing the boxes in some independent array), but this works with what we have. Next, we do the actual animations!

  animations_block = lambda {
  last_view.alpha = 0
  last_view.backgroundColor = UIColor.redColor
  other_views.reject { |view|
  view == last_view
  }.each { |view|
  new_origin = [
  view.frame.origin.x,
  view.frame.origin.y - (last_view.frame.size.height + 10)
  ]
  view.frame = CGRect.new(
  new_origin,
  view.frame.size)
  }
  }
  completion_block = lambda { |finished|
  last_view.removeFromSuperview
  }
  UIView.animateWithDuration(0.5,
 animations: ​animations_block,
 completion: ​completion_block)
 end

Animations revolve around the UIView.animateWithDuration:animations: group of methods (you can also use animateWithDuration:delay:options:animations:completions: if you need to fine-tune things). Any alterations to your views made in the lambda we pass for animations will animate if possible. Beyond the basics like frame and opacity, most sensible properties of UIView will work as expected.[8] In our case, we fade out the box by setting the floating-point alpha to zero. Then we enumerate through all the other views and adjust their frames.

We use the optional completion: argument to get a callback when the animation finishes. This block takes one boolean argument, which tells us if the callback has been fired when the animation actually completed (the callback may fire prematurely if the animation has been canceled elsewhere). This is a good place to clean up our views, which we do here by invoking removeFromSuperview. This will remove the view from its parent’s subviews and be erased from the screen.

The animation function looks a bit strange because of the multiple lambdas, but it’s no different from changing those properties of a view when they’re static. Give it a rake, add some boxes, and then watch them float away with the Remove button:

images/views/remove_boxes_anim.png
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset