Created
February 11, 2016 16:38
-
-
Save jbender/fc7f2d997424014d9477 to your computer and use it in GitHub Desktop.
Example RubyMotion Swipable Cell module
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module SwipeableCell | |
ANIMATION_DURATION = 0.2 | |
MAX_TRANSITION_ALPHA = 0.5 | |
SWIPE_THRESHOLD = 110 | |
LONG_SWIPE_THRESHOLD = SWIPE_THRESHOLD + 80 | |
FADE_IN_DISTANCE = SWIPE_THRESHOLD | |
def gestureRecognizer(recognizer, | |
shouldRecognizeSimultaneouslyWithGestureRecognizer: other_recognizer) | |
return true unless recognizer.is_a? UIPanGestureRecognizer | |
# Allow other recognizers to see vertical panning | |
Gesture.panning_vertically?(recognizer, content_view) | |
end | |
def gestureRecognizerShouldBegin(recognizer) | |
return true unless recognizer.is_a? UIPanGestureRecognizer | |
# Only respond to events if they're panning side to side | |
Gesture.panning_horizontally?(recognizer, content_view) | |
end | |
def on_pan(recognizer) | |
current_location = Gesture.location(recognizer, content_view) | |
case recognizer.state | |
when UIGestureRecognizerStateBegan | |
@pan_gesture_start = current_location | |
when UIGestureRecognizerStateChanged | |
handle_panning horizontal_movement(current_location) | |
when UIGestureRecognizerStateEnded, UIGestureRecognizerStateCancelled | |
handle_gesture_end horizontal_movement(current_location) | |
end | |
end | |
# rubocop:disable Style/HashSyntax | |
def slide_card(location, delay = 0, &completion) | |
UIView.animateWithDuration(ANIMATION_DURATION, | |
delay: delay, | |
options: UIViewAnimationOptionCurveLinear && | |
UIViewAnimationOptionAllowUserInteraction, | |
animations: -> { move_card_horizontally(location) }, | |
completion: -> (_) { completion.call if completion } | |
) | |
end | |
# rubocop:enable Style/HashSyntax | |
def slide_card_to_center(&callback) | |
slide_card(0, &callback) | |
end | |
def slide_card_off_screen(direction = :rtl, &callback) | |
raise 'Invalid direction' unless [:rtl, :ltr].include? direction | |
screen_size = content_view.frame.size.width | |
location = direction == :rtl ? -screen_size : screen_size | |
slide_card(location, &callback) | |
table_screen.swiped_cell = self | |
end | |
def move_card_horizontally(location) | |
move_content_to(location) | |
reapply_layout | |
end | |
def hidden_icons | |
{} | |
end | |
def icon_layout | |
raise 'Cell must define icon_layout' | |
end | |
def move_content_to(location) | |
raise 'Cell must define move_content_to' | |
end | |
def handle_panning(delta) | |
slide_card(-delta) | |
update_icons(delta) | |
end | |
private | |
def alpha_from_delta(delta) | |
return 1 if delta.abs >= FADE_IN_DISTANCE | |
(delta.abs / FADE_IN_DISTANCE) * MAX_TRANSITION_ALPHA | |
end | |
# rubocop:disable Style/HashSyntax | |
def connect_to_pan_recognizer | |
pan_recognizer = | |
UIPanGestureRecognizer.alloc.initWithTarget self, action: 'on_pan:' | |
pan_recognizer.delegate = self | |
layout.view.addGestureRecognizer pan_recognizer | |
end | |
# rubocop:enable Style/HashSyntax | |
# Right -> Left = Positive delta | |
# Left -> Right = Negative delta | |
def delta_to_direction(delta) | |
delta > 0 ? :rtl : :ltr | |
end | |
def handle_gesture_end(delta) | |
end | |
def hide_icon(icon_id) | |
icon_layout.get(icon_id).alpha = 0 | |
end | |
def hide_icons(&callback) | |
hidden_icons.values.map { |icon| icon[:object_id] }.each do |icon_id| | |
# rubocop:disable Style/HashSyntax | |
UIView.animateWithDuration(ANIMATION_DURATION, | |
animations: -> { hide_icon(icon_id) } | |
) | |
# rubocop:enable Style/HashSyntax | |
end | |
rmq.app.delay(ANIMATION_DURATION) { callback.call if callback } | |
end | |
def hide_inverse_icon(inverse_icon_hash) | |
hide_icon inverse_icon_hash[:object_id] | |
end | |
def horizontal_movement(current_location) | |
@pan_gesture_start.x - current_location.x | |
end | |
def icon_image_name(delta) | |
icon_options = hidden_icons[delta_to_direction(delta)] | |
return unless icon_options | |
return icon_options[:default_image] if delta.abs < SWIPE_THRESHOLD | |
if delta.abs < LONG_SWIPE_THRESHOLD || !icon_options[:long_swipe_image] | |
return icon_options[:short_swipe_image] | |
end | |
icon_options[:long_swipe_image] | |
end | |
def reapply_layout | |
content_view.layoutIfNeeded | |
end | |
def screen_size | |
content_view.frame.size.width | |
end | |
def show_revealing_icon(icon_hash, delta) | |
icon = icon_layout.get(icon_hash[:object_id]) | |
icon.alpha = alpha_from_delta(delta) | |
icon.image = | |
rmq.image.resource("dashboard/#{icon_image_name(delta)}") | |
end | |
def update_icons(delta) | |
direction = delta_to_direction(delta) | |
icon_hash = hidden_icons[direction] | |
inverse_icon_hash = hidden_icons[direction == :rtl ? :ltr : :rtl] | |
show_revealing_icon(icon_hash, delta) if icon_hash | |
return unless inverse_icon_hash | |
hide_inverse_icon(inverse_icon_hash) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment