Move UI element with keyboard nativescript (IOS) - nativescript

On my nativescript app - I have a button at the bottom of a screen. On the screen there is a Text area. When the user taps in the Text Area, a virtual keyboard appears. At this point, I want the button at the bottom to move up and appear just on top of the virtual keyboard. Any suggestions on how I can achieve this in both android and iOS?
Code
<GridLayout>
<ActionBar title="" backgroundColor="#f82462" top="0" left="0">
<NavigationButton (tap)="goBack()"></NavigationButton>
</ActionBar>
<GridLayout rows="*, auto">
<GridLayout row ='0' rows="auto *" columns="">
<GridLayout row="0" rows="" columns="">
<Button text="Top Button" (tap)="goNext()"></Button>
</GridLayout>
<GridLayout row="1" backgroundColor="#f82462">
<TextView [(ngModel)]="xyz" class="input" hint="Write your question as a complete sentence.Click on camera to add images if required." returnkeyType="done" id="questionText"></TextView>
</GridLayout>
</GridLayout>
<StackLayout row='1'>
<Button text="Next" (tap)="goNext()"></Button>
</StackLayout>
</GridLayout>

I am not able to test this right now, but have you tried to wrap everything inside the main GridLayout in <ScrollView> ... </ScrollView>

I also encountered this problem for my instant chat application, here is the solution : https://gist.github.com/elvticc/0c789d08d57b1f4d9273f7d93a7083ec
// Also use IQKeyboardManager to customize the iOS keyboard
// See https://github.com/tjvantoll/nativescript-IQKeyboardManager
// let iqKeyboard: IQKeyboardManager = IQKeyboardManager.sharedManager();
// iqKeyboard.toolbarDoneBarButtonItemText = "OK";
// iqKeyboard.canAdjustAdditionalSafeAreaInsets = true;
// iqKeyboard.shouldFixInteractivePopGestureRecognizer = true;
// Angular
[...]
import { OnInit, OnDestroy, ElementRef, ViewChild } from "#angular/core";
[...]
// NativeScript
[...]
import { ios as iosApp } from "tns-core-modules/application/application";
[...]
#ViewChild("element") private _element: ElementRef<StackLayout>; // FlexboxLayout, GridLayout, etc.
private _keyboardHeight: number = 0;
private _elementHeight: number = 0;
private _observerIDs: Array<object> = new Array();
// Start events when the component is ready
ngOnInit(): void {
// iOS keyboard events
if (iosApp) {
let eventNames: Array<string> = [
UIKeyboardWillShowNotification,
UIKeyboardDidShowNotification,
UIKeyboardWillHideNotification
];
// Catch the keyboard height before it appears
this._observerIDs.push({
event: eventNames[0],
id: iosApp.addNotificationObserver(eventNames[0], (event) => {
let currHeight: number = this._keyboardHeight,
newHeight: number = event.userInfo.valueForKey(UIKeyboardFrameEndUserInfoKey).CGRectValue.size.height;
if (currHeight != newHeight) {
this._keyboardHeight = newHeight;
}
})
});
// Position the element according to the height of the keyboard
this._observerIDs.push({
event: eventNames[1],
id: iosApp.addNotificationObserver(eventNames[1], (event) => {
if (this._elementHeight == 0) {
this._elementHeight = this._element.nativeElement.getActualSize().height;
}
this._element.nativeElement.height = this._keyboardHeight + this._elementHeight;
})
});
// Reposition the element according to its starting height
this._observerIDs.push({
event: eventNames[2],
id: iosApp.addNotificationObserver(eventNames[2], () => {
this._element.nativeElement.height = this._elementHeight; // or "auto";
})
});
}
}
// Stop events to avoid a memory leak
ngOnDestroy(): void {
if (iosApp) {
let index: number = 0;
for (index; index < this._observerIDs.length; index++) {
let observerId: number = this._observerIDs[index]['id'],
eventName: string = this._observerIDs[index]['event'];
iosApp.removeNotificationObserver(observerId, eventName);
}
}
}
Marcel Ploch's original : https://gist.github.com/marcel-ploch/bf914dd62355049a0e5efb4885ca4c6e

