How to position element at the bottom of Absolute Layout in NativeScript? - 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>

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:

iOS buttons become unclickable after making a floating layout

I'm creating a custom action bar and my idea is that the actionbar must be transparent (thats why I can't/don't want to use the native one) and behind the action bar there must be content (an image in this case).
I tried multiple ways and all of them with the same result, the iOS button become unclickable for no reason.
Ok, so I have an AbsoluteLayout inside I have GridLayout fixed on top as the custom actionbar and then a ScrollView with 100% width and height, inside the scrollview there's some content. The problem is as soon as I put a button inside the GridLayout this become unclickable only on iOS because in Android works just fine.
Let me show you my example with some code:
<Page actionBarHidden="true">
<AbsoluteLayout>
<GridLayout rows="50, *" backgroundColor="red" top="0" left="0" height="50" id="bar">
<Button text="click" #tap="goToDetailPage" id="buttondelsous"></Button>
</GridLayout>
<ScrollView backgroundColor="blue" width="100%" height="100%" top="0" id="content">
<WrapLayout>
<StackLayout>
<Label text="this is behind"></Label>
</StackLayout>
</WrapLayout>
</ScrollView>
</AbsoluteLayout>
</Page>
As for the styles forcing the custom actionbar being in front of the scrollview I have:
#bar {
z-index: 5;
width: 100%;
}
#content {
z-index: 2;
}
This looks like this on Android:
Same way on iOS but the button "Click" is not working, like if it was behind something..
Any idea on how to fix this or any other approach to get what I need? Remember behind the action I must be able to place content (like a background image that I don't want to place in the Page tag itself but in another layout)
Thanks!
A wise man once told me that Nativescript stacks elements in the order you list them. Try flipping around the order so the button is listed last in the template. I believe you won't even need the z-index line if you flip the order around.
should look like
<Page actionBarHidden="true">
<AbsoluteLayout>
<ScrollView backgroundColor="blue" width="100%" height="100%" top="0" id="content">
<WrapLayout>
<StackLayout>
<Label text="this is behind"></Label>
</StackLayout>
</WrapLayout>
</ScrollView>
<!-- I'm listed second so I will be on top even though I have row="0" -->
<GridLayout rows="50, *" backgroundColor="red" top="0" left="0" height="50" id="bar">
<Button text="click" #tap="goToDetailPage" id="buttondelsous"/>
</GridLayout>
</AbsoluteLayout>
</Page>

StackLayout's vertical alignment

I'm trying to put a button at the bottom of a StackLayout and it's not working.
I don't know what I'm doing wrong!
Here's my template:
<Page actionBarHidden="true">
<GridLayout rows="*, *, *, *" columns="*">
<StackLayout horizontalAlignment="center"
verticalAlignment="center">
<Label text="My Account" id="login-label"/>
</StackLayout>
<CardView row="1" class="cardStyle" margin="10"
elevation="40"
radius="4"
verticalAlignment="center"
horizontalAlignment="center"
rowSpan="2"
id="login-box">
<StackLayout id="form-container">
<TextField hint="Login"/>
<TextField hint="Password"/>
<Label text="Forgot password?" horizontalAlignment="right"/>
<Button text="Button" #tap="loginButton()" verticalAlignment="bottom"/>
</StackLayout>
</CardView>
</GridLayout>
</Page>
Thank you!
StackLayout do not support that by its design. It is used to just stack the child elements one after another in given orientation, you can't have mixed output - few child elements at top and few at bottom or center.
Use Grid / Dock layout in order to dock an element at bottom.

Resources