class UIView

BubbleWrap has these same methods, but the logic and options are a little different. In the spirit of open source, I am blatantly copying their code, changing it to suit my needs, and offering it here

Public Class Methods

animate(options={}, more_options={}, &animations) click to toggle source

If options is a Numeric, it is used as the duration. Otherwise, duration is an option, and defaults to 0.3. All the transition methods work this way. @option options [Float] :duration Animation duration. default: 0.3 @option options [Float] :delay Delay before animations begin. default: 0 @option options [Float] :damping Enables the “spring” animation. Value of 1.0 is a stiff spring. @option options [Float] :velocity Used in a spring animation to set the initial velocity @option options [Proc] :after A block that is executed when the animation is complete, useful for chaining (though the `animation_chain` method is better!) @option options [Fixnum,Symbol] :options The options parameter that is passed to the UIView.animateWithDuration(…) method. You can also use the more intuitive options `:curve`, `:from_current`, and `:allow_interaction`. `uianimationcurve` symbols (e.g. :ease_in_out) are converted to Fixnum. @option options [Fixnum,Symbol] :curve The animation curve option. `uianimationcurve` symbols (e.g. :ease_in_out) are converted to Fixnum. default: UIViewAnimationOptionCurveEaseInOut @option options [Boolean] :from_current Whether or not to have animations start from their current position. default: true (aka UIViewAnimationOptionBeginFromCurrentState) @option options [Boolean] :allow_interaction default: false (aka UIViewAnimationOptionAllowUserInteraction)

# File lib/ios/sugarcube-animations/uiview.rb, line 34
def animate(options={}, more_options={}, &animations)
  raise "animation block is required" unless animations

  if options.is_a? Numeric
    duration = options
    options = more_options
  else
    duration = options[:duration] || 0.3
  end

  delay = options[:delay] || 0

  damping_ratio = options[:damping] || nil
  spring_velocity = options[:velocity] || 0.0

  # chain: true is used inside animation_chain blocks to prevent some weird
  # animation errors (nested animations do not delay/queue as you'd expect)
  if options[:chain] || Thread.current[:sugarcube_chaining]
    duration = 0
    delay = 0
    raise "Completion blocks cannot be used within an animation_chain block" if options[:after]
  end

  after_animations = options[:after]
  if after_animations
    if after_animations.arity == 0
      after_adjusted = ->(finished){ after_animations.call }
    else
      after_adjusted = after_animations
    end
  else
    after_adjusted = nil
  end

  animation_options = sugarcube_animation_options(options)

  if duration == 0 && delay == 0
    animations.call
    after_adjusted.call(true) if after_adjusted
  else
    prev_value = Thread.current[:sugarcube_chaining]
    Thread.current[:sugarcube_chaining] = true

    if damping_ratio
      UIView.animateWithDuration( duration,
                           delay: delay,
          usingSpringWithDamping: damping_ratio,
           initialSpringVelocity: spring_velocity,
                         options: animation_options,
                      animations: animations,
                      completion: after_adjusted
                                )
    else
      UIView.animateWithDuration( duration,
                           delay: delay,
                         options: animation_options,
                      animations: animations,
                      completion: after_adjusted
                                )
    end
    Thread.current[:sugarcube_chaining] = prev_value
  end
  nil
end
animation_chain(options={}, &first) click to toggle source

Animation chains are great for consecutive animation blocks. Each chain can take the same options that UIView##animate take.

# File lib/ios/sugarcube-animations/uiview.rb, line 101
def animation_chain(options={}, &first)
  chain = SugarCube::AnimationChain.new
  if first
    chain.and_then(options, &first)
  end
  return chain
end
attr_updates(*attrs) click to toggle source
# File lib/ios/sugarcube-ui/uiview.rb, line 10
def attr_updates(*attrs)
  attr_accessor(*attrs)
  attrs.each do |attr|
    define_method("#{attr}=") do |value|
      if instance_variable_get("@#{attr}") != value
        setNeedsDisplay
      end
      willChangeValueForKey(attr)
      instance_variable_set("@#{attr}", value)
      didChangeValueForKey(attr)
    end
  end
end
first_responder() click to toggle source

returns the first responder, starting at the Window and searching every subview

# File lib/ios/sugarcube-ui/uiview.rb, line 6
def first_responder
  UIApplication.sharedApplication.keyWindow.first_responder
end
sugarcube_animation_options(options) click to toggle source

This is an internal helper method to determine the animation options.

# File lib/ios/sugarcube-animations/uiview.rb, line 6
def sugarcube_animation_options(options)
  animation_options = options[:options]
  animation_options = animation_options.uianimationoption if animation_options.respond_to?(:uianimationoption)
  unless animation_options
    curve = options.fetch(:curve, UIViewAnimationOptionCurveEaseInOut)
    curve = curve.uianimationoption if curve.respond_to?(:uianimationoption)

    from_current = options.fetch(:from_current, true) ? UIViewAnimationOptionBeginFromCurrentState : 0
    allow_interaction = options.fetch(:allow_interaction, false) ? UIViewAnimationOptionAllowUserInteraction : 0
    repeat = options.fetch(:repeat, false) ? UIViewAnimationOptionRepeat : 0

    animation_options = curve | from_current | allow_interaction | repeat
  end
  return animation_options
