Reputation: 168
I'm working on a JavaScript App for iOS, Android and the Windows Store, using VS2015 TACO for iOS and Android. I just installed the Ace Plugin and I'm working my way through the Javascript/Ace code samples. For iOS and Android, I want to use native UI for: 1) a bottom tab bar and 2) a scrolling picker list with icons and text (basically, a WinJS.UI ListView). The Ace Intro YouTube Video shows a sample tab bar in XAML markup but I'm not familiar with XAML. Can anyone point me to Ace Plugin Javascript samples for a tab bar UI and Table View UI for iOS and Android? I'd be grateful.
UPDATE #1 Thank you to Adam Nathan. Using his code, I get a docked Tab Bar in my Mac iPad Simulator. It snaps nicely in both portrait and landscape view. I'm using this code to toggle it on and off:
TEST_ACE.dockTabs = function () {
if (TEST_ACE.pageTabBar == null) {
// Surround the WebView with native UI
TEST_ACE.pageTabBar = TEST_ACE.buildDockedTabsWithoutXaml();
// Replace the WebView with the loaded native page
// (For the title setting in XAML to work on Android, ShowTitle must be
// set to true in the Android section of config.xml.)
ace.getHostPage().setContent(TEST_ACE.pageTabBar);
// Reparent the WebView inside the native page
TEST_ACE.pageTabBar.setContent(ace.getHostWebView());
// Save the native page so we can set its title as tabs are clicked
_nativePage = TEST_ACE.pageTabBar;
}
else {
ace.getHostPage().setContent(ace.getHostWebView());
TEST_ACE.pageTabBar = null;
}
}
When I toggle the Tab Bar off, I still see the bottom block where the icons used to be - but it's now empty. That is, when I try to toggle off both the upper title bar and the bottom tab bar, I only dismiss the upper title bar but the bottom block, now blank, is still there. If I change orientation (portrait / landscape) then the bottom block will disappear. Is there a better way clear the HostPage with the tab bar before resetting it to the HostWebView?
For Android, I have updated the config.xml file to set ShowTitle to true:
<name>AcePluginTester</name>
<description>A blank project that uses Apache Cordova to help you build an app that targets multiple mobile platforms: Android, iOS, Windows, and Windows Phone.</description>
<author href="http://cordova.io" email="[email protected]">Apache Cordova Team </author>
<content src="index.html" />
<access origin="*" />
<vs:features />
<preference name="SplashScreen" value="screen" />
<preference name="windows-target-version" value="8.1" />
<!-- Support for Cordova 5.0.0 plugin system -->
<plugin name="cordova-plugin-whitelist" version="1" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<allow-intent href="tel:*" />
<allow-intent href="sms:*" />
<allow-intent href="mailto:*" />
<allow-intent href="geo:*" />
<platform name="android">
<allow-intent href="market:*" />
<preference name="ShowTitle" value="true"/>
</platform>
<platform name="ios">
<allow-intent href="itms:*" />
<allow-intent href="itms-apps:*" />
<preference name="orientation" value="all" />
</platform>
<!--blah blah blah-->
But when I run the App in the VS Android Emulator, I get still this error:
Uncaught Error: Native error: java.lang.RuntimeException: Error setting FrameLayout's UserControl.Content to Windows.UI.Xaml.Controls.Page{6b1647a V.E...... ......I. 0,0-0,0}
at run.ace.IncomingMessages.set(IncomingMessages.java:206)
at run.ace.NativeHost$2.run(NativeHost.java:239)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.RuntimeException: Cannot set title on the main page in Android unless you set <preference name="ShowTitle" value="true"/> in config.xml.
at run.ace.TabBar.setTitle(TabBar.java:39)
at Windows.UI.Xaml.Controls.Page.updateTitle(Page.java:63)
at Windows.UI.Xaml.Controls.Page.processBars(Page.java:71)
at Windows.UI.Xaml.Controls.ViewGroupHelper.setProperty(ViewGroupHelper.java:51)
at run.ace.IncomingMessages.set(IncomingMessages.java:191)
... 8 more
See http://ace.run/docs/errors for help.
cordova.js (314,13)
Did I miss something for Android?
Once again, thank you. This is going to be great!
UPDATE #2
I moved the ShowTitle preference to the widget root and I still get the error. I get the error with both value="True" (upper case True) and with value="true". Here's my config.xml file in the entirety:
<?xml version="1.0" encoding="utf-8"?>
<widget xmlns:cdv="http://cordova.apache.org/ns/1.0" xmlns:vs="http://schemas.microsoft.com/appx/2014/htmlapps" id="io.cordova.myapp740bed" version="1.0.1" xmlns="http://www.w3.org/ns/widgets" defaultlocale="en-US">
<name>AcePluginTester</name>
<description>A blank project that uses Apache Cordova to help you build an app that targets multiple mobile platforms: Android, iOS, Windows, and Windows Phone.</description>
<author href="http://cordova.io" email="[email protected]">Apache Cordova Team </author>
<content src="index.html" />
<access origin="*" />
<vs:features />
<preference name="SplashScreen" value="screen" />
<preference name="windows-target-version" value="8.1" />
<!-- Support for Cordova 5.0.0 plugin system -->
<plugin name="cordova-plugin-whitelist" version="1" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<allow-intent href="tel:*" />
<allow-intent href="sms:*" />
<allow-intent href="mailto:*" />
<allow-intent href="geo:*" />
<platform name="android">
<allow-intent href="market:*" />
</platform>
<platform name="ios">
<allow-intent href="itms:*" />
<allow-intent href="itms-apps:*" />
<preference name="orientation" value="all" />
</platform>
<platform name="android">
<icon src="res/icons/android/icon-36-ldpi.png" density="ldpi" />
<icon src="res/icons/android/icon-48-mdpi.png" density="mdpi" />
<icon src="res/icons/android/icon-72-hdpi.png" density="hdpi" />
<icon src="res/icons/android/icon-96-xhdpi.png" density="xhdpi" />
</platform>
<platform name="ios">
<!-- iOS 8.0+ -->
<!-- iPhone 6 Plus -->
<icon src="res/icons/ios/icon-60-3x.png" width="180" height="180" />
<!-- iOS 7.0+ -->
<!-- iPhone / iPod Touch -->
<icon src="res/icons/ios/icon-60.png" width="60" height="60" />
<icon src="res/icons/ios/icon-60-2x.png" width="120" height="120" />
<!-- iPad -->
<icon src="res/icons/ios/icon-76.png" width="76" height="76" />
<icon src="res/icons/ios/icon-76-2x.png" width="152" height="152" />
<!-- iOS 6.1 -->
<!-- Spotlight Icon -->
<icon src="res/icons/ios/icon-40.png" width="40" height="40" />
<icon src="res/icons/ios/icon-40-2x.png" width="80" height="80" />
<!-- iPhone / iPod Touch -->
<icon src="res/icons/ios/icon-57.png" width="57" height="57" />
<icon src="res/icons/ios/icon-57-2x.png" width="114" height="114" />
<!-- iPad -->
<icon src="res/icons/ios/icon-72.png" width="72" height="72" />
<icon src="res/icons/ios/icon-72-2x.png" width="144" height="144" />
<!-- iPhone Spotlight and Settings Icon -->
<icon src="res/icons/ios/icon-small.png" width="29" height="29" />
<icon src="res/icons/ios/icon-small-2x.png" width="58" height="58" />
<!-- iPad Spotlight and Settings Icon -->
<icon src="res/icons/ios/icon-50.png" width="50" height="50" />
<icon src="res/icons/ios/icon-50-2x.png" width="100" height="100" />
</platform>
<platform name="windows">
<icon src="res/icons/windows/Square150x150Logo.scale-100.png" width="150" height="150" />
<icon src="res/icons/windows/Square150x150Logo.scale-240.png" width="360" height="360" />
<icon src="res/icons/windows/Square30x30Logo.scale-100.png" width="30" height="30" />
<icon src="res/icons/windows/Square310x310Logo.scale-100.png" width="310" height="310" />
<icon src="res/icons/windows/Square44x44Logo.scale-100.png" width="44" height="44" />
<icon src="res/icons/windows/Square44x44Logo.scale-240.png" width="106" height="106" />
<icon src="res/icons/windows/Square70x70Logo.scale-100.png" width="70" height="70" />
<icon src="res/icons/windows/Square71x71Logo.scale-100.png" width="71" height="71" />
<icon src="res/icons/windows/Square71x71Logo.scale-240.png" width="170" height="170" />
<icon src="res/icons/windows/StoreLogo.scale-100.png" width="50" height="50" />
<icon src="res/icons/windows/StoreLogo.scale-240.png" width="120" height="120" />
<icon src="res/icons/windows/Wide310x150Logo.scale-100.png" width="310" height="150" />
<icon src="res/icons/windows/Wide310x150Logo.scale-240.png" width="744" height="360" />
</platform>
<platform name="wp8">
<icon src="res/icons/wp8/ApplicationIcon.png" width="62" height="62" />
<icon src="res/icons/wp8/Background.png" width="173" height="173" />
</platform>
<platform name="android">
<splash src="res/screens/android/screen-hdpi-landscape.png" density="land-hdpi" />
<splash src="res/screens/android/screen-ldpi-landscape.png" density="land-ldpi" />
<splash src="res/screens/android/screen-mdpi-landscape.png" density="land-mdpi" />
<splash src="res/screens/android/screen-xhdpi-landscape.png" density="land-xhdpi" />
<splash src="res/screens/android/screen-hdpi-portrait.png" density="port-hdpi" />
<splash src="res/screens/android/screen-ldpi-portrait.png" density="port-ldpi" />
<splash src="res/screens/android/screen-mdpi-portrait.png" density="port-mdpi" />
<splash src="res/screens/android/screen-xhdpi-portrait.png" density="port-xhdpi" />
</platform>
<platform name="ios">
<splash src="res/screens/ios/screen-iphone-portrait.png" width="320" height="480" />
<splash src="res/screens/ios/screen-iphone-portrait-2x.png" width="640" height="960" />
<splash src="res/screens/ios/screen-ipad-portrait.png" width="768" height="1024" />
<splash src="res/screens/ios/screen-ipad-portrait-2x.png" width="1536" height="2048" />
<splash src="res/screens/ios/screen-ipad-landscape.png" width="1024" height="768" />
<splash src="res/screens/ios/screen-ipad-landscape-2x.png" width="2048" height="1536" />
<splash src="res/screens/ios/screen-iphone-568h-2x.png" width="640" height="1136" />
<splash src="res/screens/ios/screen-iphone-portrait-667h.png" width="750" height="1334" />
<splash src="res/screens/ios/screen-iphone-portrait-736h.png" width="1242" height="2208" />
<splash src="res/screens/ios/screen-iphone-landscape-736h.png" width="2208" height="1242" />
</platform>
<platform name="windows">
<splash src="res/screens/windows/SplashScreen.scale-100.png" width="620" height="300" />
<splash src="res/screens/windows/SplashScreen.scale-240.png" width="1152" height="1920" />
<splash src="res/screens/windows/SplashScreenPhone.scale-240.png" width="1152" height="1920" />
</platform>
<platform name="wp8">
<splash src="res/screens/wp8/SplashScreenImage.jpg" width="480" height="800" />
</platform>
<plugin name="cordova-plugin-crosswalk-webview" version="1.5.0" src="https://github.com/crosswalk-project/cordova-plugin-crosswalk-webview" />
<plugin name="cordova-plugin-ace" version="0.0.12" src="https://github.com/Microsoft/ace" />
<engine name="ios" spec="~3.9.2" />
<engine name="android" spec="~4.1.1" />
<plugin name="cordova-plugin-ace" spec="https://github.com/microsoft/ace.git" />
<preference name="ShowTitle" value="true" />
</widget>
I get the error even after running "Clean Solution" and "Clear Cordova Cache".
I'll post the tab bar code in a separate update.
UPDATE #3 My App content is a WebGL canvas. When my Mac iPad2 Simulator (portrait) first draws, the window size is 768 (width) x 1004 (height) as shown JavaScript Console log output below:
Window size: 768 x 1004
debug.js (29,24)
Window size: 768 x 960
debug.js (29,24)
Window size: 768 x 960
debug.js (29,24)
When I toggle the Tab Bar open, it resizes to a height of 960. When I toggle the tab bar closed (staying in portrait), the window height does not return to 1004. So it looks like the window is not resizing when the tab bar is toggled closed. As soon as I rotate the device to landscape, the full window gets redrawn and the bottom bar disappears, likewise, when I rotate back to portrait, the full window size snaps back to 768 x 1004 and the blank bottom bar is also not there. Here's my testing code (in progress):
Inside index.js, when onDeviceReady fires, my code calls GAME.initialize():
function onDeviceReady() {
// Handle the Cordova pause and resume events
document.addEventListener( 'pause', onPause.bind( this ), false );
document.addEventListener( 'resume', onResume.bind( this ), false );
// TODO: Cordova has been loaded. Perform any initialization that requires Cordova here.
GAME.initialize();
};
Here is the code in game.js:
//game.js
var GAME = { NAME: "hiio", VERMAJOR: 0, VERMINOR: 9, DEBUG: true, BUILD: 1, CLASSNAME: "GAME" };
GAME.initialize = function () {
"use strict";
//log if debug
loginit();
log(GAME.NAME + " " + GAME.VERMAJOR + "." + GAME.VERMINOR + (GAME.DEBUG ? " (Debug)" : " (Release)"));
//renderer
var testcanvas = document.getElementById("idcanvas");
if (!GAME.renderer) GAME.renderer = new THREE.WebGLRenderer( { canvas: testcanvas } );
//scene
if (!GAME.scene) GAME.scene = new THREE.Scene();
//camera
GAME.setcamera();
//orbitcontrols
GAME.orbctls = new THREE.OrbitControls(GAME.camera);
// ambient
var al = new THREE.AmbientLight(0x222222, 0.5);
GAME.scene.add(al);
// light
var dl = new THREE.DirectionalLight(0xffffff, 1.0);
dl.position.set(2000, 2000, 500);
GAME.scene.add(dl);
// axes
GAME.scene.add(new THREE.AxisHelper(4000));
// Sphere
var geometry = new THREE.SphereGeometry(500, 24, 8);
var material = new THREE.MeshPhongMaterial({
color: 0x00ffff,
transparent: true,
opacity: 0.7,
});
var mesh = new THREE.Mesh(geometry, material);
GAME.scene.add(mesh);
//FPS
GAME.stats = initStats();
//events
window.addEventListener("resize", GAME.setcamera, false);
//Ace
TEST_ACE.initialize();
animate(); //run
}
//camera
GAME.setcamera = function () {
"use strict";
var c = document.getElementById("idcanvas");
var h = window.innerHeight;
var w = window.innerWidth;
if (!GAME.camera) { //new
GAME.camera = new THREE.PerspectiveCamera(45, w / h, 1, 16000);
GAME.camera.position.set(50, 100, 3000);
GAME.scene.add(GAME.camera);
}
else { //change
GAME.camera.aspect = (w / h);
}
if (GAME.renderer) { //onsize
GAME.renderer.setSize(w, h); //this sets the height of the canvas.
log("Window size: " + window.innerWidth + " x " + window.innerHeight);
}
if (GAME.orbctls) {
GAME.target = new THREE.Vector3(0, 0, 0);
GAME.orbctls.target0 = GAME.target.clone();
GAME.orbctls.reset();
}
GAME.camera.updateProjectionMatrix();
}
function initStats() {
"use strict";
var stats = new Stats();
stats.setMode(0);
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
//animate loop
var banimate = false;
function animate() {
"use strict";
requestAnimationFrame(animate);
if (banimate) { loge("animate re-entry."); return; }
banimate = true;
GAME.renderer.render(GAME.scene, GAME.camera);
GAME.stats.update();
banimate = false;
};
//end
Here's the test_act.js code (little messy sorry):
//test_ace.js
var TEST_ACE = { CLASSNAME: "TEST_ACE" };
TEST_ACE.popupSegCtrl = null;
TEST_ACE.pageTabBar = null;
TEST_ACE.initialize = function () {
"use strict";
var abutton1 = document.getElementById("idbutton1"); //Tab Bar
abutton1.addEventListener("click", TEST_ACE.dockTabs, false);
return;
}
TEST_ACE.buildDockedTabsWithoutXaml = function () {
var page = new ace.Page();
page.setTitle("Docked");
var tabBar = new ace.TabBar();
var b1 = new ace.AppBarButton();
var b2 = new ace.AppBarButton();
var b3 = new ace.AppBarButton();
var b4 = new ace.AppBarButton();
var b5 = new ace.AppBarButton();
b1.setLabel("Docked");
b1.setIcon("www/images/info-{platform}.png");
b1.addEventListener("click", function () { onTabClick(b1, 0); });
b2.setLabel("Overlay");
b2.setIcon("www/images/info-{platform}.png");
b2.addEventListener("click", function () { onTabClick(b2, 1); });
b3.setLabel("Fullscreen");
b3.setIcon("www/images/info-{platform}.png");
b3.addEventListener("click", function () { onTabClick(b3, 2); });
b4.setLabel("Code");
b4.setIcon("www/images/info-{platform}.png");
b4.addEventListener("click", function () { onTabClick(b4, 3); });
b5.setLabel("Plat-Specific UI");
b5.setIcon("www/images/info-{platform}.png");
b5.addEventListener("click", function () { onTabClick(b5, 4); });
// TabBar's "content property" maps to a PrimaryCommands property.
// Therefore, to add children, we get the PrimaryCommands collection:
tabBar.getPrimaryCommands().add(b1);
tabBar.getPrimaryCommands().add(b2);
tabBar.getPrimaryCommands().add(b3);
tabBar.getPrimaryCommands().add(b4);
tabBar.getPrimaryCommands().add(b5);
page.setBottomAppBar(tabBar);
return page;
}
function onTabClick(a, b) {
return;
}
TEST_ACE.dockTabs = function () {
if (TEST_ACE.pageTabBar == null) {
// Surround the WebView with native UI
TEST_ACE.pageTabBar = TEST_ACE.buildDockedTabsWithoutXaml();
// Replace the WebView with the loaded native page
// (For the title setting in XAML to work on Android, ShowTitle must be
// set to true in the Android section of config.xml.)
ace.getHostPage().setContent(TEST_ACE.pageTabBar);
// Reparent the WebView inside the native page
TEST_ACE.pageTabBar.setContent(ace.getHostWebView());
// Save the native page so we can set its title as tabs are clicked
_nativePage = TEST_ACE.pageTabBar;
}
else {
ace.getHostPage().setContent(ace.getHostWebView());
TEST_ACE.pageTabBar = null;
GAME.setcamera();
}
}
//end
Upvotes: 0
Views: 1306
Reputation: 93
For the Android issue, I believe it's because your ShowTitle preference element needs to be a direct child of the root widget element. You can see an example at the bottom of https://github.com/Microsoft/ace/blob/master/examples/AceExamples/config.xml. If so, then the comment inside AceExamples is indeed wrong and we should fix that! Sorry about that.
For the iOS issue, I just tried to reproduce it with the similar AceExamples code on all 5 iPad simulators, and could not. Would you be able to share your code?
Upvotes: 0
Reputation: 93
The sample you reference does include a bottom tab bar example (note that it renders on the top in Android). It appears in the sample once you click the first link.
UPDATE: Here's how you do the tab bar example all in JavaScript, with no markup:
function buildDockedTabsWithoutXaml() {
var page = new ace.Page();
page.setTitle("Docked");
var tabBar = new ace.TabBar();
var b1 = new ace.AppBarButton();
var b2 = new ace.AppBarButton();
var b3 = new ace.AppBarButton();
var b4 = new ace.AppBarButton();
var b5 = new ace.AppBarButton();
b1.setLabel("Docked");
b1.setIcon("www/images/info-{platform}.png");
b1.addEventListener("click", function() { onTabClick(b1, 0); });
b2.setLabel("Overlay");
b2.setIcon("www/images/info-{platform}.png");
b2.addEventListener("click", function() { onTabClick(b2, 1); });
b3.setLabel("Fullscreen");
b3.setIcon("www/images/info-{platform}.png");
b3.addEventListener("click", function() { onTabClick(b3, 2); });
b4.setLabel("Code");
b4.setIcon("www/images/info-{platform}.png");
b4.addEventListener("click", function() { onTabClick(b4, 3); });
b5.setLabel("Plat-Specific UI");
b5.setIcon("www/images/info-{platform}.png");
b5.addEventListener("click", function () { onTabClick(b5, 4); });
// TabBar's "content property" maps to a PrimaryCommands property.
// Therefore, to add children, we get the PrimaryCommands collection:
tabBar.getPrimaryCommands().add(b1);
tabBar.getPrimaryCommands().add(b2);
tabBar.getPrimaryCommands().add(b3);
tabBar.getPrimaryCommands().add(b4);
tabBar.getPrimaryCommands().add(b5);
page.setBottomAppBar(tabBar);
return page;
}
function dockTabs() {
// Surround the WebView with native UI
var page = buildDockedTabsWithoutXaml();
// Replace the WebView with the loaded native page
// (For the title setting in XAML to work on Android, ShowTitle must be
// set to true in the Android section of config.xml.)
ace.getHostPage().setContent(page);
// Reparent the WebView inside the native page
page.setContent(ace.getHostWebView());
// Save the native page so we can set its title as tabs are clicked
_nativePage = page;
}
More work is probably needed in Ace to give you the Table View experience cross-platform, however. There is a ListBox control, but it has minimal functionality at the moment. At one point I had experimented with a cross-platform TableView control. I'll look into adding that to Ace to help address your needs. Let me know if you have any other issues/requests!
Upvotes: 1