nativescript-community / ui-collectionview

Allows you to easily add a collection view (grid list view) to your projects. Supports vertical and horizontal modes, templating, and more.
Apache License 2.0
59 stars 18 forks source link

Collection view interfering with page transitions in android and loading items to late on IOS #34

Closed cjohn001 closed 8 months ago

cjohn001 commented 2 years ago

Which platform(s) does your issue occur on?

Please, provide the following version numbers that your issue occurs with:

Please, tell us how to recreate the issue in as much detail as possible.

@farfromrefug The setup looks as follows, I am using a route resolver, in order to prefetch all data that should be displayed within the ui collection view. All data to be rendered are therefore already available and setup in ngOnInit of the component that is using the collection view. Note: There is no observable, timer or similar which could be firing during page transition, hence definitely no different interferences.

The following videos show that the page transition on android gets interfered, i.e. frames of the animation are lost (video 1 and 2 made on Android emulator, Video 1 shows how transition should look like and video 2 shows effect when collectionview is added). This problem results from the collection view, who seems to load its child items during the page transition, and not before on initial page rendering. I would expect, that the collectionview first sets up its child items and navigation should then happen afterwords. The broken transition looks even more worth on a real device which has less processing power.

I think the problem is not platform specific. On IOS I am seeing a similar issue (video 3 from ios emulator). The page transitions here do not break, but one can see, that the collection view does load its items during the page transition and not beforehand. On IOS this does not get visible if only small amounts of cells need to be rendered, but if a larger amount is provided one can see that the items are loaded to late. In video 3 made on IOS emulator the collectionview has about 100 cells to load, and you can see, that the tab holding the collectionview gets visible too late.

As the collectionviews do only create as much child elements as they require to displayed data on the screen + some cells for scrolling which are than recycled, I would expect that the amount of data to be displayed should neither effect initial rendering nor page transitions. Would be great if someone could come up with a solution. The collection view is going to look quite unprofessional in an app if it cannot come up preloaded with data items.

Video 1:

https://user-images.githubusercontent.com/43253483/131254117-1659d923-a7a6-46b4-a88f-6728d9b8c0b1.mov

Video 2:

https://user-images.githubusercontent.com/43253483/131254149-93423f72-ab8a-4f6d-9a94-5e941535fec3.mov

Video 3:

https://user-images.githubusercontent.com/43253483/131254188-d84e4e43-b2ed-49c2-9758-7f5f393f3a9c.mov

farfromrefug commented 2 years ago

@cjohn001 thanks for the detailed report. The issue does not really come from the collectionview plugin. It only does what you tell it to do. The issue is that you set items too soon and thus when the view is loaded (before the transition starts) it has to load the collectionview items. This is a common issue in app design, not only with nativescript but with any mobile app. You need to set your items in navigatedTo event. This should solve your issue. Now i see that you would not want the cells to appear after the transition. This is kind of another issue. You could actually want to set items in 'loaded' event and thus before transition so that you get a smooth transition with already loaded cells. There the issue that the cells creation is to slow. This is mostly related to N and the way it works on android. It should be easy to see by profiling the app. The rule to make cell load faster is less and faster views. I personally created @nativescript-community/ui-canvaslabel for that sol purpose. In your video 3 with this plugin the cell can be represented with only one crazy fast canvaslabel view. It has some limits but i see it working with your design. Though all that requires some work and optimisation. I highly use it here https://github.com/farfromrefug/ns-forecastie if you want to see an example app with it

cjohn001 commented 2 years ago

@farfromrefug: Thanks for the explanation. I will have a look into the canvaslabel. My cell items actually consist of a grid with two subgrids. One subgrid holding the visible content, and another subgrid holding a menu, which is displayed if the cell content is slided to the left. Not sure if I can do this with canvas label, see second video. Maybe using one Grid with two canvas labels? I will have a look if I can that way reduce by 2 internal Gridlayouts. However, I assume the general problem than will still stay.

Putting the data setup of the uicollection view into navigatedTo event is something I have tried already. Unfortunately, this looks terrible. See following video. Again all data are already available as they are precached. Till navigatedTo event fires it seems to take ages. ('loading text indicator displaying')

https://user-images.githubusercontent.com/43253483/131255956-b220321f-7d00-4d47-a221-3f231def1abb.mov

Is there not a way to let collection view load its data before N transition happens? I mean, this cannot be a performance issue. I have been using the nativescript UI RadListView before and there I have never observed such an issue. Such list loading is even possible with non-native apps.

https://user-images.githubusercontent.com/43253483/131256583-0917b277-fb9e-4b4f-938e-db8ad6f78cd1.mov

cjohn001 commented 2 years ago

@farfromrefug : I have another question regarding the canvaslabel, The documentation say it does not support css. On my cells I am using font icons. For them I have to set the font class. Is there a way to use them with the canvas label?

I have seen in an example that fontFamily can be set.

<CSpan id="text3" :text="item.text2" color="#eee" fontFamily="Lato-Regular" fontSize="16" fontStyle="normal" />

But I am wondering how the fontFamily name has to be made visible within the Angular templates. Currently my understanding is, the font name is only available via the css class for which I defined it. In css:

.fas {
    font-family: 'Font Awesome 5 Free', 'fa-solid-900';
    font-weight: 900;
}

By the way, I am unfortunately not able to build the forecasti app. Errors out with

webpack 5.50.0 compiled with 8 warnings in 8081 ms Webpack compilation complete. Watching for file changes. __extends is not defined

farfromrefug commented 2 years ago