end

Public Instance Methods

<<(view) click to toggle source

superview << view

> superview.addSubview(view)

# File lib/ios/sugarcube-ui/uiview.rb, line 28
def <<(view)
  self.addSubview(view)
  return self
end
animate(options={}, more_options={}, &animations) click to toggle source

Same as UIView##animate, but acts on self

# File lib/ios/sugarcube-animations/uiview.rb, line 122
def animate(options={}, more_options={}, &animations)
  if options.is_a? Numeric
    options = more_options.merge(duration: options)
  end

  UIView.animate(options, &animations)
  return self
end
animate_constraints(options={}, &animations) click to toggle source

Just calls layoutIfNeeded before and after constraints are applied

# File lib/ios/sugarcube-animations/uiview.rb, line 132
def animate_constraints(options={}, &animations)
  self.layoutIfNeeded
  animations.call
  UIView.animate(options) do
    self.layoutIfNeeded
  end
end
back_fiend!(options={}, &after) click to toggle source

Moves the view backwards, similar to what Google has been doing a lot recently

# File lib/ios/sugarcube-animations/uiview.rb, line 540
def back_fiend!(options={}, &after)
  scale = options[:scale] || 0.5
  perspective = options[:perspective] || -0.0005
  size = options[:size] || -140

  options[:duration] ||= 200.millisecs
  options[:curve] ||= UIViewAnimationOptionCurveLinear

  UIView.animation_chain(options) do
    self.layer.transform = CATransform3DTranslate(CATransform3DScale(CATransform3D.new(1,0,0,0, 0,1,0,perspective, 0,0,1,0, 0,0,0,1), scale, scale, scale), 0, size, 0)
  end.and_then(duration:300.millisecs, options:UIViewAnimationOptionCurveLinear) do
    self.layer.transform = CATransform3DTranslate(CATransform3DScale(CATransform3DIdentity, scale, scale, scale), 0, size, 0)
  end.and_then(&after).start
end
center_to(center, options={}, more_options={}, &after) click to toggle source
# File lib/ios/sugarcube-animations/uiview.rb, line 218
def center_to(center, options={}, more_options={}, &after)
  if options.is_a? Numeric
    options = more_options.merge(duration: options)
  end

  options[:after] = after

  animate(options) {
    self.center = SugarCube::CoreGraphics::Point(center)
  }
end
controller() click to toggle source

returns the nearest nextResponder instance that is a UIViewController. Goes up the responder chain until the nextResponder is a UIViewController subclass, or returns nil if none is found.

# File lib/ios/sugarcube-ui/uiview.rb, line 56
def controller
  if nextResponder.is_a?(UIViewController)
    nextResponder
  elsif nextResponder.is_a?(UIView)
    nextResponder.controller
  else
    nil
  end
end
convert_frame_from(source) click to toggle source
# File lib/ios/sugarcube-ui/uiview.rb, line 109
def convert_frame_from(source)
  return self.convert_rect(CGRectMake(0, 0, source.frame.size.width, source.frame.size.height), from: source)
end
convert_frame_to(destination) click to toggle source
# File lib/ios/sugarcube-ui/uiview.rb, line 105
def convert_frame_to(destination)
  return self.convert_rect(CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), to: destination)
end
convert_origin_from(source) click to toggle source
# File lib/ios/sugarcube-ui/uiview.rb, line 125
def convert_origin_from(source)
  return self.convert_point([0, 0], from: source)
end
convert_origin_to(destination) click to toggle source
# File lib/ios/sugarcube-ui/uiview.rb, line 121
def convert_origin_to(destination)
  return self.convert_point([0, 0], to: destination)
end
convert_point(point, to: destination) click to toggle source
# File lib/ios/sugarcube-ui/uiview.rb, line 129
def convert_point(point, to: destination)
  return self.convertPoint(point, toView: destination)
end
convert_rect(rect, to: destination) click to toggle source
# File lib/ios/sugarcube-ui/uiview.rb, line 113
def convert_rect(rect, to: destination)
  return self.convertRect(rect, toView: destination)
end
delta_to(delta, options={}, more_options={}, &after) click to toggle source
# File lib/ios/sugarcube-animations/uiview.rb, line 244
def delta_to(delta, options={}, more_options={}, &after)
  f = self.frame
  delta = SugarCube::CoreGraphics::Point(delta)
  position = SugarCube::CoreGraphics::Point(f.origin)
  to_position = CGPoint.new(position.x + delta.x, position.y + delta.y)
  move_to(to_position, options, more_options, &after)
  return self
end
dont_wiggle() click to toggle source
# File lib/ios/sugarcube-animations/uiview.rb, line 616
def dont_wiggle
  self.layer.removeAnimationForKey('wiggle_rotate')
  self.layer.removeAnimationForKey('wiggle_translate_y')
  self.layer.removeAnimationForKey('wiggle_translate_x')
  self.layer.transform = CATransform3DIdentity
end
fade(options={}, more_options={}, &after) click to toggle source

Changes the view alpha.

