Skip to content
colinta edited this page Sep 25, 2014 · 4 revisions

Simple show/hide

These do not affect the alpha channel; to hide an image in preparation for fade_in, either set view.alpha = 0 or run the animation immediately view.fade_out(0).

self.view.show  # => self.hidden = false
self.view.hide  # => self.hidden = true

Animations

jQuery-like animation methods. They accept a "completed" callback that is passed an optional 'completed' boolean. These methods delegate to UIView.animateWithDuration(delay:options:animations:completion:) or UIView.animateWithDuration(delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:) if you are using the :damping option.

# default timeout is 0.3
view.fade_out

# with a callback
view.fade_out do
  view.removeFromSuperview
end

# and the completed argument
view.fade_out do |completed|
  view.removeFromSuperview
end

# fade_out options
view.fade_out(duration: 0.5,
                 delay: 0,
               options: UIViewAnimationOptionCurveLinear,
               opacity: 0.5)

view.move_to([0, 100])   # move to position 0, 100
view.delta_to([10, 100])  # move over 10, down 100, from current position

view.rotate_to Math::PI  # rotate view upside down
view.rotate 45.degrees  # rotate *an additional* 45 degrees
view.rotate_to(duration: 0.5, angle: 45.degrees)  # using options

view.slide :left   # slides the entire view left, right, up, or down. The
                   # default amount is the width of the view being moved, but
                   # you can override
view.slide :left, 320
view.slide :left, size: 320

view.shake  # shakes the view.
# options w/ default values:
view.shake offset: 8,   # move 8 px left, and 8 px right
           repeat: 3,   # three times
         duration: 0.3, # for a total of 0.3 seconds
          keypath: 'transform.translate.x'

# vigorous nodding - modifying transform.translation.y:
view.shake offset: 20, repeat: 10, duration: 5, keypath: 'transform.translation.y'
# an adorable wiggle - modifying transform.rotation:
view.shake offset: 0.1, repeat: 2, duration: 0.5, keypath: 'transform.rotation'

# this was pulled off warrenm's AHAlertView project.  I thought the effect was
# awesome, and deserved more attention!
# https://github.com/warrenm/AHAlertView
view.tumble  # the view will fall and rotate - a good 'cancel button effect'
# the opposite transition is also available
view.tumble_in

# and this "SuperGoodDeleteWiggle" is an aptly named animation care
# of mxcl (of PromiseKit, YOLOKit, and if he used RubyMotion I'm 99%
# sure he would be using SugarCube - see his "initWith...F**It' repo
# if you don't believe me)
# https://github.com/mxcl/SuperGoodDeleteWiggle
view.wiggle  # weee!  excellent 'delete/move' animation
view.super_good_delete_wiggle  # aliased, with a hat tip to the creator
view.dont_wiggle
# since these are ongoing animations, they do *not* accept a "completion" block

The default behavior on all the animation methods is to animate from "the current" position (UIViewAnimationOptionBeginFromCurrentState). To disable that, you can either assign options: to something else, or you can disable just that option. If your animation doesn't seem to have the correct initial state, you might try this.

# in some cases, like if we're in the middle of an animation, setting the frame
# here doesn't have an "immediate" effect
view.frame = [[0, 0], [[100, 20]]
view.slide :left, from_current: false  # frame will start from [0, 0] and slide out of the screen

Other options can be assigned this way, like the curve

view.slide :left, from_current: false, curve: :ease_in  # :ease_in_out, :ease_in, :ease_out, :linear

Allow user interaction (by default it's disabled)

view.slide :left, allow_interaction: true

Not all options are configurable this way. Refer to UIViewAnimationOptions and assign them directly to options: if there are options you need that are not listed here.

view.slide :left, options: UIViewAnimationOptionAllowAnimatedContent | UIViewAnimationOptionCurveEaseInOut

Chaining animations

Using the completion: callback you can string animations together to create an animation sequence.

view.slide(:left, 20) do
  view.slide(:up, 20) do
    view.slide(:right, 20) do
      view.slide(:down, 20) do
        view.fade_out
      end
    end
  end
end

Those be some gnarly callbacks. In SugarCube you can write this as a chain instead!

UIView.animation_chain do
  view.slide(:left, 20)
end.and_then do
  view.slide(:up, 20)
end.and_then do
  view.slide(:right, 20)
end.and_then do
  view.slide(:down, 20)
end.and_then do
  view.fade_out
end.start            # <= make sure to call `start`!

Behind the scenes, calls to SugarCube animation methods (slide, fade, rotate) will not be run inside of a UIView#animateWithDuration(...) block. Because of this, animation chains seem to be a little fickle. For instance, you can call multiple animation methods inside the block, but if you animate the same property twice, only the last animation will have any effect.

UIView.animation_chain do
  view.slide(:left, 20)
  view.rotate(90.degrees)
end.and_then do
  view.slide(:up, 20)
  view.rotate(90.degrees)
end.and_then do
  view.slide(:right, 20)
  view.rotate(90.degrees)
end.and_then do
  view.slide(:down, 20)
  view.rotate(90.degrees)
end.and_then do
  # here's an example of doing things *wrong*
  view.fade_out   # no effect
  view.fade(0.5)  # this animation "wins"
  view.rotate_to(0.degrees)
end.start

Chains can also be written like this (why? so that you can add animations based on some application logic - the usual style is not as amenable to adding blocks conditionally).

chain = UIView.animation_chain
chain << proc { view.slide(:left, 20) }
chain << proc { view.slide(:up, 20) }
chain << proc { view.slide(:right, 20) }
chain << proc { view.slide(:down, 20) }
chain << proc { view.fade_out }
chain.start

AND chains can be looped! You can set a number of times, or call stop on the chain.

chain = UIView.animation_chain do
  view.slide(:left, 20)
end.and_then do
  view.slide(:right, 20)
end

chain.loop  # loop forever
2.seconds.later { chain.stop }  # the animation will complete, but not loop again

chain.loop(10)  # would loop 10 times

# if you're impatient
chain.abort
# will stop the animation at the end of whatever block it is in, so it could be
# in a strange position, depending on where in the chain it is.

Core Animation

All the animations we've discussed so far are "UIKit animations". Core Animation offers a much richer framework to perform animations, and SugarCube has just a few helpers here (more to come!).

# A simple fade-in animation.  If you haven't used CoreAnimation before, this might
# look a little unintuitive:
view.layer.opacity = 1
view.layer.basic_animation('opacity', from: 0)

# fade in, back out, and then in all the way
view.layer.keyframe_animation('opacity', values: [0, 0.5, 0.1, 1])

# similar effect using a cubic bezier path
path = UIBezierPath.bezierPath
path.moveToPoint([0, 0])
path.addCurveToPoint([1, 0], controlPoint1: [1.5, 0], controlPoint2: [-0.5, 0])
view.layer.keyframe_animation('opacity', path: path)
Clone this wiki locally