Appcelerator alloy views slide - animation

I am using the Alloy MVC framework over Titanium and want to make a slideshow between views. When I swipe on the screen, I want to display the next/previous view with a slide effect from right to left or left to right.
I am using this code:
A tab in my index.xml:
<Tab title="Bilan" icon="KS_nav_ui.png">
<Window title="Bilan" id="bilanTab" onSwipe="doBilanSwipe">
</Window>
</Tab>
The question view dynamically added and filled inside bilanTab:
<Alloy>
<Collection src="ReponsePossible">
<View id="questionContainer" class="container">
<Label id="questionText" />
<Button id="buttonNextQuestion">Question suivante</Button>
</View>
</Alloy>
and my two functions (3 with prevQuestion not printed here) inside index.js controller:
var previousQuestion;
var nextQuestion;
function doBilanSwipe(e){
if (e.direction == 'left'){
nextQuestion();
}
else if (e.direction == 'right'){
prevQuestion();
}
}
function nextQuestion(){
if (questionsCurrentIndex < questions.length-1){
questionsCurrentIndex++;
$.previous = previousQuestion;
$.next = Alloy.createController('question', questions.at(questionsCurrentIndex));
nextQuestion = $.next;
$.next.questionContainer.left = 320;
$.bilanTab.add($.next.questionContainer);
$.next.questionContainer.animate({left:0, duration:200});
$.previous.questionContainer.animate({left:-320, duration:200},function(){
$.previous = previousQuestion;
$.next = nextQuestion;
$.bilanTab.remove($.previous.questionContainer);
previousQuestion = $.next;
$.previous.destroy();
});
}
}
My problem is that first animation (first view moving to the left) is ok but after that, the next view just appear without any animation.
Could someone help? Thanks!