Related

Open popup at specific position - Xamarin.Forms

I have a xamarin.forms app which uses Rg.Plugins.Popup. I have a list view with an "edit" icon on each view cell. I am opening the popup when we click on this icon. The problem is the popup will open at the center of screen no matter what where the listview cells position. How can we open the popup exactly at the position of listview cell which being clicked? Is it possible? Any help is appreciated.
For now, Rg.Plugins.Popup support the Animations for Right, Left, Center, Top, Bottom.
For more details of Animations, you could check the link below. https://github.com/rotorgames/Rg.Plugins.Popup/wiki/Animations#custom-animations
Applying a custom animations in a xaml file:
class UserAnimation : MoveAnimation
{
private double _defaultTranslationY;
public UserAnimation()
{
DurationIn = DurationOut = 300;
EasingIn = Easing.SinOut;
EasingOut = Easing.SinIn;
PositionIn = MoveAnimationOptions.Right;
PositionOut = MoveAnimationOptions.Right;
}
public override void Preparing(View content, PopupPage page)
{
base.Preparing(content, page);
page.IsVisible = false;
if (content == null) return;
_defaultTranslationY = content.TranslationY;
}
public override void Disposing(View content, PopupPage page)
{
base.Disposing(content, page);
page.IsVisible = true;
if (content == null) return;
content.TranslationY = _defaultTranslationY;
}
public async override Task Appearing(View content, PopupPage page)
{
var taskList = new List<Task>();
taskList.Add(base.Appearing(content, page));
if (content != null)
{
var topOffset = GetTopOffset(content, page);
var leftOffset = GetLeftOffset(content, page);
taskList.Add(content.TranslateTo(content.Width, _defaultTranslationY, DurationIn, EasingIn));
};
page.IsVisible = true;
await Task.WhenAll(taskList);
}
public async override Task Disappearing(View content, PopupPage page)
{
var taskList = new List<Task>();
taskList.Add(base.Disappearing(content, page));
if (content != null)
{
_defaultTranslationY = content.TranslationX - content.Width;
var topOffset = GetTopOffset(content, page);
var leftOffset = GetLeftOffset(content, page);
taskList.Add(content.TranslateTo(leftOffset, _defaultTranslationY, DurationOut, EasingOut));
};
await Task.WhenAll(taskList);
}
}
Usage:
<pages:PopupPage.Animation>
<animations:UserAnimation />
</pages:PopupPage.Animation>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center">
<Frame BackgroundColor="Silver">
<StackLayout Spacing="20">
<Label
FontSize="16"
HorizontalOptions="Center"
Text="User Animation" />
<Button Clicked="OnClose" Text="Close" />
</StackLayout>
</Frame>
</StackLayout>
OnClose event:
private void OnClose(object sender, EventArgs e)
{
PopupNavigation.Instance.PopAsync();
}
If you wannna Left, Top, Botton, Center for the Popup, you could change the MoveAnimationOptions of your custom animations.
PositionIn = MoveAnimationOptions.Right;
PositionOut = MoveAnimationOptions.Right;

Nativescript RadListView: How to change background color programmatically on selected item?

