Skip to content

Instantly share code, notes, and snippets.

@jpriebe
Last active August 15, 2021 13:23
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jpriebe/b14d00a660464171464083f2f7b91784 to your computer and use it in GitHub Desktop.
Save jpriebe/b14d00a660464171464083f2f7b91784 to your computer and use it in GitHub Desktop.
Appcelerator Titanium code for managing scrollviews inside of scrollviews
function NestedScrollViewManager (parent_view)
{
// Appcelerator Titanium code for managing scrollviews inside of scrollviews (not true Android
// NestedScrollViews, but just a horizontal scrollview inside of a vertical scrollview).
//
// If you want to put a horizontal scrollview inside a vertical scrollview (like the Netflix app UI),
// it seems to work reasonably well on iOS. But on android, the user experience is very janky.
// Unless the user's drag movements are nearly exactly horizontal, there will be some movement of
// the parent scrollview, and then it becomes very difficult to scroll the child view. Flinging is
// almost impossible.
//
// With a little code to detect the horizontal movement, we can temporarily lock the parent view
// from scrolling, making the behavior closer to that of ios.
//
// call it like this:
//
// var NestedScrollViewManager = require ('/path/to/NestedScrollViewManager');
// var nsvm = new NestedScrollViewManager (v_scrollview);
// nsvm.add_child_view (h_scrollview);
//
// (assuming v_scrollview is the parent scrollview containing h_scrollview)
var _parent_view = parent_view;
var _child_views = [];
var _current_child = null;
var _startx,
_starty,
_direction_detected = false;
function log_event (evt, msg)
{
var log_msg = "[NSVM] ";
if (evt)
{
log_msg += "<<" + evt + ">>";
}
if (msg)
{
log_msg += " " + msg;
}
Ti.API.info (log_msg);
}
function set_scrolling_enabled (p, c)
{
_parent_view.scrollingEnabled = p;
// if user is interacting with one specific child, enable/disable its scrolling
if (_current_child !== null)
{
_current_child.scrollingEnabled = c;
return;
}
// otherwise, apply the change to all children
for (var i = 0; i < _child_views.length; i++)
{
_child_views[i].scrollingEnabled = c;
}
}
function on_parent_view_touchstart (e)
{
log_event ("parent_view.touchstart");
_current_child = null;
set_scrolling_enabled (true, true);
}
function on_child_view_touchstart (e) {
log_event ("touchstart");
_startx = e.x;
_starty = e.y;
_direction_detected = false;
set_scrolling_enabled (false, true);
e.cancelBubble = true;
}
function on_child_view_touchmove (e){
if (_direction_detected)
{
return;
}
log_event ("touchmove");
var deltax = Math.abs (e.x - _startx);
var deltay = Math.abs (e.y - _starty);
var distance = Math.sqrt (deltax * deltax + deltay * deltay);
if (distance > 30)
{
if (deltax > deltay)
{
log_event ("touchmove", "direction = vertical");
set_scrolling_enabled (false, true);
}
if (deltay > deltax)
{
log_event ("touchmove", "direction = vertical");
set_scrolling_enabled (true, false);
}
_direction_detected = true;
}
}
this.add_child_view = function (child_view)
{
if (Ti.Platform.osname !== 'android')
{
return;
}
child_view.addEventListener('touchstart', function (e) {
_current_child = child_view;
on_child_view_touchstart (e);
});
child_view.addEventListener('touchmove', function (e) {
_current_child = child_view;
on_child_view_touchmove (e);
});
_child_views.push (child_view);
};
if (Ti.Platform.osname !== 'android')
{
return;
}
log_event (null, "creating new NSVM...");
_parent_view.addEventListener ('touchstart', on_parent_view_touchstart);
}
module.exports = NestedScrollViewManager;
@AppWerft
Copy link

Great work!

@joshualambert
Copy link

Very useful code snippet here! Thanks for sharing.

@Jei
Copy link

Jei commented Feb 20, 2018

This is awesome!
I modified it slightly to support multiple horizontal ScrollViews. See my fork.

@jpriebe
Copy link
Author

jpriebe commented Mar 6, 2018

Sorry, Jei -- I missed your fork, and meanwhile, I was busy doing something similar. I posted my change to support adding multiple child views. Mine's a little different in that it doesn't need underscore/lodash.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment