NativeScript: Image with Text Overlay | GridLayout won't fit content - nativescript

I'm having trouble with NativeScript Layouts.
I wan't this look, just with an text overlay over the image.
The Image above was made with just an Image inside a StackLayout but when I add a Label it gets placed below. I tried using position: absolute in css but that won't work.
When I use GridLayout like this:
<CardView class="cardStyle" shadowRadius="10" margin="10" elevation="50" radius="10">
<GridLayout rows="*">
<GridLayout rows="*" columns="*">
<Image class="card-img" stretch="aspectFit" src="~/app/assets/pull.jpg"></Image>
<GridLayout verticalAlignment="bottom">
<Label text="hallo" class="card-header"></Label>
</GridLayout>
</GridLayout>
</GridLayout>
</CardView>
The Layout does not wrap the height of the Image as in the picture above.
What can I do?

try rows="auto" instead of rows="*"

Related

How to vertically fill space between elements in ScrollView while preserving scrolling

I am trying to achieve the following:
I tried achieving this with a GridLayout and only get one of the following two things working:
The space between the Label and the StackLayout is filled (label is
stretched), but scrolling will not work when adding more labels into
the StackLayout
Scrolling will work when more labels are added into
the StackLayout, but when only one label is showed for example, the
label before the StackLayout will not stretch
Is there any way to achieve such a thing? GridLayout is not necessarily needed, but I tried several ways and could not find any way of doing this.
example code of (1)
<Page class="page dark">
<ScrollView backgroundColor="red">
<GridLayout rows="*, auto" height="100%" backgroundColor="blue">
<Label backgroundColor="green">Top Label</Label>
<StackLayout row="1" backgroundColor="green">
<Label backgroundColor="purple">Label A</Label>
<Label backgroundColor="purple">Label B</Label>
</StackLayout>
</GridLayout>
</ScrollView>
</Page>
example code of (2):
<Page class="page dark">
<ScrollView backgroundColor="red">
<GridLayout rows="*, auto" backgroundColor="blue">
<Label backgroundColor="green">Top Label</Label>
<StackLayout row="1" backgroundColor="green">
<Label backgroundColor="purple">Label A</Label>
<Label backgroundColor="purple">Label B</Label>
</StackLayout>
</GridLayout>
</ScrollView>
</Page>
I wanted to achieve this for months and I finally found a working solution, for iOS and Android.
The solution is to use FlexboxLayout instead of GridLayout. When placed in a ScrollView, FlexboxLayout will take the whole available height if it is not tall enough. If it is taller than the ScrollView, it will be scrollable.
Here is the solution for your example:
<Page class="page dark">
<ScrollView backgroundColor="red">
<FlexboxLayout
flexDirection="column"
justifyContent="space-between"
alignItems="stretch"
backgroundColor="blue"
>
<Label backgroundColor="green">Top Label</Label>
<StackLayout backgroundColor="green">
<Label backgroundColor="purple">Label A</Label>
<Label backgroundColor="purple">Label B</Label>
</StackLayout>
</FlexboxLayout>
</ScrollView>
</Page>

How to remove separator line from listview on IOS?

I'm facing issue in removing separator line after each list item and not able to change the separator line color.
I have tried separatorColor="transparent" property in Listview tag and in CSS but both of them are not working.
I have tried SeparatorVisibility="None" property also, but no luck.
I have tried this solution provided by GitHub, but it is not working.
Here is the code :
<GridLayout row="1" class="shop-list-container">
<ListView [items]="rewardsPageData?.shops" class="list-group" height="{{rewardsPageData?.shops?.length * 75}}" separatorColor="transparent">
<ng-template let-shop="item">
<GridLayout class="shop-item list-group-item" columns="2*, 6*, 2*" rows="*, auto" (tap)="goToShopDetails(shop.id)">
<Image src="{{shop.logoImageUrl}}" class="thumb img-circle" col="0" row="0" rowSpan="2" horizontalAlignment="left"></Image>
<Label class="shop-name" [text]="shop.title" row="0" col="1"></Label>
<Label class="shop-type" text="{{shop?.category}}" row="1" col="1"></Label>
<Label text="See location" class="see-location-text" textWrap="true" col="2" row="0" rowSpan="2" horizontalAlignment="right"></Label>
</GridLayout>
</ng-template>
</ListView>
</GridLayout>
I created an isolated example with setting separator-color to transparent in CSS and works just fine for me in iOS. Notice the ListView rule in app.css.
It works equally well if you remove the CSS rule and set separatorColor="transparent" to the ListView object in home.component.html.
Add this following line in your _app-common.scss file
ListView { separator-color: transparent; }
Also, when creating a ListView, use class = "list-group"