There is already the Titanium.UI.ScrollableView that does this exact thing, for all platforms.
Use it in Alloy like this:
<Alloy>
<Window id="win">
<ScrollableView id="scrollableView" showPagingControl="true">
<View id="view1" backgroundColor="#123" />
<View id="view2" backgroundColor="#246" />
<View id="view3" backgroundColor="#48b" />
</ScrollableView>
</Window>
</Alloy>
You can dynamically add views to it inside the controller like this:
$.scrollableView.addView(Ti.UI.createView({ // your custom attributes here});

Related

NativeScript binding breaks UI

I have setup a custom tab view defined as the following :
main.xml
<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="loaded"
xmlns:t1="partial-views/explore"
xmlns:t2="partial-views/community">
<!--ACTION BAR-->
<ActionBar title="Haloose">...</ActionBar>
<StackLayout>
<!-- TABS -->
<StackLayout id="sl_main">
<t1:explore id="tab_explore" visibility="{{ currentActive == 'explore' ? 'visible' : 'collapsed' }}" />
<t2:community id="tab_community" visibility="{{ currentActive == 'community' ? 'visible' : 'collapsed' }}"/>
</StackLayout>
<-- FIXED MENU -->
<GridLayout id="menu">
<Image tap="changeVisibleTab"/>
<Image tap="changeVisibleTab" />
</GridLayout>
</StackLayout>
</Page>
Let's call this file main.xml . It's associated to a main.js where I've defined a binding context:
main.js
exports.loaded = function(args){
page = args.object;
//Set Up page view model
mainObservable = new Observable({
currentActive:"explore",
menuItemsArray:[
new MenuItem("explore"),
new MenuItem("community")
]
});
//Bind page to view model
page.bindingContext = mainObservable;
}
For each tab I have a folder containing a js , css and xml file.
A sample tab.xml file would look like this :
tab.xml
<StackLayout loaded="tabLoaded" > <looots of stuff /> </StackLayout>
Everything works as expected, however if I try to bind the stack layout to an object , all of the UI elements are hidden.
If I remove binding, I can see them again.
not working tab.js
var Observable = require("data/observable").Observable;
var profile;
exports.tabLoaded = function(args){
profile = args.object;
var profileBinding = {
username : "Aaron Ullal"
}
profile.bindingContext = profileBinding; //removing this line makes elements visible
}
What is causing this? Perhaps multi level binding is not supported?
When you use custom XML components, like your tabs, and add bindings to them (in your case the visibility binding, those bindings are basically applied to the root tag in your XML component. So when you change the binding context in your tab.js the visibility binding starts looking for a currentActive property in profileBinding. In order to achieve what you want you have to wrap your tab XML in another layout, like this:
<StackLayout>
<StackLayout loaded="tabLoaded" >
<!--looots of stuff -->
</StackLayout>
</StackLayout>
It should work as expected then.

NativeScript: how to make view 'transparent' for any user interactions

Is there a way to make a view 'transparent' to any user interactions? For example I have a view (with transparent background) and a button under that view. I want the user could tap the button under that view. If I have a scroller view under that view I want the user interacts with scroller when scroll over that view, so the view doesn't interfere or intercept user's gestures. But only this view should be transparent to user's interactions not its children. So, if I have a button inside that view it behaves normally.
Example XML:
<AbsoluteLayout width="100%" height="100%">
<Button text="Button1" tap="onTap1" />
<GridLayout width="100%" height="100%" backgroundColor="transparent">
<Button text="Button2" tap="onTap2" horizontalAlignment="center" verticalAlignment="center"/>
</GridLayout>
</AbsoluteLayout>
Thank you for your help.
You have multiple approaches to make a view change its color in NativeScript.
For example you can directly change its backgroundColor. Another oiption is to use animation and third option is to use CSS-animation.
Here is a basic example for the first two options.
page.xml
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo">
<StackLayout>
<GridLayout width="300" height="300" id="myGrid" backgroundColor="transparent">
</GridLayout>
<Button text="Tap me" tap="onTap" />
<Button text="Or Tap me" tap="onAnotherTap" />
</StackLayout>
</Page>
page.js
import { EventData } from "data/observable";
import { Page } from "ui/page";
import { HelloWorldModel } from "./main-view-model";
import { GridLayout } from "ui/layouts/grid-layout";
import { Color } from "color";
var myGridView;
export function navigatingTo(args: EventData) {
var page = <Page>args.object;
page.bindingContext = new HelloWorldModel();
// get refference to the view using its id
myGridView = <GridLayout>page.getViewById("myGrid");
}
export function onTap(args:EventData) {
var color = new Color("#FF0000");
myGridView.backgroundColor = color;
}
export function onAnotherTap(args:EventData) {
myGridView.animate({
backgroundColor: new Color("#3D5AFE"),
duration: 3000
});
}
All of the options can be found described in NativeScript documenation

Nativescript -Open SideDrawer on button click

I am using telerik ui for native script. I need a toggle button at top to open the side menu. but I am not able to call the Showdrawer() as per the docs.
What I need is on button click side menu should open. I tried calling RadSideDrawer.prototype.showDrawer(), but failed.
Is there any other side menu available for Nativescript?
main-page.xml
<Page xmlns="http://www.nativescript.org/tns.xsd" xmlns:drawer="nativescript-telerik-ui/sidedrawer" loaded="pageLoaded">
<Page.actionBar>
<ActionBar>
<android>
<NavigationButton text="Go Back" android.systemIcon="ic_menu_moreoverflow" tap="showSideDrawer" />
</android>
</ActionBar>
</Page.actionBar>
<drawer:RadSideDrawer>
<drawer:RadSideDrawer.mainContent>
<StackLayout>
<Label text="{{ mainContentText }}" textWrap="true" />
</StackLayout>
</drawer:RadSideDrawer.mainContent>
<drawer:RadSideDrawer.drawerContent>
<StackLayout cssClass="drawerContent" style="background-color:white;">
<StackLayout cssClass="headerContent">
<Label text="Header" />
</StackLayout>
<StackLayout cssClass="drawerMenuContent">
<Label text="Item 1" style="color:black;" />
<Label text="Item 2" style="color:black;" />
<Label text="Item 3" style="color:black;" />
<Label text="Item 4" style="color:black;" />
</StackLayout>
</StackLayout>
</drawer:RadSideDrawer.drawerContent>
</drawer:RadSideDrawer>
</Page>
getting-started-model.js
var observableModule = require("data/observable");
var GettingStartedViewModel = (function (_super) {
__extends(GettingStartedViewModel, _super);
function GettingStartedViewModel() {
_super.call(this);
this.set("mainContentText", "SideDrawer for NativeScript can be easily setup in the XML definition of your page by defining main- and drawer-content. The component"
+ " has a default transition and position and also exposes notifications related to changes in its state.");
}
return GettingStartedViewModel;
})(observableModule.Observable);
exports.GettingStartedViewModel = GettingStartedViewModel;
function showSideDrawer(args) {
console.log("Show SideDrawer tapped.");
// Show sidedrawer ...
_super.prototype.showDrawer.call(this);
}
exports.showSideDrawer = showSideDrawer;
main page.js
var viewModelModule = require("./getting-started-model");
function pageLoaded(args) {
console.log("Page loaded");
var page = args.object;
page.bindingContext = new viewModelModule.GettingStartedViewModel();
}
exports.pageLoaded = pageLoaded;
You can take a look at this SDK examples that show the main functionality of the RadSideDrawer. As mentioned by #R Pelzer all you need to do is get the instance of the RadSideDrawer for example by using its id:
import drawerModule = require("nativescript-telerik-ui-pro/sidedrawer");
import frameModule = require("ui/frame");
var sideDrawer: drawerModule.RadSideDrawer = <drawerModule.RadSideDrawer>(frameModule.topmost().getViewById("sideDrawer"));
and call its showDrawer() method:
sideDrawer.showDrawer();
Are you calling the showSideDrawer function from code you didn't post? Are you sure you linked the tap button?
<Button tap="showSideDrawer" text="ToggleDrawer"/>
Maybe you can try to give the sideDrawer an Id and use this code.
var drawer = frameModule.topmost().getViewById("sideDrawer");
drawer.showDrawer();
You are getting undefined because no id was assigned to the drawer so to fix your problem assign an id to the sideDrawer <drawer:RadSideDrawer id="sideDrawer"> then you can call
var frame = require('ui/frame');
var drawer = frame.topmost().getViewById("sideDrawer");
function showSideDrawer(){
drawer.showDrawer(); // i prefer using .toggleDrawerState();
};
In my case, I missed inserting , as a result, there was a missing component when toggleDrawer was being called hence the error "TypeError: Cannot read property 'toggleDrawerState' of undefined".
Try inserting all the body component of the xml file in this might solve the issue.
Happy coding :))

Appcelerator Titanium View Proxy Memory Management

I've been struggling on how to handle memory allocations on my Titanium app (built for android [9.4MB], ios[2.8MB] and MobileWeb[5.4MB].
I have created a widget which will be use to open views from a menu selection.
<Alloy>
<Window id="mainWindow">
<View id="menuView">
<View id="vTop">
<!-- Header goes here -->
<TableView id="menuTable" />
</View>
<!-- footer message goes here -->
</View>
<View id="contentView">
<View id="nav">
<Label id="winTitle"/>
<View id='leftButtonView'>
<Label id="icoLeftButton" />
</View>
<View id='backButtonTitleView'>
<Label id="icoBack" />
<Label id="backButtonTitle" />
</View>
<View id='rightButtonView'>
<Label id="icoRightButton" />
</View>
</View>
<View id="mainView" layout="composite" />
</View>
</Window>
</Alloy>
Sample usage of this widget:
I was able to reduce the memory allocations of views by following this solution. I've applied this every time I open another view from menu selection.
(controller of my widget)
var memPool = Ti.UI.createWindow();
memPool.open();
memPool.hide();
memPool.close();
// Open view from menu selection
function openView(e) {
var cbAdd = function() {
var ctrl = Alloy.createController(e["url"]);
if (ctrl != null) {
setWindowTitle(e["wTitle"]);
setRightIco(ctrl.getRightIco()); //setting right label icon (IcoMoon font)
$.setRightNavClick(ctrl.getRightNavCb());
//Have to passed navGroup to be able to open other views
ctrl.winMain.navGroup = $;
//Close splash screen after layout
ctrl.winMain.addEventListener("postlayout", postLayout);
$.mainView.add(ctrl.winMain);
ctrl.init();
ctrl = null;
}
};
var cbRemove = function() {
//Remove all children from mainView; fn [commonjs]
fn.removeChildren($.mainView, cbAdd);
};
if ($.mainView.children.length > 0) {
cleanUp($.mainView.children[0]);
cbRemove();
} else
cbAdd();
}
function cleanUp(obj, cb) {
memPool.open();
memPool.hide();
memPool.setZIndex(-1);
memPool.add(obj);
memPool.close();
obj = null;
if (cb != null) cb();
}
Checking the result on XCode Instruments:
TiUIView is always reduced everytime I open other views from menu but TiUIViewProxy doesn't.
Sample View to be open: (consist of widgets)
<Alloy>
<View id="winMain">
<ScrollView id="form" layout="vertical">
<Widget id="fperiod" src="ph.com.test.controls.period" top="10" />
<Widget id="ftermid" src="ph.com.test.controls.key" label="Terminal No." top="20" />
<Widget id="fofficeid" src="ph.com.test.controls.combobox" label="Branch" />
<View id="btnReset" width="100%" height="50" layout="horizontal" top="20" bottom="20" backgroundColor="#B40026">
<Label class="resetFilter" />
<Label class="lblReset" />
</View>
</ScrollView>
</View>
</Alloy>
The following are the references that help alot:
http://developer.appcelerator.com/question/116867/this-is-a-solution-to-your-memory-woes#answer-203729
http://www.tidev.io/2014/03/27/memory-management/
http://developer.appcelerator.com/question/152656/tips-and-tricks-for-memory-management
http://developer.appcelerator.com/question/140852/quick-memory-question--tiuiview-vs-tiuiviewproxy
http://developer.appcelerator.com/question/151989/should-i-null-objects-to-release-memory-in-alloy
How to reduce the memory allocations of TiUIViewProxy? Do I have to also cleanup the views of widgets from its controller?
I have tried to cleanup the views of my widgets from its controller. Then, the TiUIViewProxy is somewhat reduced as per checking it on XCode Instruments but my problem is that my app suddenly crashes. I don't know why. Or I'm not just doing it right. Sample clean up function from my widget:
$.cview.remove($.formItemLabel);
$.cview.remove($.lblValue);
$.mView.remove($.cview);
//nulling controller variables goes here
mData = null;
animateLeft = null;
...
This is a really great article on memory management, I follow these guidelines for my own development purposes and highly recommend you read through this.
Memory Management
http://www.tidev.io/2014/03/27/memory-management/

Appcelerator Alloy model binding failing with instance model

controller:
var args=arguments[0]||{};
if(args.model){
$.myFighter = args.model;
}else{
alert('data not passed');
}
View:
<Alloy>
<Model id="myFighter" src="fighters" instance="true"/>
<Window>
<ScrollView layout="vertical">
<View layout="vertical">
<Label id="name" text="{$.myFighter.name}"/>
<Label id="nickname" text="{$.myFighter.nickname}"/>
<Label id="fighterId" text="{$.myFighter.id}"/>
</View>>
</ScrollView>>
</Window>
</Alloy>
Yet the text is always empty in my view, like if model was never assigned.
The controller is called from another controller, passing the model as an argument like this:
var win=Alloy.createController('detail',{model:detailObj});
win.getView().open();
What am I doing wrong? How can I do it right?

Resources