PhoneGap Tutorial Series – #5 Third-Party Plugins (NativeControls)
Extending the PhoneGap API – Third-Party Plugins (NativeControls)
Continuing on down the path of using third party plugins, today we will look at a little more complex example and use the NativeControls plugin to display a UIActionSheet to allow the user to select whether they want to take a photo using the camera or pick one from the photo library by utilizing the PhoneGap Camera API.
If you haven’t read my previous post on Third-Party Plugins (ChildBrowser) you may want to peruse that to understand the structure of a plugin and how to go about installing one before continuing on.
Native Controls Plugin
The NativeControls plugin provides access to several native controls that are commonly used in iOS iPhone development. The plugin for the controls provides access to varying levels of functionality:
- UIActionSheet – provides a slide up control with one to many buttons, the plugin allows the creation of the actionSheet, adding of buttons, and a delegate to respond to a user selection
- UIStatusBar – the plugin provides the ability to hide the standard status bar
- UIToolBar – provides a toolBar control with one “refresh” button, the plugin support for controlling the toolBar is currently somewhat limited
- UITabBar – provides a tabBar control with with one to five buttons, the plugin allows the creation of the tabBar, adding buttons, button actions, positioning, and show/hide capabilities
How to Use the Native Controls Plugin?
Just like with the ChildBrowser plugin, there is a JavaScript file that must be included on the HTML page. After inspecting that Javascript, you will see that near the end of the file there is a reference to the PhoneGap.addConstructor so this particular plugin “installs” itself when you include the NativeControls.js on the page.
PhoneGap.addConstructor(function() { if(!window.plugins) { window.plugins = {}; } window.plugins.nativeControls = new NativeControls(); });
The following is an excerpt from the nativeControls.html in the HelloPhoneGap project. The code demonstrates how to use the nativeControls plugin to create and display an actionSheet and how to react to a user selection in order to exercise the PhoneGap Camera API.
<script type="text/javascript" charset="utf-8" src="phonegap.0.9.4.min.js"></script> <script type="text/javascript" charset="utf-8" src="NativeControls.js"></script> <script type="text/javascript" charset="utf-8"> var nativeControls; function onBodyLoad() { document.addEventListener("deviceready",onDeviceReady,false); } /* When this function is called, PhoneGap is ready to roll */ function onDeviceReady() { phoneGapReady.innerHTML = "PhoneGap is Ready"; nativeControls = window.plugins.nativeControls; } function showCameraOptions() { var buttons = ["Take Photo", "Choose From Library", "Cancel"]; var delegate = nativeControls.createActionSheet(buttons, null, 2, null); delegate.onActionSheetDismissed = function(index) { if (index == 0) { navigator.camera.getPicture(onPhotoURISuccess, onFail, {quality:5,destinationType:1,sourceType:1,allowEdit:false}); } else if (index == 1) { navigator.camera.getPicture(onPhotoURISuccess, onFail, {quality:5,destinationType:1,sourceType:0,allowEdit:false}); } } }; function onPhotoURISuccess(imageURI) { var myImage = document.getElementById('myImage'); myImage.style.display = 'block'; myImage.src = imageURI; } function onFail(mesage) { alert('Failed because: ' + message); } </script> </head> <body onload="onBodyLoad()"> <button onclick="showCameraOptions();">Display Photo</button> <br> <img height=200 width=200 id="myImage" /> </body>
There are a number of things to take note of here:
- line 2 – NativeControls.js is included after the phonegap.js file
- line 13 – shortened reference to window.plugins.nativeControls is created
- lines 15-19 – function showCameraOptions() creates the action sheet and assigns a delegate function for when the user selects an option
- lines 19-30 – defined anonymous function to get a photo depending on whether user opts for the camera or the library
- lines 31-39 – onSuccess and onFailure functions defined for the camera API
Putting It All Together
So there is actually quite a bit going on here and I think it may be helpful to see a diagram that explains how all this actually works. If you haven’t read my post about PhoneGap Project Structure and Internals you may find it helpful for understanding the following interactions in more detail.
The diagram has several areas that I’d like to call your attention to:
- nativeControls.showTabBar() – executing a call to a third-party plugin
- PhoneGap.exec(…) – executing PhoneGap API to run a command
- PhoneGap.runCommand(…) – ultimately changing the document.location on the UIWebView
- PhoneGapDelegate – document.location change causes the UIWebViewDelegate (aka the PhoneGapDelegate) to intercept the request
- PhoneGapDelegate – request is inspected and forwards to the appropriate PhoneGapCommand
- PhoneGapCommand – executes requested operation and then notifies the UIWebView by executing the JavaScript onSuccess or onFailure callbacks
The gist is that every time you execute a PhoneGap API function or a function from a third-party plugin it will ultimately end up changing the document.location of the webView which will be intercepted by the webViewDelegate and then forwarded on to the appropriate PhoneGapCommand class.
Once the command has completed it will typically turn around and execute [webView stringByEvaluatingJavaScriptFromString:jsCallBack]; to call either the provided onSuccess or onFailure js callback functions.
In the example with the ActionSheet and the Camera, we are actually running through the loop between the JavaScript runtime and the Objective-C runtime twice, once for showing the ActionSheet and getting the user response, and once for showing either the camera or the photo library to get a photo.
Where to get the Code?
The plugins used are from Jesse MacFayden (aka @purplecabbage on twitter). The original source code for the plugins can be downloaded from github here: https://github.com/purplecabbage/phonegap-plugins.git
If you are using PhoneGap 0.9.6
You can get the entire source for my sample project “HelloPhoneGap” from github here: https://github.com/hutley/HelloPhoneGap
If you are using PhoneGap 1.0
You can get the entire source for my sample project “HelloPhoneGap1.0” from github here: https://github.com/hutley/HelloPhoneGap1.0
That’s all for now – stay tuned for the next post on creating your own PhoneGap plugin.
Amazing tutorials! Fantastic that you’ve put this series together so far!
Having loaded up your HelloPhoneGap project I am noticing that the TabBar is positioned a little higher than native apps. I’ve had this problem in my own projects too. Is this a known issue?
Cheers once again
Hi these are great thank you, is there any chance you could do one in how to record and store audio through phone gap, if possible.
Thanks again, keep up the good work.
I’ll add it to my list – in the meantime take a look at the PhoneGap docs for some examples – http://docs.phonegap.com/phonegap_media_media.md.html#Media
Is there any way to display 2 splash screens by giving time duration while loading iPhone app in phonegapdelegate.m. Because my app is quite heavy and takes 12-15 secs to load. Could you please give some code sample.
Yes there is – you could put up an activity indicator or an action sheet — both of which are demonstrated in my HelloPhoneGap app BUT the better question is why is your app so slow? Are you loading up heavy JS frameworks? Downloading a lot of data? Could your users be better served by you rethinking your strategy?
Hi, Thanks for your reply. My app is heavy because it contains lots of images and and info. I am using Jquery Mobile with Phonegap. I have tried several otions but still it takes long time to load. Could you please explain me how to display 2/3 splash screens with code sample as I am not sure where it has been described, Thansk
Why would I be getting
Result of expression ‘window.plugins’ [undefined] is not an object.
When I try to use NativeControls in Xcode 4 using Phone Gap 0.9.5 – I’ve copy and pasted your example and it doesn’t seem to like it – putting the .m and .h files in the Plugins folder doesn’t seem to work, I have to put them in Users -> Shared -> PhoneGap ->Frameworks ->PhoneGap.framework->Headers for it to work and not error when compiling, but it doesn’t load the tab bar due to the error above – any ideas?
Cheers
J
Hi Hiedi,
Thanks very much for you’re post. I’m having some trouble getting native controls to work with the new version of phonegap i’m building 0.9.5.1 (I had native controls working fine with an earlier version of phonegap). I noticed a difference between the way you’re hello phonegap example project is built and the way the default phonegap project is created, e.g. you’re example project includes Phonegaplib as an xcode project *within* you’re hello phone gap project. When you create a default phonegap project with the latest version of phonegap – it does’nt construct it like yours it seems to include phonegap as a framework and with a different structure – its this relevant ? There is a top level plugins directory in this new layout and i have added native controls there but it does not seem to work ! Would appriciate any thoughts – thanks P.
Please post your project somewhere and I will take a look.
Hi Hiedi,
Thanks for getting back – i did actually manage to sort it out. So while the default project structure the latest version of phonegap makes is different to yours (i.e. phonegap lib as a static lib) it does now seem to work out of the box. I think the problem was more with getting nativecontrols and other plugins working with this static lib – so using this as an include seemed to sort it so in the top of say NativeControls.h rather than:
#import “PhoneGapCommand.h”
You need:
#ifdef PHONEGAP_FRAMEWORK
#import
#else
#import “PhoneGapCommand.h”
#endif
Which will then deal with either case.
Also i needed to delete the reference to the .plist file (but leave file) in the new project structure.
Thanks anyway for you’ve very helpful articles !
Sorry a typo there ! the correct include code is:
#ifdef PHONEGAP_FRAMEWORK
#import
#else
#import “PhoneGapCommand.h”
#endif
No its not a typo, wordpress is ignoring less/more than brackets ! maybe it thinks they are tags ? ok i’ll have to write in pseudo-code so the include is:
#ifdef PHONEGAP_FRAMEWORK
#import (less than bracket) PhoneGap/PhoneGapCommand.h (more than bracket)
#else
#import “PhoneGapCommand.h”
#endif
Hiedi:
Appreciate your blog.
I’ve been trying to implement NativeControls in an iOS PhoneGap app, and while it works in portrait orientation, when the device is rotated (in the simulator, simulating a 4.3 device), the toolbar redraws, but the toolBar items (the buttons) disappear. All I see is a black bar.
Here’s how I’m building and calling NativeControls:
…
function onDeviceReady()
{
nativeControls = window.plugins.nativeControls;
nativeControls.createTabBar();
// settings
nativeControls.createTabBarItem(
“settings”,
“Settings”,
“/www/settings_settings.png”,
{“onSelect”: function() {
showSettings();
}}
);
// email tab
nativeControls.createTabBarItem(
“email”,
“Email”,
“/www/tabs/email.png”,
{“onSelect”: function() {
emailIt();
}}
);
// twitter
nativeControls.createTabBarItem(
“twitter”,
“Twitter”,
“/www/tabs/tweet.png”,
{“onSelect”: function() {
showTweet();
}}
);
// about
nativeControls.createTabBarItem(
“about”,
“About”,
“/www/tabs/about.png”,
{“onSelect”: function() {
showAbout();
}}
);
// Compile the TabBar
nativeControls.showTabBar();
nativeControls.showTabBarItems(“settings”, “email”, “twitter”,”about”);
nativeControls.selectTabBarItem(“settings”);
//start everything else
init();
…
Do you experience this problem? Do you have a solution?
Thanks in advance.
The black bar you see is actually the space where the tab bar should be but it is actually positioned off the screen when in landscape mode. As of now the tab bar as its implemented does not handle rotation. I don’t have time at the moment to fix it but you could take a crack at it – I’ll fix it at some point when I have time…