How do I get the height of an image or GridLayout after it displays

I'm using angular. I have an image carousel. I have two gridlayouts, one on top of the other. The top grid has the naviagation buttons. I want them to be positioned in the center of the image top to bottom. I'm trying to do that based on the image height, as it will differ depending on the device size. How do I get either the view image height or the containing grid height?
<GridLayout class="template_body" rows="auto,50*,auto" columns="*" #sv>
<GridLayout row="0" #wviWrapper>
<!-- <web-view id="wvi" #wvi></web-view> -->
<template-text *ngIf="lessonDetail.instruction" [data]="lessonDetail.instruction" [version]="version" [screenwidth]="screenWidth"></template-text>
</GridLayout>
<GridLayout row="1">
<player-carousel [data]='lessondata' [itemindex]="lessonindex" (loaded)="onCarouselLoaded($event)" [clength]="carouselLength"></player-carousel>
</GridLayout>
<GridLayout row="1">
<FlexboxLayout backgroundColor="#3c495e" alignItems="center" justifyContent="space-between" verticalAlignment="top" orientation="horizontal" class="button-group" #bg>
<Button text="" appDebounceTap (debounceClick)="getItem(-1)" [debounceTime]="1000" id="backbtn" col="0" alignSelf="flex-end" class="back-btn fas" horizontalAlignment="left"></Button>
<Button text="" appDebounceTap (debounceClick)="getItem(1)" [debounceTime]="1000" id="nextbtn" col="1" alignSelf="flex-end" class="next-btn fas" horizontalAlignment="right"></Button>
</FlexboxLayout>
</GridLayout>
</GridLayout>
I'm using angular:

GridLayout in ScrollView - NativeScript

I am new to NativeScript. I have 4 images in my page and I want to achieve this.
Except I want my cover image to be of full-height of the screen. This is the code I'm trying.
<ScrollView>
<StackLayout height="100%" width="100%">
<GridLayout columns="*, *" rows="5*, *, *">
<StackLayout
:row="0"
:col="0"
:colSpan="2"
class="bgImage coverImage"
:style="{backgroundImage:`url('${defaultImg}')`}"
></StackLayout>
<StackLayout
:row="calcRow(idx)"
:col="calcCol(idx)"
:colSpan="idx==0?2:1"
v-for="(item,idx) in event.imgs"
:key="idx"
class="bgImage eventImage"
:style="{backgroundImage:`url('${item}')`}"
></StackLayout>
</GridLayout>
</StackLayout>
</ScrollView>
If I didn't give height="100%" to StackLayout it's not even loading the images. If I do give it then there is no scroll.
Here is the playground link Playground Link
You may use the layoutChanged event of your ScrollView where you may calculate the height of it and use it as height for the first image.
Playground Sample

How to position element at the bottom of Absolute Layout in NativeScript?