@cjohn001 that error seems to come from latest cli. I have the same with latest. Downgrading works here. Now canvaslabel supports css on the canvaslabel item but will not on CSpan/CGroup. Now you can set any css property on the item directly. Just transform the name to camelcase. So icon font works too. About your gridlayout for me. No canvaslabel wont do that. If you wanted to to get rid of it you would to create the menu natively. Finally about the navigatedTo event dont understand why you say it looks ugly. Btw almost all apps.do what i explained they load the data only when the view transition is done. Look at neflix for example they now do it for.every single page. They use the squeleton loading view( i am only saying that to point out it is not only a N issue, although i do think it is accentuated in N because of JNI)

cjohn001 commented 2 years ago

@farfromrefug : Thank you very much for the background info.

I assume with error you mean the issue with the forecasti app. I will try an older cli version than. I am not 100% sure if I got you regarding the canvaslabel. I paste my current cell content here, in order to be able to show what I am now trying to optimize. It looks as follows:

Bildschirmfoto 2021-08-29 um 21 56 55
        <ng-template let-item="item" let-index="index">
          <GridLayout class="list-item-container" (touch)="onItemTouch($event)">

            <StackLayout id="idMainView" orientation=horizontal class="list-item">
              <Label class="h3 dark-grey-text-color" [text]="item.formatedWeight" style="width:21%;"
                verticalAlignment="center"></Label>
              <Label class="h3 dark-grey-text-color" [text]="item.formatedDate" style="text-align:right;width:39%;"
                verticalAlignment="center"></Label>
              <Label class="h3 dark-grey-text-color" [text]="item.formatedTime" style="text-align:right; width:40%;"
                verticalAlignment="center"></Label>
            </StackLayout>

            <GridLayout id="idMenuView" rows="45" columns="*" class="delete-grid-layout" width="60" visibility="hidden"
              horizontalAlignment="right">
              <Label col="0" text="&#xe03d;" class="fsap icon-font-menu-button" verticalAlignment="center"
                horizontalAlignment="center" (tap)="onDeleteButtonClicked($event, item)"
                (loaded)="addHighlightedPseudo($event)"></Label>
            </GridLayout>

          </GridLayout>
        </ng-template>

So in this example, I have an outer gridlayout which detects touch events and starts the slide animations to bring in the menu. The main content is given by the StackLayout and the menu by the inner grid. I would now try to replace the stacklayout with a canvas label and the inner gridlayout with a second canvas label. I assume if I can do this it would save me a lot overhead.

Than I have a second example here where I would like to ask your for your opinion

Bildschirmfoto 2021-08-29 um 21 59 48
<ng-template let-item="item" let-index="index">
    <GridLayout rows="25,20" columns="*,auto" (tap)="itemTapped($event, item)" class="list-item">
                <Label row="0" col="0" class="h3 dark-grey-text-color zero p-r-5" [text]="item.productName"
                    horizontalAlignment="Left"></Label>

                <GridLayout row="1" columns="*,auto" horizontalAlignment="left">
                    <Label col="0" horizontalAlignment="Left" [text]="item.subHeader" class="h5 subheader zero"></Label>
                    <Label col="1" [text]="item.logo" [class]="item.cssClass"></Label>
                </GridLayout>

                <Label col="1" rowSpan="2" [text]="item.energy" horizontalAlignment="Right" verticalAlignment="center"
                    class="h3 energy-label"></Label>
            </GridLayout>
        </ng-template>

Here the relevant part is the inner grid. Do you think it is possible to replace it with a single canvaslabel? Like you can see from the screenshot, there are 3 different text fonts involved, one for the standard text, and I do have different items (person, database symbol) which come from different textfonts. I assume if I have to set one css class to the canvaslabel, I will not be able to use a textfont for the text and a second font for the logo. Hence, I can not do it with a single canvaslabel? If I replace a NSLabel with a CanvasLabel 1by1, I assume I will not gain any performance benefits. Or is my assumption wrong and this could be done with a single canvaslabel as well? How could I do it?

<Label col="1" [text]="item.logo" [class]="item.cssClass">

If you wanted to to get rid of it you would to create the menu natively.

I unfortunately do not have the programming skills for it. I would likely sit for 2 month on this problem. I will try to optimize with canvaslabel, and if this does not work I will try to switch back to RadListView (this did work till I migrated to NS8). Before I would als look at the standard Nativescript Listview if it cant do it.

Finally about the navigatedTo event dont understand why you say it looks ugly.

Well, of course this is opinionated, but I think a good UI should not be distinguishable from a completely local running app. Hence, ideally you should never see a loading icon or wait for content to appear. Of course this is not always possible when internet is slow. But currently I am spending a lot of effort to cache all relevant data in order not to be required to reload them via internet. And actually 95% of the displayed data in my app can be resolved from a local database. I do not want to break the entire philosophy because a plugin does have problems to get the data rendered to screen appropriately.

cjohn001 commented 2 years ago

@farfromrefug : I would like to give another comment on this issue. I have now replaced my usage of collectionview with the standard listview from core. I do not have issues with the android transitions then anymore. The transition problem did not exist with RadListView and it also does not with the standard ListView. I consider the transition problem as a real show stopper for usage of this plugin. What I am still seeing is, that when lots of items need to be loaded, that the page might become visible a little too late. Hence, I assume the transition problem is a distinct issue from the one very loading of cell content. is slow.

farfromrefug commented 2 years ago

@cjohn001 i understand. Listview might not be loading data as soon as i do. Also be careful in comparing listview does NOT use the same native view class If you can create a repro sample(not angular please) i can look at it.