# File lib/ios/sugarcube-animations/uiview.rb, line 141
def fade(options={}, more_options={}, &after)
  if options.is_a? Numeric
    options = { opacity: options }
  end

  options[:after] = after

  animate(options) do
    self.alpha = options[:opacity]
  end
end
fade_in(options={}, more_options={}, &after) click to toggle source

Changes the view alpha to 1. @see fade

# File lib/ios/sugarcube-animations/uiview.rb, line 167
def fade_in(options={}, more_options={}, &after)
  if options.is_a? Numeric
    options = more_options.merge(duration: options)
  end

  options[:opacity] ||= 1.0

  fade(options, &after)
end
fade_out(options={}, more_options={}, &after) click to toggle source

Changes the view alpha to 0. @see fade

# File lib/ios/sugarcube-animations/uiview.rb, line 155
def fade_out(options={}, more_options={}, &after)
  if options.is_a? Numeric
    options = more_options.merge(duration: options)
  end

  options[:opacity] ||= 0.0

  fade(options, &after)
end
fade_out_and_remove(options={}, more_options={}, &after) click to toggle source

Changes the view alpha to 0 and then removes the view from its superview @see fade_out

# File lib/ios/sugarcube-animations/uiview.rb, line 179
def fade_out_and_remove(options={}, more_options={}, &after)
  if options.is_a? Numeric
    options = more_options.merge(duration: options)
  end

  original_opacity = self.alpha

  after_remove = proc do
    self.alpha = original_opacity
    removeFromSuperview
    after.call if after
  end

  fade_out(options, &after_remove)
end
first_responder() click to toggle source

returns the first responder, or nil if it cannot be found

# File lib/ios/sugarcube-ui/uiview.rb, line 39
def first_responder
  if self.firstResponder?
    return self
  end

  found = nil
  self.subviews.each do |subview|
    found = subview.first_responder
    break if found
  end

  return found
end
forward_fiend!(options={}, &after) click to toggle source

restores the layer after a call to 'back_fiend!'

# File lib/ios/sugarcube-animations/uiview.rb, line 556
def forward_fiend!(options={}, &after)
  options[:after] ||= after
  UIView.animate(options) do
    self.layer.transform = CATransform3DIdentity
  end
end
hide() click to toggle source
# File lib/ios/sugarcube-animations/uiview.rb, line 116
def hide
  self.hidden = true
  return self
end
move_to(position, options={}, more_options={}, &after) click to toggle source
# File lib/ios/sugarcube-animations/uiview.rb, line 230
def move_to(position, options={}, more_options={}, &after)
  if options.is_a? Numeric
    options = more_options.merge(duration: options)
  end

  options[:after] = after

  animate(options) do
    f = self.frame
    f.origin = SugarCube::CoreGraphics::Point(position)
    self.frame = f
  end
end
off_gestures() click to toggle source
# File lib/ios/sugarcube-gestures/gestures.rb, line 33
def off_gestures
  if @sugarcube_recognizers
    @sugarcube_recognizers.each do |recognizer, proc|
      self.removeGestureRecognizer(recognizer)
    end
    @sugarcube_recognizers = nil
  end

  self
end
on_gesture(klass, options={}, &proc) click to toggle source

A generic gesture adder, but accepts a block like the other gesture methods @yield [recognizer] Handles the gesture event, and passes the recognizer instance to the block. @param options [Hash] method/value pairs to call on the gesture. @overload on_gesture(recognizer)

Adds the gesture to the view, and yields the block when the gesture is recognized

@overload on_gesture(recognizer_class)

Instantiates a gesture and adds it to the view.

@example Using a UIGestureRecognizer class

view.on_gesture(UISwipeGestureRecognizer, direction: UISwipeGestureRecognizerDirectionLeft) { puts "swiped left" }

@example Using a UIGestureRecognizer instance

gesture = UISwipeGestureRecognizer
gesture.direction = UISwipeGestureRecognizerDirectionLeft
view.on_gesture(gesture) { puts "swiped left" }
# File lib/ios/sugarcube-gestures/gestures.rb, line 19
def on_gesture(klass, options={}, &proc)
  if klass.is_a? UIGestureRecognizer
    recognizer = klass
    recognizer.addTarget(self, action:'sugarcube_handle_gesture:')
  else
    recognizer = klass.alloc.initWithTarget(self, action:'sugarcube_handle_gesture:')
  end

  options.each do |method, value|
    recognizer.send(method, value)
  end
  sugarcube_add_gesture(proc, recognizer)
end
on_pan(fingers_or_options=nil, &proc) click to toggle source

@yield [recognizer] Handles the gesture event, and passes the recognizer instance to the block. @overload on_pan(fingers)

@param taps [Fixnum] Number of fingers

@overload on_pan(options)

@option options [Fixnum] :min_fingers Minimum number of fingers for gesture to be recognized
@option options [Fixnum] :max_fingers Maximum number of fingers for gesture to be recognized
@option options [Fixnum] :fingers If min_fingers or max_fingers is not assigned, this will be the default.
@option options [Array]  :edges Some combination of [:left, :right, :top, :bottom, :all]. If present, overrides fingers options and instead handles gestures originating at specified screen edges.
# File lib/ios/sugarcube-gestures/gestures.rb, line 125
def on_pan(fingers_or_options=nil, &proc)
  fingers = nil
  edge_options = [:none]
  min_fingers = nil
  max_fingers = nil
  recognizer = nil

  if fingers_or_options
    if fingers_or_options.is_a? Hash
      fingers = fingers_or_options[:fingers] || fingers
      edge_options = fingers_or_options[:edges] || edge_options
      min_fingers = fingers_or_options[:min_fingers] || min_fingers
      max_fingers = fingers_or_options[:max_fingers] || max_fingers
    else
      fingers = fingers_or_options
    end
  end

  if edge_options.uniq == [:none] # full view pan, possibly with finger options
    # if fingers is assigned, but not min/max, assign it as a default
    min_fingers ||= fingers
    max_fingers ||= fingers
    recognizer = UIPanGestureRecognizer.alloc.initWithTarget(self, action:'sugarcube_handle_gesture:')
    recognizer.maximumNumberOfTouches = min_fingers if min_fingers
    recognizer.minimumNumberOfTouches = max_fingers if max_fingers
  else #edges option, finger options ignored
    edges = :none.uirectedge
    edge_options.each do | edge |
      edges |= (edge.uirectedge || 0)
    end
    recognizer = UIScreenEdgePanGestureRecognizer.alloc.initWithTarget(self, action:'sugarcube_handle_gesture:')
    recognizer.edges = edges
  end
  sugarcube_add_gesture(proc, recognizer)
end
on_pinch(&proc) click to toggle source

@yield [recognizer] Handles the gesture event, and passes the recognizer instance to the block.

# File lib/ios/sugarcube-gestures/gestures.rb, line 70
def on_pinch(&proc)
  recognizer = UIPinchGestureRecognizer.alloc.initWithTarget(self, action:'sugarcube_handle_gesture:')
  sugarcube_add_gesture(proc, recognizer)
end
on_press(duration_or_options=nil, &proc) click to toggle source

@yield [recognizer] Handles the gesture event, and passes the recognizer instance to the block. @overload on_press(duration)

@param duration [Fixnum] How long in seconds before gesture is recognized

@overload on_press(options)

@option options [Fixnum] :duration How long in seconds before gesture is recognized
@option options [Fixnum] :taps Number of taps before gesture is recognized
@option options [Fixnum] :fingers Number of fingers before gesture is recognized
# File lib/ios/sugarcube-gestures/gestures.rb, line 168
def on_press(duration_or_options=nil, &proc)
  duration = nil
  taps = nil
  fingers = nil
  distance = nil

  if duration_or_options
    if duration_or_options.is_a? Hash
      duration = duration_or_options[:duration] || duration
      taps = duration_or_options[:taps] || taps
      fingers = duration_or_options[:fingers] || fingers
      distance = duration_or_options[:distance] || distance
    else
      duration = duration_or_options
    end
  end

  recognizer = UILongPressGestureRecognizer.alloc.initWithTarget(self, action:'sugarcube_handle_gesture:')
  recognizer.minimumPressDuration = duration if duration
  recognizer.numberOfTapsRequired = taps if taps
  recognizer.numberOfTouchesRequired = fingers if fingers
  recognizer.allowableMovement = distance if distance
  sugarcube_add_gesture(proc, recognizer)
end
on_press_begin(duration_or_options=nil, &proc) click to toggle source
# File lib/ios/sugarcube-gestures/gestures.rb, line 193
def on_press_begin(duration_or_options=nil, &proc)
  duration = nil
  taps = nil
  fingers = nil
  distance = nil

  if duration_or_options
    if duration_or_options.is_a? Hash
      duration = duration_or_options[:duration] || duration
      taps = duration_or_options[:taps] || taps
      fingers = duration_or_options[:fingers] || fingers
      distance = duration_or_options[:distance] || distance
    else
      duration = duration_or_options
    end
  end

  recognizer = UILongPressGestureRecognizer.alloc.initWithTarget(self, action:'sugarcube_handle_gesture_on_begin:')
  recognizer.minimumPressDuration = duration if duration
  recognizer.numberOfTapsRequired = taps if taps
  recognizer.numberOfTouchesRequired = fingers if fingers
  recognizer.allowableMovement = distance if distance
  sugarcube_add_gesture(proc, recognizer)
end
on_press_ended(duration_or_options=nil, &proc) click to toggle source
# File lib/ios/sugarcube-gestures/gestures.rb, line 218
def on_press_ended(duration_or_options=nil, &proc)
  duration = nil
  taps = nil
  fingers = nil
  distance = nil

  if duration_or_options
    if duration_or_options.is_a? Hash
      duration = duration_or_options[:duration] || duration
      taps = duration_or_options[:taps] || taps
      fingers = duration_or_options[:fingers] || fingers
      distance = duration_or_options[:distance] || distance
    else
      duration = duration_or_options
    end
  end

  recognizer = UILongPressGestureRecognizer.alloc.initWithTarget(self, action:'sugarcube_handle_gesture_on_ended:')
  recognizer.minimumPressDuration = duration if duration
  recognizer.numberOfTapsRequired = taps if taps
  recognizer.numberOfTouchesRequired = fingers if fingers
  recognizer.allowableMovement = distance if distance
  sugarcube_add_gesture(proc, recognizer)