I want to position an element at the bottom of the screen in Absolute Layout in NativeScript.
I have this code:
<AbsoluteLayout>
<maps:mapView
left="0"
top="0"
width="100%"
height="100%"
latitude="{{ map.latitude }}"
longitude="{{ map.longitude }}"
zoom="{{ map.zoom }}"
padding="{{ map.padding }}"
mapReady="onMapReady"
coordinateTapped="onCoordinateTapped"
markerSelect="onMarkerSelect"
shapeSelect="onShapeSelect"
cameraChanged="onMapCameraChanged"/>
<ScrollView
left="0"
top="0"
width="100%"
orientation="horizontal">
<!-- More XML -->
</ScrollView>
<StackLayout
left="0"
bottom="0"
width="100%"
visibility="visible"
orientation="horizontal"
style="background-color: red;">
<Label text="TITLE"></Label>
</StackLayout>
</AbsoluteLayout>
I figured out that there is no bottom attribute for AbsoluteLayout... Here is the picture of what I want to create:
So how to arange items like in the picture, especially the bottom one?
EDIT: I should note that dimensions of this bottom rectangle may not be always same....
I did something similar one day, programmatically & with Angular, maybe this can help.
If you don't want to use a GridLayout you can try to get height of your bottom element and of the screen, then place your element from the top with a simple calcul : screen's height - bottom element's height (- more if you want some padding). You can use two types of values : DIPs and pixels. If you're using pixels, you need to convert your values into DIPs by using the screen scale.
Something like this (I didn't test the code I'm giving you, it's just an example) :
1] add an id to your bottom element so you can access it inside your component :
<StackLayout #bottomElt></StackLayout>
2] update your component to set element position inside your absolute layout
// you need ElementRef, OnInit and ViewChild
import { Component, ElementRef, OnInit, ViewChild, ViewContainerRef } from "#angular/core";
import { AbsoluteLayout } from "ui/layouts/absolute-layout";
import { StackLayout } from "ui/layouts/stack-layout";
// you need access to screen properties
import { screen } from "tns-core-modules/platform";
[...]
export class YourComponent implements OnInit {
// add access to element inside your component
#ViewChild("bottomElt") bottomElt: ElementRef;
// create variable to access bottom element properties
bottomContainer: StackLayout;
// set bottom element position after view init
// example : inside ngOnInit function (for Angular version)
ngOnInit(): void {
this.bottomContainer = <StackLayout>this.bottomElt.nativeElement;
// using DIPs values only
AbsoluteLayout.setTop(this.bottomContainer, (screen.mainScreen.heightDIPs - Number(this.bottomContainer.height)));
// using pixels and screen scale
// this way you can get height without knowing it
AbsoluteLayout.setTop(this.bottomContainer, (screen.mainScreen.heightDIPs - (Number(this.bottomContainer.getMeasuredHeight()) / screen.mainScreen.scale)));
}
More information about screen values : https://docs.nativescript.org/api-reference/interfaces/platform.screenmetrics.html
Alternative way
Instead of using AbsoluteLayout, you can use a GridLayout to set a bottom bar, with two rows : one with a wildcard size and the other with auto size so it can fit your bottom bar height everytime it changes. I did it this way in a mobile application to get a menu at the bottom in Android and IOS :
<GridLayout rows="*, auto" width="100%">
<AbsoluteLayout row="0" orientation="vertical">
<!-- YOUR CONTENT (maps & ScrollView) -->
</AbsoluteLayout>
<!-- YOUR BOTTOM BAR (StackLayout). Don't forget to add row="1" -->
<StackLayout #bottomElt row="1">[...]</StackLayout>
</GridLayout>
Another option is using FlexboxLayout in your AbsoluteLayout container like this:
<FlexboxLayout flexDirection="column" justifyContent="space-between" height="100%">
<ScrollView
width="100%"
orientation="horizontal">
<!-- More XML -->
</ScrollView>
<StackLayout
width="100%"
visibility="visible"
orientation="horizontal"
style="background-color: red;">
<Label text="TITLE"></Label>
</StackLayout>
</FlexboxLayout>
This is the absolute best solution, got it from one of the devs: https://github.com/NativeScript/NativeScript/issues/5591#issuecomment-482640921
<GridLayout rows="*,auto">
<ItemTakingFullScreen rowSpan="2"/>
<ItemShownUnder row="1"/>
<ItemShownAbove row="1">
</GridLayout>
Basically, you can use grid layout and have a item take up multiple grid spaces, sharing them with some other item.
here is the best solution
wrapper all the elements in an absolutelayout with width and hieght to 100% and maybe add a gridlayout to hold the main content .
<AbsoluteLayout width='100%' height='100%'>
<StackLayout width='100%' hieght='100%' left='0' top='0'>
//add you structure here
</StackLayout>
add your fixed element here
<image src='add the float item'/>
</AbsoluteLayout>
It can be done also with GridLayout:
<GridLayout rows="16,*,16" columns="16,*,16" width="100%" backgroundColor="red">
<GridLayout row="1" col="1" rows="auto, auto, auto" columns="auto" horizontalAlignment="right" verticalAlignment="bottom" backgroundColor="blue">
<!-- Your content at bottom right corner -->
<Label row="0" text="Your content" textAlignment="center" textWrap="true"></Label>
<Label row="1" text="at" textAlignment="center" textWrap="true"></Label>
<Label row="2" text="bottom right corner" textAlignment="center"></Label>
</GridLayout>
</GridLayout>
This is the easy way
<DockLayout backgroundColor="lightgray" stretchLastChild="true">
<Label text="top" dock="top" height="60" backgroundColor="green">
</Label>
<Label text="bottom" dock="bottom" height="60" backgroundColor="yellow"></Label>
<Label text="center" backgroundColor="red"></Label>
</DockLayout>
This is what you want!
<DockLayout backgroundColor="lightgray" stretchLastChild="false">
<Label text="top" dock="top" height="60" backgroundColor="green">
</Label>
<Label text="bottom" dock="bottom" height="60" backgroundColor="yellow"></Label>
</DockLayout>

Resources