Doing this programmatically in the code behind (not binding to XML), everything works except my background color does not show when the item is selected. No errors and my images show up fine. What's messed up in my code? (Thanks!)
I'm running {N} CLI "tns-android":"version": "3.4.2", "nativescript-ui-listview": "3.5.1".
<GridLayout columns="auto, *">
<StackLayout col="0" id="containers">
</StackLayout>
<StackLayout col="1" id="notes">
</StackLayout>
</GridLayout>
function setContainers() {
myCntnrPics.forEach(function (element) { // Need to it in ObservableArray
pathToCntnrBitmapsArray.push(element);
});
let radListLeft = new listview.RadListView();
// iOS NEEDS height set
radListLeft.height = 2000;
radListLeft.items = pathToCntnrBitmapsArray;
let gridLayout = new listview.ListViewGridLayout();
gridLayout.scrollDirection = "Vertical";
radListLeft.selectionBehavior = "Press";
radListLeft.itemSelected = "selected";
radListLeft.on("itemSelected", function (ListViewEventData) {
const item = pathToCntnrBitmapsArray.getItem(args.index);
item.selected = true;
console.log("itemSelected is true -----"); // This does not show up
});
radListLeft.on("itemTap", function (element){
var selectedItemPath = pathToCntnrBitmapsArray.getItem(element.index).toString();
console.log("on itemTap selectedItemPath: " + selectedItemPath); // shows correctly
});
if (application.android) {
// Android needs H & W
radListLeft.itemTemplate = "<StackLayout backgroundColor='{{ selected ? 'red' : 'white'}}'><Image src='{{ $value }}' height='123' width='123' loadMode='async' class='items'/></StackLayout>";
} else if (application.ios) {
radListLeft.itemTemplate = "<StackLayout backgroundColor='{{ selected ? 'red' : 'white'}}'><Image src='{{ $value }}' class='items'/></StackLayout>";
gridLayout.itemHeight = 123;
gridLayout.itemWidth = 123;
}
gridLayout.spanCount = 1;
radListLeft.listViewLayout = gridLayout;
let myContainers = page.getViewById("containers");
myContainers.removeChildren();
myContainers.addChild(radListLeft);
}

Xamarin Forms Auto adjust elements in ScrollView

Xamarin Forms Version : 2.5
Complete and partial scroll look like below :
https://imgur.com/a/kvpBZaE
Height of ScrollView is 150 , and the height of each item in the scroll view is 50.
Requirement:
At any point in time the scroll always has to be in the complete scrolled position ie The vertical Scroll has to display only 3 items and the center item to be highlighted. If and when the items are scrolled and the number of items is anything other than 3(Partial Scroll) then the display has to auto adjust to three items.
Problem : There is no event on the scrollview which can be used to auto adjust the scroll.
Code
<ScrollView
x:Name="ListScrollViewer"
HorizontalOptions="FillAndExpand"
Orientation="Vertical">
<StackLayout x:Name="ItemsLayout" Spacing="0" />
</ScrollView>
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(VerticalScrollView), null, BindingMode.Default, null, null);
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set
{
SetValue(ItemsSourceProperty, value);
var i = 0;
foreach (var item in value)
{
ItemsLayout.Children.Add(new TextView(item, i));
var tapGestureRecognizer = new TapGestureRecognizer()
{
CommandParameter = i,
Command = new Command<object>(OnTap),
NumberOfTapsRequired = 1,
};
ItemsLayout.Children.Last().GestureRecognizers.Add(tapGestureRecognizer);
i++;
}
}
}
Scroll View Usage:
<local:VerticalScrollView
x:Name="VerticalScroll"
BackgroundColor="Black"
HeightRequest="150"
HorizontalOptions="Center"
VerticalOptions="Center" />
Code Behind
public List<object> Options { get; set; } = new List<object>();
Options.Add(new { Name = "Val 1" });
Options.Add(new { Name = "Val 2" });
Options.Add(new { Name = "Val 3" });
Options.Add(new { Name = "Val 4" });
Options.Add(new { Name = "Val 5" });
VerticalScroll.ItemsSource = Options;

Nativescript ListView scrollToIndex with animation on android