end
on_rotate(&proc) click to toggle source

@yield [recognizer] Handles the gesture event, and passes the recognizer instance to the block.

# File lib/ios/sugarcube-gestures/gestures.rb, line 76
def on_rotate(&proc)
  recognizer = UIRotationGestureRecognizer.alloc.initWithTarget(self, action:'sugarcube_handle_gesture:')
  sugarcube_add_gesture(proc, recognizer)
end
on_swipe(direction_or_options=nil, &proc) click to toggle source

@yield [recognizer] Handles the gesture event, and passes the recognizer instance to the block. @overload on_swipe(taps)

@param direction [Fixnum] Direction of swipe

@overload on_swipe(options)

@option options [Fixnum] :fingers Number of fingers before gesture is recognized
@option options [Fixnum, Symbol] :direction Direction of swipe, as a UISwipeGestureRecognizerDirection constant or a symbol (`:left, :right, :up, :down`)
# File lib/ios/sugarcube-gestures/gestures.rb, line 87
def on_swipe(direction_or_options=nil, &proc)
  direction = nil
  fingers = nil

  if direction_or_options
    if direction_or_options.is_a? Hash
      direction = direction_or_options[:direction] || direction
      fingers = direction_or_options[:fingers] || fingers
    else
      direction = direction_or_options
    end
  end

  case direction
  when :left
    direction = UISwipeGestureRecognizerDirectionLeft
  when :right
    direction = UISwipeGestureRecognizerDirectionRight
  when :up
    direction = UISwipeGestureRecognizerDirectionUp
  when :down
    direction = UISwipeGestureRecognizerDirectionDown
  end

  recognizer = UISwipeGestureRecognizer.alloc.initWithTarget(self, action:'sugarcube_handle_gesture:')
  recognizer.direction = direction if direction
  recognizer.numberOfTouchesRequired = fingers if fingers
  sugarcube_add_gesture(proc, recognizer)
end
on_tap(taps_or_options=nil, &proc) click to toggle source

@yield [recognizer] Handles the gesture event, and passes the recognizer instance to the block. @overload on_tap(taps)

@param taps [Fixnum] Number of taps

@overload on_tap(options)

@option options [Fixnum] :taps Number of taps before gesture is recognized
@option options [Fixnum] :fingers Number of fingers before gesture is recognized
# File lib/ios/sugarcube-gestures/gestures.rb, line 50
def on_tap(taps_or_options=nil, &proc)
  taps = nil
  fingers = nil

  if taps_or_options
    if taps_or_options.is_a? Hash
      taps = taps_or_options[:taps] || taps
      fingers = taps_or_options[:fingers] || fingers
    else
      taps = taps_or_options
    end
  end

  recognizer = UITapGestureRecognizer.alloc.initWithTarget(self, action:'sugarcube_handle_gesture:')
  recognizer.numberOfTapsRequired = taps if taps
  recognizer.numberOfTouchesRequired = fingers if fingers
  sugarcube_add_gesture(proc, recognizer)
end
reframe_to(frame, options={}, more_options={}, &after) click to toggle source
# File lib/ios/sugarcube-animations/uiview.rb, line 267
def reframe_to(frame, options={}, more_options={}, &after)
  if options.is_a? Numeric
    options = more_options.merge(duration: options)
  end

  options[:after] = after

  animate(options) do
    self.frame = frame
  end
end
resize_to(size, options={}, more_options={}, &after) click to toggle source
# File lib/ios/sugarcube-animations/uiview.rb, line 253
def resize_to(size, options={}, more_options={}, &after)
  if options.is_a? Numeric
    options = more_options.merge(duration: options)
  end

  options[:after] = after

  animate(options) do
    f = self.frame
    f.size = SugarCube::CoreGraphics::Size(size)
    self.frame = f
  end
end
rotate(options={}, more_options={}, &after) click to toggle source

Changes the current rotation by `new_angle` (`rotate` rotates to a specific angle)

# File lib/ios/sugarcube-animations/uiview.rb, line 298
def rotate(options={}, more_options={}, &after)
  if options.is_a? Numeric
    new_angle = options
    options = more_options
  else
    new_angle = options[:angle]
  end

  old_angle = valueForKeyPath('layer.transform.rotation.z')
  options[:angle] = old_angle + new_angle
  rotate_to(options, &after)
end
rotate_to(options={}, more_options={}, &after) click to toggle source

Changes the current rotation to `new_angle` (`rotate` rotates relative to the current rotation)

# File lib/ios/sugarcube-animations/uiview.rb, line 281
def rotate_to(options={}, more_options={}, &after)
  if options.is_a? Numeric
    new_angle = options
    options = more_options
  else
    new_angle = options[:angle]
  end

  options[:after] = after

  animate(options) do
    self.transform = CGAffineTransformMakeRotation(new_angle)
  end
