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
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 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
# 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
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
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
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
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
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
# 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
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
# 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
# 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
# File lib/ios/sugarcube-ui/uiview.rb, line 125 def convert_origin_from(source) return self.convert_point([0, 0], from: source) end
# File lib/ios/sugarcube-ui/uiview.rb, line 121 def convert_origin_to(destination) return self.convert_point([0, 0], to: destination) end
# File lib/ios/sugarcube-ui/uiview.rb, line 129 def convert_point(point, to: destination) return self.convertPoint(point, toView: destination) end
# File lib/ios/sugarcube-ui/uiview.rb, line 113 def convert_rect(rect, to: destination) return self.convertRect(rect, toView: destination) end
# 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
# 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
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
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
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
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
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
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
# File lib/ios/sugarcube-animations/uiview.rb, line 116 def hide self.hidden = true return self end
# 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
# 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
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
@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
@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
@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
# 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
# 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
@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
@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
@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
# 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
# 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
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
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
# 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
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
# File lib/ios/sugarcube-animations/uiview.rb, line 111 def show self.hidden = false return self end
# 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
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
# 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
# File lib/ios/sugarcube-to_s/uiview.rb, line 36 def to_s sugarcube_to_s end
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
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
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
# File lib/ios/sugarcube-ui/uiview.rb, line 33 def unshift(view) self.insertSubview(view, atIndex: 0) return self end
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
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
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
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
# 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
# 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
# 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