I am developing a chat app, when list loaded and when a new item added to list I need to scroll to bottom of list. I can do that with this.
scrollToBottom() {
let lv = <ListView>frame.topmost().getViewById('messageList');
lv.scrollToIndex(this.store.items.getValue().length - 1)
}
But it showing bottom of list instant
There is a guide to do that on IOS but not on Android
private srollListView(position: number) {
if (this._listView.ios) {
this._listView.ios.scrollToRowAtIndexPathAtScrollPositionAnimated(
NSIndexPath.indexPathForItemInSection(position, 0),
UITableViewScrollPosition.UITableViewScrollPositionTop,
true
);
}
else {
this._listView.scrollToIndex(position);
}
}
link to guide: http://nuvious.com/Blog/2016/4/4/how-to-make-the-nativescript-listview-scrolltoindex-animated-on-ios
Is there any way to do that on Android?
You could use smoothScrollToPosition android method, which provides smooth scroll for the ListView, which you need. I am providing sample code.
main-page.xml
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo">
<GridLayout>
<ListView items="{{ source }}" id="lvid" loaded="onLoaded" itemLoading="onItemLoading" itemTap="onItemTap">
<ListView.itemTemplate>
<StackLayout>
<Label text="{{title}}" textWrap="true" />
</StackLayout>
</ListView.itemTemplate>
</ListView>
</GridLayout>
</Page>
main-page.ts
import { EventData } from 'data/observable';
import { Page } from 'ui/page';
import { HelloWorldModel } from './main-view-model';
import {ListView} from "ui/list-view"
// Event handler for Page "navigatingTo" event attached in main-page.xml
export function navigatingTo(args: EventData) {
let page = <Page>args.object;
var array=[];
for(var i=0;i<100;i++){
array.push({title:"title"+i});
}
page.bindingContext = {source:array};
setTimeout(function(){
var listview:ListView =<ListView> page.getViewById("lvid");
listview.android.smoothScrollToPosition(60);
}, 4000)
}
Just to let everyone who are still looking for this know, they have added the scrollToIndexAnimated method to ListView since v4.2.0
https://github.com/NativeScript/NativeScript/blob/master/CHANGELOG.md#420-2018-08-08

Make a NativeScript ListView Transparent on iOS

I’m trying to get a NativeScript <ListView> to be transparent on iOS and I’m failing. I found an old thread on the topic at https://groups.google.com/forum/#!topic/nativescript/-MIWcQo-l6k, but when I try the solution it doesn’t work for me. Here’s my complete code:
/* app.css */
Page { background-color: black; }
<!-- main-page.xml -->
<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="loaded">
<ListView id="list-view" items="{{ items }}" itemLoading="itemLoading">
<ListView.itemTemplate>
<Label text="{{ name }}" />
</ListView.itemTemplate>
</ListView>
</Page>
// main-page.js
var ios = require("utils/utils");
var Observable = require("data/observable").Observable;
var ObservableArray = require("data/observable-array").ObservableArray;
var page;
var items = new ObservableArray([]);
var pageData = new Observable();
exports.loaded = function(args) {
page = args.object;
page.bindingContext = pageData;
// Toss a few numbers in the list for testing
items.push({ name: "1" });
items.push({ name: "2" });
items.push({ name: "3" });
pageData.set("items", items);
};
exports.itemLoading = function(args) {
var cell = args.ios;
if (cell) {
// Use ios.getter for iOS 9/10 API compatibility
cell.backgroundColor = ios.getter(UIColor.clearColor);
}
}
Any help would be appreciated. Thanks!
Don't forget to set the listview to transparent, seems to have a backgroundcolor itself
ListView{
background-color: transparent;
}
Currently with NativeScript 2.4 the following works
var cell = args.ios;
if (cell) {
cell.selectionStyle = UITableViewCellSelectionStyleNone
}
And if you want to change the selection highlight color here is a simple approach, I have not tested performance but it works okay on an iPhone 6.
import { Color } from 'color';
cell.selectedBackgroundView = UIView.alloc().initWithFrame(CGRectMake(0, 0, 0, 0));
let blue = new Color('#3489db');
cell.selectedBackgroundView.backgroundColor = blue.ios
Not sure if there are better ways to do this, but this is what worked for me with NativeScript 2.4 on iOS to both A) make the ListView background transparent, and B) change the color when an item is tapped:
let lvItemLoading = (args) => {
let cell = args.ios;
if (cell) {
// Make the iOS listview background transparent
cell.backgroundColor = ios.getter(cell, UIColor.clearColor);
// Create new background view for selected state
let bgSelectedView = UIView.alloc().init();
bgSelectedView.backgroundColor = new Color("#777777").ios;
bgSelectedView.layer.masksToBounds = true;
cell.selectedBackgroundView = bgSelectedView;
}
};

Resources