Webview orientation with Phonegap

1 Comment

I was having this really weird problem in PhoneGap where when the orientation would change the webview would not resize to fit the whole screen.  Turns out that the meta viewport tag was causing the problem.  My viewport setting was this:

1
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>

So when I would re-orient my iPad to landscape it would still be using the device-width setting for its width.  So what I did was bind to the orientationchange event and call a JavaScript function each time the page re-oriented.  That function had this code in it.

1
2
3
4
5
6
var el = $('meta[name=viewport]');
if (window.orientation == 90 || window.orientation == -90) {
el.attr('content', 'width=device-height');
} else {
el.attr('content', 'width=device-width');
}

Now the orientation works in both portrait and landscape.


Phonegap build issues

1 Comment

I have had the worst two days.  Here I have my app, Notiffi, pretty much ready to go.  I have it set up in iTunes connect, ready to go.  I am ready for the last step in the process, archiving.  I hit Archive and BOOM.  Nothing.  Build works.  Debug works.  Archive does not work, and it still doesn’t as I’m writing this.  Also, suddenly I start getting linker errors like this when I was building it and so I couldn’t even check to see if the app worked any more.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(null): "_OBJC_CLASS_$_CDVPlugin", referenced from:
(null): _OBJC_CLASS_$_ChildBrowserCommand in ChildBrowserCommand.o
(null): _OBJC_CLASS_$_PushNotification in PushNotification.o
(null): "_OBJC_CLASS_$_CDVViewController", referenced from:
(null): _OBJC_CLASS_$_MainViewController in MainViewController.o
(null): "_OBJC_METACLASS_$_CDVPlugin", referenced from:
(null): _OBJC_METACLASS_$_ChildBrowserCommand in ChildBrowserCommand.o
(null): _OBJC_METACLASS_$_PushNotification in PushNotification.o
(null): "_OBJC_CLASS_$_CDVPluginResult", referenced from:
(null): Objc-class-ref in PushNotification.o
(null): "_OBJC_METACLASS_$_CDVViewController", referenced from:
(null): _OBJC_METACLASS_$_MainViewController in MainViewController.o
(null): Symbol(s) not found for architecture armv7
(null): Linker command failed with exit code 1 (use -v to see invocation)

While Archiving (at the moment) still isn’t working I did find a way to get the linker errors to go away.  I presume that it was because of some incorrect paths in Xcode, but I’m a PHP, Java, HTML and JavaScript developer and so compiler issues on a Mac with Xcode are things that I know a lot about.  Linux?  Sure.  Mac/Xcode?  Not a bit.

But my workaround for this was relatively easy.  I removed CordovaLib as a subproject and built it separately.  Then I did find ./ -name libCordova.a on my home directory, created a new dir called /lib in my project and copied libCordova.a there.  I cleaned the project, rebuilt and and voila! it worked.

Now on to fix the Archiving problem.  For some reason I get this

1
Command /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/copypng emitted errors but did not return a nonzero exit code to indicate failure

and

1
/Users/tk421/Documents/Notiffi/Notiffi/Plugins/ChildBrowserCommand.h:5:9: 'Cordova/CDVPlugin.h' file not found

Hopefully these will be easy issues

[UPDATE]

They were not easy issues. I basically created a brand new Cordova project and copied all of my source files in.  Clearly there was something wrong in my build configuration, but I don’t know what it was.


Transform performance on mobile applications

Leave a comment

Just a quick one today.  I have read on several web pages that if you want to speed up the rendering of your site that you need to add -webkit-transform: translate3d(0,0,0) to the page elements to have the browser use the GPU to render the page instead of the CPU.

I tried this trick several times and it never seemed to work.  But I think that today I figured out why not.

I would apply this trick to a container element that houses the things that were animating.  This may not seemed to have been the correct way of doing it.  I had a DIV with a UL and several LI elements in it.  I applied the trick to the DIV under the assumption that it would accelerate everything in the container.

I think I was wrong.  It almost looks like you need to apply the trick to the individual element that you are animating.  In my case, the LI elements because they start out with a height of 0px and are expanded to Xpx.  The animations worked fine beforehand, but as soon as it was coupled with automated scrolling of the container the performance would drop tremendously down to around 6-8 frames per second.  When I moved the translate3d() trick from the DIV to the LI elements the animations and the scrolling became quite smooth.

So the lesson of the day is that if you want to use the GPU trick, apply it directly to the elements you are animating and not their container.

Additionally, it seems like applying the GPU trick also messes up CSS fixed position elements.  In other words, position: fixed ends up being not so.


External links in phonegap

Leave a comment

Found an interesting behavior today.  I have an LI element that has a click handler on it, but that LI element has an A element which has a link.  So it looked kind of like this:

1
2
3
4
5
<ul>
<li onclick="doSomething(); ">
<A href="http://somewhere" target="_blank">Go</a>
</li>
</ul>

But when I clicked on the link the event would fire (expectedly) but it wouldn’t open the link in the mobile browser window.

Pulled my hair out for a while and then I wanted to see if the A element was actually being clicked so I attached a click event.  It was, which made no sense.  Why would the click fire but not the href?

On a whim I thought I would try stopping the event propagation.  Turns out that worked.  The link opened in the new window.  So now my code has this in there.

1
2
3
4
5
var a = $('<a target="_blank" />');
a.attr('href', myLink);
a.click(function(e() {
  e.stopPropagation();
});

Just a little trick.  Hope it helps you.


Handling clicks for mobile platforms [UPDATED]

1 Comment

I’ve been doing a lot of work in Phonegap lately and I really like it.  However, one of the things I don’t like is that click events are really, really slow.  There’s a reason for this.  How does the browser know if it’s a click or the beginning of a move?  The answer to this is to bind your click events to the “touchend” event.

So, in JQuery you would previously do something like this.

1
target.bind('click', callback);

But that introduces the lag.  So I wrote a little function where instead of calling bind() I would call this function instead.  It would determine if it’s a desktop or mobile device and then either register the click event or the touchend event.

However, there is a problem with this.  The touchend call might come at the end of a move, in which case you don’t want to fire the click event’s callback.  So I wrote a little bit of code that checks for a move event and then unbinds the touchend event, storing the callback in the new function called at touchend.  If there is no move event the callback is called at touchend.  If the move event is not thrown the callback is re-bound at touchend so the next time a “click’ event is triggered the event will be there to handle it.

1
2
3
4
5
6
7
8
9
target.bind('touchmove', function() {
  var cb = callback;
  $(this).unbind('touchend', cb);
  $(this).bind('touchend', function() {
    var cbb = cb;
    $(this).bind('touchend', cbb);
    });
  });
target.bind('touchend', callback);

[EDIT]

It turns out that this is wrong.  One of the things that I neglected to see offhand was that this code was fine… IF only one touchmove event was fired.  However, if many were fired the callback would be added for each time touchmove was called.  If your finger was moving a lot, then a lot of calls to the callback would be executed on touch end.

Here is code that actually works properly.

1
2
3
4
5
6
7
8
9
10
11
12
target.bind('touchmove', function() {
  $(this).data('moved', true);
});
target.bind('touchend', function() {
  if ($(this).data('moved')) {
    $(this).data('moved', false);
    return;
  }
  $(this).data('moved', false);
  $(this).trigger('touchendnomove');
});
target.bind('touchendnomove', callback);

What this does differently is simply set a flag if a touchmove event is fired.  Then on touchend it checks to see if the flag has been set.  If not a CUSTOM event is fired.  This is important because we want $(this) to refer to the target when an event is fired.  This way we don’t have to do any magic to get the proper “this” association done and we don’t have the problem of those pesky touchmove events.