I have developed a native module which requires me to pass a view while opening the module controller. The module is basically a PDF Reader and I need to put a custom sharing button which I need to pass from Titanium code. This button view was visible and working perfectly fine with Classic Titanium apps and also working fine when I programmatically create the button. The only problem is now using Alloy, I created a separate control for the button and now it stopped working.
Below I have pasted snippet for demo code.
Here is my code which was working fine when used programmatic control:
App.js:
var shareBase = Ti.UI.createView({
width: 40,
height: 40,
top: 0,
left: 0,
backgroundColor: 'green'
});
var shareview = Ti.UI.createView({
backgroundImage: 'shareview.png',
width: 40,
height: 40,
});
shareBase.add(shareView);
...
...
...
// Below is the code how I am passing the view for the module
var PDF_READER = require('com.investis.docreader');
var document = PDF_READER.createPDFDocument(reportFile.nativePath.trim());
...
document.setCustomView(shareBase); // Share view is being passed
document.setTitle(report.title);
document.display();
Controller in the module which accepts the view passed from Titanium code:
controller.m
- (void)setCustomView: (id)args {
TiUIViewProxy *_argVw = args;
if (!args) {
NSLog(#"View Proxy nil", nil);
}
[_pdfViewController setCustomView: _argVw.view ]; // _argVw.view gets UIView from ViewProxy
NSLog(#"View assigned here", nil);
NSLog([_argVw description], nil);
NSLog([_argVw.view description], nil);
}
pdfViewController.m - The controller from where original PDF Reader is called, so I need to pass the view to this controller which loads the view into Toolbar
...
_customButton = [[UIBarButtonItem alloc] initWithCustomView: _customView]; // _customView is the view which is passed from controller.m
...
// Now display button items into toolbar
toolbarItems = [NSArray arrayWithObjects:... _customButton, fixedSpace, ..., nil];
The above code was working fine. But when I implemented the code as Alloy controller, the custom view is stopped being loaded from the controller.
App.js
var pdfshare = Alloy.createController("SocialShare", {});
...
document.setCustomView(pdfshare.getView()); // Passing custom view from the controller, other code in the file remain same
...
Here is the code how SocialShare is created.
SocialShare.xml
<Alloy>
<View id="container" class="container">
<View id="shareIt" onSingletap="shareIt" platform="ios"/>
<View id="shareIt" onSingletap="shareItAndroid" platform="android" />
</View>
</Alloy>
SocialShare.tss
".container":{
backgroundColor: 'yellow',
borderWidth: 2,
borderColor: 'black',
right: 0,
bottom: 0
},
".container[formFactor=tablet]":{
height: 43,
width: 43
},
".container[formFactor=handheld]":{
height: 40,
width: 40
}
SocialShare.js
var args = arguments[0] || {};
...
// Here nothing extra ordinary UI related tasks are performed. Only event handlers for shareIt
The same code at Module side but still it stopped working. If I pass view generated programmatically then it works fine. Let me know if we need to treat Alloy views differently or not. I have searched a lot over Google and read through docs but unable to find any proper solution.
Details:-
Titanium SDK: 3.2.3.GA
Alloy: 1.3.0
iPhone Simulator: 7.1
Let me know if there is any solution around this or any patch.
Related
This is my first post, so sorry for any errors.
I'm trying to make a series of nested navigators, starting from a Switch navigator, then going to a Drawer Navigator, that for each of it's sections, it calls a different Stack Navigator, like this:
SwitchNavigator
DrawerNavigator
MapStackNavigator
Map Screen
ProfileStackNavigator
Profile Screen
The initial route for the Drawer Nav is the Stack Nav which calls a Map Screen. The problem is, if I go to the "Profile" Section of the drawer nav, which calls for the Stack Nav that opens the Profile Screen, I'm not beeing able to return to the Map Screen through an onPress() method inside the Navigation headerLeft Icon.
The react-native app is being developed for iOS only, and now as I don't have an iOS device to test on, neither a Mac to use a simulator, I'm currently testing it on my android smartphone.
As I've tested, it wouldn't be a problem if the app was meant to run on android, as the android has the built-in "back" button and it automatically goes back to the previous screen (which is set to the Map Screen inside the DrawerNav). But, as it is for iOS only, we actually need to display a "back" button on the top of the screen (so, inside the StackNav Header) for going back to the Map Screen.
My first guess was to use the navigation.navigate inside the onPress() to call another screen from the current profile StackNav, and that screen calls the first SwitchNav that I've Created to load things up, but it won't work, as it acts like a "navigation loop", and the const SwitchNavigator can't be called from the Profile StackNav.
I've tried to call the StackNav that holds the MapScreen from the onPress(), so it was: click on the back button > it calls the onPress() which calls another screen > that another screen calls the StackNav holding the MapScreen, but it nests one StackNav inside the other.
My Routes.js looks like this:
import Testemapa from './src/components/Map/index';
import MyProfile from './src/components/DrawerNavigator/MyProfile';
import Icon from 'react-native-vector-icons/Ionicons';
import IconE from 'react-native-vector-icons/Entypo';
// The StackNav that holds the Map Screen
const IvesStackNavigator = createStackNavigator ({
screenMap: Testemapa,
},
{
defaultNavigationOptions: ({navigation})=>{
return{
headerRight: (<Icon
style={{paddingRight: 10}}
onPress={() => navigation.openDrawer()}
name="md-menu" size={30}
/>)
};
}
});
// What I tried to do to call the MapScreen, but it ends up nesting it inside
const wayBackSwitchNavigator = createSwitchNavigator ({
transitionToHome: IvesStackNavigator,
});
// The StackNav that holds the Profile Screen
const myProfileStackNavigator = createStackNavigator ({
drawer_myProfile: MyProfile,
transitionPathHome: wayBackSwitchNavigator,
},
{
headerBackTitleVisible: 'true',
defaultNavigationOptions: ({navigation})=>{
return{
//This guy is the Back button, that's actually an Icon
headerLeft: (<IconE
style={{paddingLeft: 10}}
onPress={() => navigation.navigate('transitionPathHome')}
name="chevron-small-left" size={30}
/>),
};
},
});
// The Drawer which calls all of the stacks
const MainDrawerNavigator = createDrawerNavigator ({
BOOK_A_RIDE: {
screen: IvesStackNavigator,
navigationOptions: () =>
({
title: 'BOOK A RIDE'
})
},
MY_EARNINGS: {
screen: ridesStackNavigator,
navigationOptions: () =>
({
title: 'MY EARNINGS'
})
},
MY_PROFILE: {
screen: myProfileStackNavigator,
navigationOptions: () =>
({
title: 'MY PROFILE'
})
},
},
{
initialRouteName: 'BOOK_A_RIDE',
drawerOpenRoute:'DrawerOpen',
drawerPosition: 'right',
drawerBackgroundColor: 'black',
hideStatusBar: 'true',
overlayColor: 'red',
edgeWidth: 100,
contentOptions: {
inactiveBackgroundColor: 'black',
inactiveTintColor: '#fff',
activeBackgroundColor: '#4d4d4d',
activeTintColor: '#fff',
},
contentComponent: props =>
<Container style={{alignItems: 'stretch', marginLeft: 0, marginRight : 0, backgroundColor: 'black'}}>
<Header style={{height:150, marginLeft: 0, marginRight : 0, backgroundColor: 'black'}}>
<Body style={{marginLeft: 0, marginRight : 0, backgroundColor: 'black'}}>
<ImageBackground source={require('./src/assets/splash_ImageLimo.jpg')} style={{left: -10, width: 264, height:150, opacity:0.7, marginLeft: 0, marginRight : 0, paddingRight: 0}}>
</ImageBackground>
</Body>
</Header>
<Content>
<DrawerItems {...props} />
</Content>
</Container>
},
);
// The SwitchNav that is the first Navigator to run on the app
const firstSwitchNavigator = createSwitchNavigator(
{
TestDrawer: MainDrawerNavigator,
},
{
initialRouteName: 'TestDrawer',
}
);
export default createAppContainer(MySwitchNavigator);
So, when The Back button inside the MyProfileStackNavigator is pressed, It was supposed to go back to the MapScreen which is holded by the IvesStackNavigator, which is inside the mainDrawerNavigator.
I'm not allowed to let the user open the DrawerNav from the Profile Screen, so the back button is really important, as it's the only way to go back to the map.
If I try to directly call the SwitchNav or the DrawerNav by using any of them as the Screen path in MyProfileStackNavigator, I get the error that the screen that was called isn't a React component ("The component for route 'transitionToHome' must be a React component...").
If I change it to call IvesStackNavigator directly, the navigator will end up nested inside the MyProfileStackNavigator.
And, if I call the actual map page, it will display the map page inside the MyProfileStackNavigator, having all of the MyProfileStackNavigator properties (like the Back button and the absency of the button to open the drawer menu).
Any help with this would be appreciated, I'm fairly new to React-Native and it's dependencies, and I had to jump in right to coding before learning, as it is a task assigned to me by my boss, so I try to learn while producing the app.
I am loading some HTML, Java script code into my iOS application. Its working fine, but, according to device height, I should have to change the animation size (width), I tried following code, but, I am very new to javascript calling into iOS.
my html page code of java script is
var $ios9 = document.getElementById('container-ios9');
var SW9 = new Wave9({
width: 800,
height: 40,
container: $ios9,
});
</script>
and I am calling into native code as
if (self.view.frame.size.width <= 400) {
[_animationWebView stringByEvaluatingJavaScriptFromString:#"document.getElementById('container-ios9').width = 500"];
}
else {
[_animationWebView stringByEvaluatingJavaScriptFromString:#"document.getElementById('container-ios9').width = 700"];
}
The above code is not changing the width, So,
Can anyone suggest me, how to fix this. Thanks!
After lot of R & D, I fixed the issue by myself. I am posting this answer here, It will help someone future.
In HTML file, Java script code,
if (window.screen.width <= 400) {
var $ios9 = document.getElementById('container-ios9');
var SW9 = new Wave9({
width: 800,
height: 40,
container: $ios9,
});
} else {
//other devices
</script>
I am developing on iOS 9.2 SDK & Titannium SDK v5.1.2.GA.
In my iPad app; there is a product tab page, which has a "Discount" button. When you click it, a Popover with a TextField and Picker is shown like this:
The above is created on the fly. (not using a controller + view).
This works as intended. I wanted to extend this a little further by recording the given discount to a product in alloy.js in a global array variable called Alloy.Globals.ProductDiscounts = []; (so it can be used later).
The way I "capture" the new discount price is by listening to the "hide" event on the picker. Then update the global array.
For debugging purpose, I added a console log to make sure it's getting recorded correctly and then in the Appcelerator Studio console window, I started see this endless output like this:
I had to kill the simulator to stop this weird constant output of nulls.
This is my code so far, any idea why the console window is spazzing out? Also, why isn't my global array isn't getting set? or is it getting set, but I missed the actual console.log entry?
// Subscribe to line discount button click event
lineDiscountButton.addEventListener('click', function(e)
{
// Stop further events
e.cancelBubble = true;
// Create popover
var discountPopover = Titanium.UI.iPad.createPopover({
arrowDirection: Titanium.UI.iPad.POPOVER_ARROW_DIRECTION_RIGHT,
orignalPrice: e.source.orignalPrice,
priceButton: e.source.priceButton
});
var discountPopoverView = Titanium.UI.createView({
width: 250,
height: 210
});
// Create discount popover view wrapper
var discountPopoverViewWrapper = Titanium.UI.createView({
top: 10,
left: 10,
right: 10,
bottom: 10,
layout: 'vertical'
});
discountPopoverViewWrapper.add(Titanium.UI.createLabel({
top: 0,
left: 0,
color: '#5C5C5C',
font: {
fontSize: 12
},
text: 'Enter a new Price'
}));
discountPopoverViewWrapper.add(Titanium.UI.createView({
top: 0,
height: 1,
backgroundColor: '#0088CE',
width: '100%'
}));
var discountPrice = Titanium.UI.createTextField({
top: 0,
width: '100%',
height: Titanium.UI.SIZE,
hintText: discountPopover.orignalPrice,
value: discountPopover.orignalPrice,
backgroundColor: '#FFFFFF',
font: {
fontSize: 18,
fontWeight: 'bold'
},
color: '#5C5C5C'
});
discountPopoverViewWrapper.add(discountPrice);
discountPopoverViewWrapper.add(Titanium.UI.createLabel({
top: 10,
left: 0,
color: '#5C5C5C',
font: {
fontSize: 12
},
text: 'Or Select a Discount Percent'
}));
discountPopoverViewWrapper.add(Titanium.UI.createView({
top: 0,
height: 1,
backgroundColor: '#0088CE',
width: '100%'
}));
var discountPercentPicker = Titanium.UI.createPicker({
top: 0,
width: Titanium.UI.FILL,
height: 112
});
var discountPercentValues = [];
for (var i = 0; i <= 100; i++) {
discountPercentValues.push(Titanium.UI.createPickerRow({
title: i +'%'
}));
}
discountPercentPicker.add(discountPercentValues);
discountPercentPicker.addEventListener('change', function(e) {
if (parseInt(e.rowIndex) === 0) {
discountPrice.value = discountPopover.orignalPrice;
} else {
discountPrice.value = (discountPopover.orignalPrice - (discountPopover.orignalPrice * (parseInt(e.rowIndex) / 100))).toFixed(2);
}
});
discountPopoverViewWrapper.add(discountPercentPicker);
// Add discount popover view wrapper to view
discountPopoverView.add(discountPopoverViewWrapper);
// Set popover content view
discountPopover.contentView = discountPopoverView;
// Subscribe to popover hide event
discountPopover.addEventListener('hide', function(e) {
e.cancelBubble = true;
Alloy.Globals.ProductDiscounts[discountPopover.priceButton.sku] = parseFloat(discountPrice.value).toFixed(2);
Alloy.Globals.LiveBasketCollection.executeQuery("UPDATE live_basket SET Price = "+ discountPrice.value +" WHERE Sku = '"+ discountPopover.priceButton.sku +"'");
discountPopover.priceButton.price = Alloy.Globals.DeviceDefaults.CurrencySymbol + discountPrice.value;
discountPopover.priceButton.title = (discountPopover.priceButton.basketQuantity > 0 ? discountPopover.priceButton.basketQuantity +' x ' : '') + discountPopover.priceButton.price;
Titanium.App.fireEvent('redrawBasket');
discountPopover = discountPopoverView = discountPrice = discountPercentPicker = discountPercentValues = null;
console.log(Alloy.Globals.ProductDiscounts);
});
// Show popover
discountPopover.show({
view: lineDiscountButton,
animated: true
});
});
Just as a Question... Why are you cancelling bubbling.
without more of the code base I can only try and make a suggestion.
1) addeventlistener.
lineDiscountButton.addEventListener('click', setupData);
2) setupData
Function setupData(e) {
lineDiscountButton.removeEventListener('click', setupData);
Your code from the inline function.
}
Basically remove the event listener once activated, so add and remove as required.
I don't want to teach you to suck eggs, but to remove an event listener, you have to use exactly the same parameters as adding it. Thus inline function on event listeners are not ideal, although they work separating the code out into its own function is preferable.
Next Alloy globals.... Not good practice. I am guessing you want to have the data only for the duration of the running of the app, and not for future use.
If you need it for future use, you can store the data in properties.
Hope this helps
T.
I have a function that is being called once the user hover the window. Is it possible to change the width of the window? this is my function:
function resize() {
console.log('resize');
}
Yes, it's possible to get access to the renderers BrowserWindow API. Simply include the remote module and get the current window, this will allow you to do everything you can from the main process.
Example:
let { remote } = require('electron')
let win = remote.getCurrentWindow()
// Access to the renderers BrowserWindow API
win.setBounds({
width: 1000
})
win.getBounds()
// Object {x: 240, y: 192, width: 800, height: 600}
Reference: BrowserWindow API documentation
I am designing a flashcard app using titanium appcelerator, the final aim is to use a scrollable view to display each card in the 'pack' and then on tapping a single card (i.e. the card in view) this should rotate about it's axis (i.e. flip over) to reveal the reverse.
You can see from the diagram below what I am hoping to achieve.
Unfortunately when tap one of the panels, instead of it's child view animating, instead the last child in the scrollableview animates.
I understand this is something to do with the fact that I am assigning variables in a loop but if someone can look at my code and tell me what I am doing wrong that would be great.
In JQuery it would look something like this
$(FrontView).click(function(){
$(this).parent().flip() //how do I access $(this) and $(this).parent() in Appcelerator?
});
Here is my appcelerator code:
while(rows.isValidRow()){
var FrontView = Ti.UI.createView({
backgroundImage: '/global/card_bg.png',
width: 295,
height:297
});
var BackView = Ti.UI.createView({
backgroundImage: '/global/card_bg.png',
width: 295,
height:297
});
var ControlView = Titanium.UI.createView({
backgroundColor:"#333",
bubbleParent : false,
width: 295,
height:297,
top : 100
});
FrontView.addEventListener('singletap', function() {
Ti.API.info('singletap');
ControlView.animate({view:BackView,transition:Ti.UI.iPhone.AnimationStyle.FLIP_FROM_RIGHT});
});
BackView.addEventListener('singletap', function() {
Ti.API.info('singletap');
ControlView.animate({view:FrontView,transition:Ti.UI.iPhone.AnimationStyle.FLIP_FROM_LEFT});
});
$.scrollable_view.addView(ControlView);
ControlView.add(BackView);
ControlView.add(FrontView);
rows.next();
} //endwhile
Can anybody help?
If you put a return variable in your event callbacks, you can get access to the source of the tap. Try this and see if it works:
FrontView.addEventListener('singletap', function(e) {
Ti.API.info('singletap');
e.source.animate({view:BackView,transition:Ti.UI.iPhone.AnimationStyle.FLIP_FROM_RIGHT});
});
BackView.addEventListener('singletap', function(e) {
Ti.API.info('singletap');
e.source.animate({view:FrontView,transition:Ti.UI.iPhone.AnimationStyle.FLIP_FROM_LEFT});
});
I found out that it works when you flip the card in the EventListener of your Scrollable View.
Check this out: https://gist.github.com/4683264