Appcelerator Titanium View Proxy Memory Management - 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/

Related

React-native Why Menu is not rendering

i have a menu for each item of a flatlist, however when ever i press on the button to show the menu nothing happens when i log the visible state of the menu i see that it is being set to true but it is not rendering.
this is a component to render each item with a menu
const RenderItem =(props)=>{
return(
<TouchableOpacity onPress={()=>{}}>
<View style={styles.flatitem}>
<Icon style={styles.pdf} name="file-pdf-o" color="#666"/>
<Text style={styles.itemtext}>{props.title}</Text>
<Menu
visible={visible}
{...console.log(visible)}
anchor={<Button onPress={openMenu} >
show
</Button>}
onDismiss={closeMenu} >
<Menu.Item icon="pencil-box-outline" onPress={() =>{}} title="Rename" />
<Menu.Item icon="label-outline" onPress={() => {}} title="Label" />
<Menu.Item icon="delete-outline" onPress={() => {}} title="Delete" />
</Menu>
</View>
</TouchableOpacity> )}
this is the flatlist:
<FlatList style={styles.flatstyle}
keyExtractor={(item)=>item['id']}
data={DATA}
renderItem={({item})=>(<RenderItem title={item.title }/>)}
/>
and these are the open and close functions
const [visible,setVisible]=useState(false)
const openMenu = () => {
setVisible(true)
}
const closeMenu = () => setVisible(false);
If i am right, it is actually not a problem. FlatList is a pure component and will only re-render if the data prop changes i.e, the data passed to the flatlist changes.
As visible is not in data, so it doesnot re-render.
To make it re-render and show the menu, you need to use extraData prop of the flatlist, which will take some other data. If that other data changes, that will also cause a re-render and that other data in your case is the visible variable.
More details here

React Native. trying to load an image from url

I've managed to load an image from the web into my app but have an issue...
I have some local data about food. each food item has a food title, and a url to a picture online.
This is the Image component that lives inside my foodComponent.
<Image style={styles.cardImg} source={{uri:image}} />
image is a string to a url that is passed as a prop to this foodComponent.
Again it works but my problem is that the image takes time to load.
How could I only render the foodItem component once it has received/loaded an image?
I know about promises but I don't see how I could use it in this scenario.
here is my entire foodItem component:
export default function FoodItemTouchable(props){
return(
<View style={styles.container}>
<View style={styles.card}>
<View style={styles.cardHeader}>
<Text style={styles.text}>{props.item.title}</Text>
</View>
<Break/>
<CardImage image={props.item.url}/>
</View>
</View>
)
}
function Break() {
return(
<View style={{alignItems:'center'}}>
<View style={styles.break}>
</View>
</View>
)
}
function CardImage({image}){
return(
<View style={{padding:10,width:'100%'}}>
<Image
style={styles.cardImg}
source={{uri:image}}
/>
</View>
)
}
CardImage is where the image gets rendered.
the rest of the Components render first but the CardImage is delayed because it's loading the image.
If I could somehow load the image into a variable in the main Component,
and then there do a check, is image===null ? return null
so that the component renders only when the image is ready.
Have you tried like this?
{
props.item.url
&&
<CardImage image={props.item.url}/>
}

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

Titanium appcelerator android and ios platform height in pixels and dp

I am about to test an app with the appcelerator titanium framework, i have to center a view on the screen :
my index.xml :
<Alloy>
<!-- Anddroid Window -->
<Window id="index" platform="android">
<Require type="view" id="firstscreen" src="firstscreen"/>
</Window>
<!-- iOS Window -->
<NavigationWindow id="nav" platform="ios">
<Window id="win1" backgroundColor="white">
<Require type="view" id="firstscreen" src="firstscreen"/>
</Window>
</NavigationWindow>
the firstscreen xml :
<Alloy>
<ScrollView scrollingEnabled="false" contentWidth="Ti.UI.FILL">
<ImageView class="fullBgImage" image="/images/login/bg2.png" />
<View layout="vertical">
<View id="loginView" class="heightAuto" layout="vertical">
<ImageView id="localImage" image="/images/logo.png" />
<Label class="logoLabel" text="Karma" />
<Label class="logoSlogan" text="Faits confiance à votre Karma pour booster votre carrière" />
<View class="btn btnVert" onClick="openCandidat"><Label class="btnLabel" text="Je recherche un job" /></View>
<View class="btn btnBlanc" ><Label class="btnLabel bleu" text="Je suis recruteur" /></View>
</View>
</View>
</ScrollView>
</Alloy>
The index.js :
The formula to achieve is : viewTopPosition = (platformheight - viewheight)/2
if (OS_ANDROID) {
$.index.addEventListener('open', after_win_load);
$.index.open();
} else {
$.nav.addEventListener('open', after_win_load);
$.nav.open();
}
function after_win_load() {
// Platform height
var platformHeight = Ti.Platform.displayCaps.platformHeight;
// The view
var firstscreen = $.firstscreen;
/* Taille du logo */
firstscreenViewHeight = firstscreen.loginView.size;
/* Centrer la vue verticalement */
firstScreenTop = platformHeight - firstscreenViewHeight.height;
//firstscreen.top = firstScreenTop;
var style = $.createStyle({
classes : 'firstScreenTop',
apiName : 'View',
top : firstScreenTop / 2
});
firstscreen.loginView.applyProperties(style);
}
On iOs it looks great, view is centered vertically, but in android the : Ti.Platform.displayCaps.platformHeight is a too big pixels value = 1920
on my tiapp.xml file i already specified :
<property name="ti.ui.defaultunit" type="string">dp</property>
I know that android is pixels unit but iOs use dp unit, so how i to achieve this please ? someone has an idea?
For now for android i replaced the
var platformHeight = Ti.Platform.displayCaps.platformHeight;
to
var platformHeight = Ti.Platform.displayCaps.dpi;
But i ask myself if it will be good for all android screen resolution? and if this is a best practice?
Thank for your helps.
var densityFactor = OS_IOS ? 1 : Ti.Platform.displayCaps.logicalDensityFactor;
var platformHeight = Ti.Platform.displayCaps.platformHeight / densityFactor;
or you can directly set the top to 50%
var style = $.createStyle({
classes : 'firstScreenTop',
apiName : 'View',
top : "50%"
});

Appcelerator alloy views slide

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});

Resources