end
scale_to(scale, options={}, more_options={}, &after) click to toggle source
# File lib/ios/sugarcube-animations/uiview.rb, line 195
def scale_to(scale, options={}, more_options={}, &after)
  if options.is_a? Numeric
    options = more_options.merge(duration: options)
  end

  if scale.is_a?(Numeric)
    scale_x = scale_y = scale
  else  # this could be an array, or CGSize; either way we'll use []
    scale_x = scale[0]
    scale_y = scale[1]
  end

  options[:after] = after

  animate(options) {
    radians = Math.atan2(self.transform.b, self.transform.a)
    # radians = self.valueForKeyPath('layer.transform.rotation.z')
    rotation_t = CGAffineTransformMakeRotation(radians)
    scale_t = CGAffineTransformMakeScale(scale_x, scale_y)
    self.transform = CGAffineTransformConcat(rotation_t, scale_t)
  }
end
shake(options={}, more_options={}) click to toggle source

Vibrates the target. You can trick this thing out to do other effects, like: @example

# wiggle
view.shake(offset: 0.1, repeat: 2, duration: 0.5, keypath: 'transform.rotation')
# slow nodding
view.shake(offset: 20, repeat: 10, duration: 5, keypath: 'transform.translation.y')
# File lib/ios/sugarcube-animations/uiview.rb, line 381
def shake(options={}, more_options={})
  if options.is_a? Numeric
    duration = options
    options = more_options
  else
    duration = options[:duration] || 0.3
  end

  offset = options[:offset] || 8
  repeat = options[:repeat] || 3
  if repeat == Float::INFINITY
    duration = 0.1
  else
    duration /= repeat
  end
  keypath = options[:keypath] || 'transform.translation.x'
  if keypath == 'transform.rotation'
    value_keypath = 'layer.transform.rotation.z'
  else
    value_keypath = keypath
  end

  if options[:from_current]
    origin = options[:origin] || valueForKeyPath(value_keypath)
  else
    origin = options[:origin] || 0
  end
  left = origin - offset
  right = origin + offset

  animation = CAKeyframeAnimation.animationWithKeyPath(keypath)
  # sometimes, because of conflicts with CATiming (or something to that
  # effect), calling 'duration=' results in a compiler or runtime error.
  animation.send(:'setDuration:', duration)
  animation.repeatCount = repeat
  animation.values = [origin, left, right, origin]
  animation.keyTimes = [0, 0.25, 0.75, 1.0]
  self.layer.addAnimation(animation, forKey:'shake')
  return self
end
show() click to toggle source
# File lib/ios/sugarcube-animations/uiview.rb, line 111
def show
  self.hidden = false
  return self
end
slide(direction, options={}, more_options={}, &after) click to toggle source
# File lib/ios/sugarcube-animations/uiview.rb, line 311
def slide(direction, options={}, more_options={}, &after)
  if options.is_a? Numeric
    size = options
    options = more_options
  else
    size = options[:size]
  end

  case direction
  when :left
    size ||= self.frame.size.width
    delta_to([-size, 0], options, &after)
  when :right
    size ||= self.frame.size.width
    delta_to([size, 0], options, &after)
  when :up
    size ||= self.frame.size.height
    delta_to([0, -size], options, &after)
  when :down
    size ||= self.frame.size.height
    delta_to([0, size], options, &after)
  else
    raise "Unknown direction #{direction.inspect}"
  end
  return self
end
slide_from(direction, options={}, more_options={}, &after) click to toggle source

moves the view off screen, then animates it back on screen. The movement off screen happens immediately, so if you provide a `delay:` option, it will only affect the movement back on screen.

# File lib/ios/sugarcube-animations/uiview.rb, line 341
def slide_from(direction, options={}, more_options={}, &after)
  if options.is_a? Numeric
    size = options
    options = more_options
  else
    size = options[:size]
  end

  options[:from_current] = false unless options.key?(:from_current)
  window_size = UIApplication.sharedApplication.windows[0].frame.size

  case direction
  when :left
    size ||= window_size.width
    self.center = CGPoint.new(self.center.x - size, self.center.y)
    self.delta_to([size, 0], options, &after)
  when :right
    size ||= window_size.width
    self.center = CGPoint.new(self.center.x + size, self.center.y)
    self.delta_to([-size, 0], options, &after)
  when :top, :up
    size ||= window_size.height
    self.center = CGPoint.new(self.center.x, self.center.y - size)
    self.delta_to([0, size], options, &after)
  when :bottom, :down
    size ||= window_size.height
    self.center = CGPoint.new(self.center.x, self.center.y + size)
    self.delta_to([0, -size], options, &after)
  else
    raise "Unknown direction #{direction.inspect}"
  end
  return self
end
sugarcube_to_s(options={}) click to toggle source
# File lib/ios/sugarcube-to_s/uiview.rb, line 3
def sugarcube_to_s(options={})
  suffix = ''
  # teacup
  if self.respond_to?(:stylename) && self.stylename && self.stylename.length > 0
    suffix += " stylename: #{self.stylename.inspect}"
  end
  # motionkit
  if self.respond_to?(:motion_kit_ids) && self.motion_kit_ids && self.motion_kit_ids.length > 0
    if motion_kit_ids.length == 1
      suffix += " motion_kit_id: #{self.motion_kit_id.inspect}"
    else
      suffix += " motion_kit_ids: #{self.motion_kit_ids.inspect}"
    end
  end
  # rmq?

  if options[:inner].is_a? Hash
    inner = ''
    options[:inner].each do |key, value|
      inner += ', ' if inner.length > 0
      inner += "#{key}: #{value.inspect}"
    end
  else
    inner = options[:inner]
  end

  "#{self.class.to_s}(##{self.object_id.to_s(16)}, [[#{frame.origin.x}, #{frame.origin.y}], [#{frame.size.width}, #{frame.size.height}]]" +
                      (inner ? ', ' + inner : '') +
                      ')' +
                      (options.fetch(:superview, true) && self.superview ? ", child of #{self.superview.class.to_s}(##{self.superview.object_id.to_s(16)})" : '') +
                      suffix
end
super_good_delete_wiggle()
Alias for: wiggle
to_s() click to toggle source
# File lib/ios/sugarcube-to_s/uiview.rb, line 36
def to_s
  sugarcube_to_s
end
tumble(options={}, more_options={}, &after) click to toggle source

Moves the view off screen while slowly rotating it.

Based on github.com/warrenm/AHAlertView/blob/master/AHAlertView/AHAlertView.m

# File lib/ios/sugarcube-animations/uiview.rb, line 431
def tumble(options={}, more_options={}, &after)
  if options.is_a? Numeric
    default_duration = options
    options = more_options
    side = options[:side] || :left
  elsif options.is_a? Symbol
    side = options
    options = more_options
    default_duration = 0.3
  else
    default_duration = 0.3
    side = options[:side] || :left
  end

  case side
    when :left
      angle = -Math::PI/4
    when :right
      angle = Math::PI/4
    else
      raise "Unknown direction #{side.inspect}"
  end

  options[:duration] ||= default_duration
  unless options.key?(:options) || options.key?(:curve)
    options[:options] = UIView.sugarcube_animation_options(curve: UIViewAnimationOptionCurveEaseIn)
  end

  reset_transform = self.transform
  reset_after = ->(finished) do
    self.transform = reset_transform
  end

  if after
    options[:after] = ->(finished) do
      reset_after.call(finished)

      if after.arity == 0
        after.call
      else
        after.call(finished)
      end
    end
  else
    options[:after] = reset_after
  end

  self.animate(options) do
    window = UIApplication.sharedApplication.windows[0]
    top = self.convertPoint([0, 0], toView:nil).y
    height = window.frame.size.height - top
    offset = CGPoint.new(0, height * 1.5)
    offset = CGPointApplyAffineTransform(offset, self.transform)
    self.transform = CGAffineTransformConcat(self.transform, CGAffineTransformMakeRotation(angle))
    self.center = CGPointMake(self.center.x + offset.x, self.center.y + offset.y)
  end
end
tumble_in(options={}, more_options={}, &after) click to toggle source

Moves the view on screen while slowly rotating it.

Based on github.com/warrenm/AHAlertView/blob/master/AHAlertView/AHAlertView.m

# File lib/ios/sugarcube-animations/uiview.rb, line 492
def tumble_in(options={}, more_options={}, &after)
  if options.is_a? Numeric
    default_duration = options
    options = more_options
    side = options[:side] || :left
  elsif options.is_a? Symbol
    side = options
    options = more_options
    default_duration = 0.3
  else
    default_duration = 0.3
    side = options[:side] || :left
  end

  case side
    when :left
      angle = -Math::PI/4
    when :right
      angle = Math::PI/4
    else
      raise "Unknown direction #{side.inspect}"
  end

  reset_transform = self.transform
  reset_center = self.center

  options[:duration] ||= default_duration
  unless options.key?(:options) || options.key?(:curve)
    options[:options] = UIView.sugarcube_animation_options(curve: UIViewAnimationOptionCurveEaseOut)
  end
  options[:after] = after

  window = UIApplication.sharedApplication.windows[0]
  top = self.convertPoint([0, 0], toView:nil).y
  height = window.frame.size.height - top
  offset = CGPoint.new(0, height * -1.5)
  offset = CGPointApplyAffineTransform(offset, self.transform)
  self.transform = CGAffineTransformConcat(self.transform, CGAffineTransformMakeRotation(angle))
  self.center = CGPointMake(self.center.x + offset.x, self.center.y + offset.y)

  self.animate(options) do
    self.transform = reset_transform
    self.center = reset_center
  end
end
uiimage(use_content_size=false) click to toggle source

Easily take a snapshot of a `UIView`.

Calling `uiimage` with no arguments will return the image based on the `bounds` of the image. In the case of container views (notably `UIScrollView` and its children) this does not include the entire contents, which is something you probably want.

If you pass a truthy value to this method, it will use the `contentSize` of the view instead of the `bounds`, and it will draw all the child views, not just those that are visible in the viewport.

It is guaranteed that `true` and `:all` will always have this behavior. In the future, if this argument becomes something that accepts multiple values, those two are sacred.

# File lib/ios/sugarcube-ui/uiview.rb, line 80
def uiimage(use_content_size=false)
  if use_content_size
    UIGraphicsBeginImageContextWithOptions(contentSize, false, 0.0)
    context = UIGraphicsGetCurrentContext()
    self.subviews.each do |subview|
      CGContextSaveGState(context)
      CGContextTranslateCTM(context, subview.frame.origin.x, subview.frame.origin.y)
      subview.layer.renderInContext(context)
      CGContextRestoreGState(context)
    end
    image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
  else
    UIGraphicsBeginImageContextWithOptions(bounds.size, false, 0.0)
    if self.respond_to?('drawViewHierarchyInRect:afterScreenUpdates:')
      self.drawViewHierarchyInRect(self.bounds, afterScreenUpdates: true)
    else
      layer.renderInContext(UIGraphicsGetCurrentContext())
    end
    image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
  end
  return image
