How to detect if component is in screen view (is visible)


#1

Hi all,

I have a listview and want to check if given component(e.g Image, Label) is visible while scrolling. Ideally I want something like this (web example) http://jsfiddle.net/jAsDJ/ . Any help would be appreciated. Thank you


#2

I have the same question! Surprised there’s not a clear answer for this yet.

Based on what I can tell, Android provides a native isShown property for most UI elements that should indicate when a UI native Android UI element is visible. Meanwhile, iOS seems to expose native viewDidAppear and viewDidDisAppear events that indicate when an element is shown on screen. Perhaps those could be leveraged by NativeScript to expose an x-plat “isShown” type property?

@tjvantoll @jen.looper @NathanaelA Any thoughts on this topic? Would there be better ways to check if an element is actually visible on screen?


#3

This is an interesting use case and I’m surprised I haven’t run into it yet. One thing you could try is listening for the loaded event, as I believe that event won’t trigger until the OS actual loads the control onto the screen.

Dunno though, this isn’t something I’ve played with so I’m curious if others have. I do like the idea of a small plugin that exposes a cross-platform attribute (or events).


#4

Well…it isn’t elegant, but here is at least one way to achieve this with a ScrollView

In essence, we tap-in to the scroll event and, after scrolling is done, we check the relative position of the elements in the ScrollView to the ScrollView itself. If the Y position of an element is less than zero (negative), that means some portion of it is not visible at the top of the ScrollView. If the Y position is greater than the actual height of the ScrollView, that means some portion of the element is not visible at the bottom of the ScrollView.

Then it’s just up to you to decide if an element should be considered “shown” if ANY part of it is in the visible range, or if some % of the element must be visible in the ScrollView to be considered “shown.” In the example below, at least 50% of the height of the element must be visible in the ScrollView to be considered shown.

main-page.ts

let scrollTimer: number;
let stack: StackLayout;
let scroll: ScrollView;

export function navigatingTo(args: EventData) {
    let page = <Page>args.object;

    // Grab the SrollView and StackLayout (inside of ScrollView) from XML layout
    stack = <StackLayout>page.getViewById("stackList");
    scroll = <ScrollView>page.getViewById("scrollList");

    // Create some dummy UI objects to fill the scrolling list
    for (let i = 0; i < 20; i++) {
        let label = new Label();
        label.cssClasses.add("bigLabel");
        label.text = `Label${i}`;

        stack.addChild(label);
    }

    // Wire-up the "onScroll" event
    scroll.on("scroll", (args: ScrollEventData) => {
        console.log("SCROLLING", args.scrollX, args.scrollY);

        // Use timer to process UI layout 2 seconds after scrolling stops
        clearTimeout(scrollTimer);
        scrollTimer = setTimeout(() => {
            afterScroll();
        }, 2000);
    });

    // Call the "afterScroll" method once on load to set initial show/not shown UI
    setTimeout(() => {
        afterScroll();
    }, 1000);
}

function afterScroll() {
    console.log("Scrolling Done");

    // Get size of the scrollview
    let scrollHeight = scroll.getActualSize().height;
    console.log("Scroll Height: "+ scrollHeight);

    // Check each item in the ScrollView and see if it's in the visible bounds
    stack.eachChildView((c) => {
        let l = <Label>(c);

        // Get the height of the UI elements in the scrollview
        let halfHeight = l.getActualSize().height / 2;

        // Get the UI element location RELATIVE TO the scrollview
        let relativeY = c.getLocationRelativeTo(scroll).y;
        console.log(`${ l.text }: ${relativeY}`, halfHeight);

        // Here's the logic for "shown"/"not shown"
        // In this example...
        // Element is considered "shown" if at least HALF of element is visible (on top or bottom of scrollview)
        if (relativeY > (halfHeight * -1) && relativeY < (scrollHeight - halfHeight)) {
            // We'll change color when item is shown/not shown, but here is
            // where you could do other things like play/pause video
            l.backgroundColor = new Color("green");
        } else {
            l.backgroundColor = new Color("blue");
        }

        return true;
    });
}

main-page.xml

<StackLayout>
   <ScrollView id="scrollList">
      <StackLayout id="stackList">
      </StackLayout>
   </ScrollView>
</StackLayout>

app.css

.bigLabel {
    width:100%;
    height: 250;
    background-color: blue;
    color: white;
    font-size: 48;
    margin-bottom: 15;
}

RESULT:

Quick and dirty, but hope it helps someone trying to work through this. I’m sure there are more elegant solutions {N} wizards greater than I can propose. :slight_smile:


#5

@toddanglin thank you for sharing your ideas and code. I am using ListView, but I will try to implement something similar unless @tjvantoll has cleaner solution or something which can be used with ListView.


#6

I need this feature as well,
Hope some one can create a plugin?
better to emit events like: enter view event, exit view event