end
unshift(view) click to toggle source
# File lib/ios/sugarcube-ui/uiview.rb, line 33
def unshift(view)
  self.insertSubview(view, atIndex: 0)
  return self
end
wiggle() click to toggle source

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) github.com/mxcl/SuperGoodDeleteWiggle

Note: doesn't accept a “completion” block, because it's an ongoing animation

# File lib/ios/sugarcube-animations/uiview.rb, line 570
def wiggle
  @wiggle_angle ||= 0.035
  @wiggle_offset ||= 0
  @wiggle_transform ||= -0.5

  self.layer.transform = CATransform3DMakeRotation(@wiggle_angle, 0, 0, 1.0)

  @wiggle_angle = -@wiggle_angle

  @wiggle_offset += 0.03
  if @wiggle_offset > 0.9
    @wiggle_offset -= 0.9
  end

  @wiggle_transform = -@wiggle_transform

  animation = CABasicAnimation.animationWithKeyPath('transform')
  animation.toValue = NSValue.valueWithCATransform3D CATransform3DMakeRotation(@wiggle_angle, 0, 0, 1.0)
  animation.repeatCount = Float::MAX
  animation.duration = 0.12
  animation.autoreverses = true
  animation.timeOffset = @wiggle_offset
  self.layer.addAnimation(animation, forKey: 'wiggle_rotate')

  animation = CABasicAnimation.animationWithKeyPath('transform.translation.y')
  animation.duration = 0.08
  animation.repeatCount = Float::MAX
  animation.autoreverses = true
  animation.fromValue = @wiggle_transform
  animation.toValue = -@wiggle_transform
  animation.fillMode = KCAFillModeForwards
  animation.timeOffset = @wiggle_offset
  self.layer.addAnimation(animation, forKey: 'wiggle_translate_y')

  animation = CABasicAnimation.animationWithKeyPath('transform.translation.x')
  animation.duration = 0.09
  animation.repeatCount = Float::MAX
  animation.autoreverses = true
  animation.fromValue = @wiggle_transform
  animation.toValue = -@wiggle_transform
  animation.fillMode = KCAFillModeForwards
  animation.timeOffset = @wiggle_offset + 0.6
  self.layer.addAnimation(animation, forKey: 'wiggle_translate_x')
end
Also aliased as: super_good_delete_wiggle
|(filter) click to toggle source

Applies a filter (to a UIImage representation) or coerces to another format

# File lib/ios/sugarcube-pipes/pipes.rb, line 35
def |(filter)
 if filter == UIImage
   self.uiimage
 else
   raise "The `|` operator is not supported for the #{filter.is_a?(Class) ? filter.name : filter.class.to_s} class"
 end
end

Private Instance Methods

dummy() click to toggle source

this dummy method is needed to define the setDuration method

# File lib/ios/sugarcube-animations/uiview.rb, line 423
def dummy
  self.setDuration(nil)
end
sugarcube_add_gesture(proc, recognizer) click to toggle source

Adds the recognizer and keeps a strong reference to the Proc object.

# File lib/ios/sugarcube-gestures/gestures.rb, line 276
def sugarcube_add_gesture(proc, recognizer)
  unless self.userInteractionEnabled?
    puts("SugarCube: userInteractionEnabled is false on #{self.inspect}. Adding a gesture will have no effect.")
  end
  self.addGestureRecognizer(recognizer)

  @sugarcube_recognizers = {} unless @sugarcube_recognizers
  @sugarcube_recognizers[recognizer] = proc.respond_to?('weak!') ? proc.weak! : proc

  recognizer
end
sugarcube_handle_gesture(recognizer) click to toggle source
# File lib/ios/sugarcube-gestures/gestures.rb, line 244
def sugarcube_handle_gesture(recognizer)
  handler = @sugarcube_recognizers[recognizer]
  if handler.arity == 0
    handler.call
  else
    handler.call(recognizer)
  end
end
sugarcube_handle_gesture_on_begin(recognizer) click to toggle source
# File lib/ios/sugarcube-gestures/gestures.rb, line 253
def sugarcube_handle_gesture_on_begin(recognizer)
  if recognizer.state == UIGestureRecognizerStateBegan
    handler = @sugarcube_recognizers[recognizer]
    if handler.arity == 0
      handler.call
    else
      handler.call(recognizer)
    end
  end
end
sugarcube_handle_gesture_on_ended(recognizer) click to toggle source
# File lib/ios/sugarcube-gestures/gestures.rb, line 264
def sugarcube_handle_gesture_on_ended(recognizer)
  if recognizer.state == UIGestureRecognizerStateEnded
    handler = @sugarcube_recognizers[recognizer]
    if handler.arity == 0
      handler.call
    else
      handler.call(recognizer)
    end
  end
end