Multiple Overlapping Layers of Transparent PNG Images Using AbsoluteLayout in NativeScript

Say you have an image of a face in your app.  There are multiple quadrants you want a user to be able to select.  When they select, it should highlight that area.  You want the user to have the ability to select any combination of these quadrants.

Stacking Images In AbsoluteLayout

Let’s go over the xml layout first.  I accomplished this task using an AbsoluteLayout with four Image views inside it like so.

https://gist.github.com/ChrisFlannagan/ca79529e943350c093f33e13023a03c7

Notice I set the width to 100% but not height. In my full code the height is set in a GridLayout for row 3, but you can set height to 100% or a pixel amount.

I placed a plain face photo as the background for the AbsoluteLayout.  Then I have four images for different quadrants of the face.  These have their absolute position set to 0px top and 0px left.  My overlaying images are the exact same size as the background image of my AbsoluteLayout so I want them to fit exactly over top.

I set the width to the parent element width, this way I know it will translate to different screen sizes.  I get the parent width in my javascript file then set it using this variable parentWidth.

Each image can be turned on or off by setting the visibility to collapsed or visible.  This is controlled in the javascript by the binding object along with the width.  See below:

https://gist.github.com/ChrisFlannagan/c94006689d4783f6667169d53e4f7beb

When the page is loaded I set my initial variables in pageData then turn that Observable into the binding data for the page.  So initially all images will be collapsed (turned off).

Gestures

Next I set gesture detection to my AbsoluteLayout that has been given the id “main-layout” and attached to the variable “quad.” I measure where the touch took place then if it’s in an images quadrant it toggles the collapsed/visible state.  The images are all positioned absolutely so they layer on top of each other.

 

Add Two Color Linear Gradient Backgrounds To View Elements In NativeScript

Since the css linear-gradient isn’t part of NativeScript core yet I went looking for the ability to do this online.  I found a git conversation over at https://github.com/NativeScript/NativeScript/issues/539 that discussed and shared some code.

The code doesn’t work on iOS as expected.  There were some typos in the code that need to be adjusted and wrong variables.  I’ve made those adjustments and you can copy below a functioning version.

I added it to a .js file in my /app directory called grad-bg.js.  Simply load this .js file into your page’s javascript code with:

var gradients = require("~/grad-bg.js");

Import the function via the require method.  Then in your page loaded function call the function on your view.  Make sure to set the view’s id in your xml file and use that id here as the second parameter:

https://gist.github.com/ChrisFlannagan/9fc4b8e2ef871208abcb7f15018cd6ec

Here’s the full javascript function for linearGradient

https://gist.github.com/ChrisFlannagan/5dd5ca61ac01b15cf60b87dda20beaba

Repo: https://github.com/ChrisFlannagan/nativescript-gradient-background

Animate Image Or View Circular Motion In NativeScript

quick-demo.mov video file

I haven’t taken calculus in years and I haven’t needed it since college.  But today I did.  I needed to animate an object going in a circular motion over a graphic of a face in an iPhone/iOS app I’m developing.  These examples are purely wire framed with no official graphics or design.

What You Need

  • An AbsoluteLayout view
  • An Image view (or whatever you’d like really) inside that AbsoluteLayout
  • A starting x and y coordinate which will be the center of your circular path
  • Radius of the circle you want for your path
  • Angle (or animation speed)

Layout

 

We are going to build our layout dynamically.  I found this to be the best method for sizing things and positioning our elements.  Let’s take a look at that code.

First we need some xml to hold our generated layout items.  A simple stack layout works here, I use the horizonalAlignment and verticalAlignment to fill up the screen.  Well, it’s supposed to but doesn’t seem to function just right depending on the xml around it.  Still, it will work well enough for our demo here.

<StackLayout id="main-layout" orientation="vertical"
      horizotnalAlignment="stretch" veritcalAlignment="stretch">
</StackLayout>

Let’s fill that with some elements we want for our circular animation.  I’ve loaded this example code up with comments to explain what’s happening but I’ll point some important bits after this code block.

https://gist.github.com/ChrisFlannagan/56c55de015c0ee55b9310d3b542ac8a4

So our angle variable in combination with our duration speed is what controls the speed of our animation.  Each “frame” is 1000 milliseconds which is set at the very end of the setInterval function’s code block, the second parameter.

Our call to .animate() has duration: 1000 ms as well so the time it will take to transition to the next point on the circle is also 1 second.  If you think about this that means it’s going to move in a straight line from one point on the circle to the next as in this graph below.  Within 1 second, before the next frame’s location on the circle path is calculated, it animates the transition between points.

Imagine the little red circles are the calculated points transitioned to every 1 second of setInterval().  Our object moves in a straight line between them over the course of that one second.

circle-animation-lines

Starting Coordinates

The oddest problem I encountered was that my animated object shot to the outer radius of the circle from the center of it before starting reach the initial calculated frame.  What was happening is that the object is animated to it’s next frame based on it’s original x and y position.  So each calculation is based on that.  Since I start at a 0 degree angle the y axis doesn’t change when it starts but the x axis does.

I tried changing the xOrigin property to 50 (the radius) less than where I plotted it in hopes this would allow all future transition calculations to be based on that and it would seamlessly animate from the beginning but that didn’t seem to work.  So you can see in the code my solution is to subtract 50 from the x when calculated and it gives a calculation like the xOrigin is 50 less.  This seems to work just fine.

Adding Controls

In my project I have a start and stop button.  It also has a timer countdown which is not complete yet so the code isn’t going to do anything when the timer runs out.  You can see it in the main video above. I’ll post that full code below so maybe you can use some of the functionality.  It’s not commented as cleanly but notice in the xml that there are start and stop buttons with “tap” parameter which calls the respective functions in our .js file.

https://gist.github.com/ChrisFlannagan/3e74f87b5769b1f00f4f48f28faf3107

And the javascript

https://gist.github.com/ChrisFlannagan/b1e8c803fa3136e26bfca406ab5c35e8

 

Touch “Hold” Function: Button Held Down In NativeScript

Tonight I needed to run a function constantly while a button is being held down in my app.  I found the gestures functionality and it had what I needed but I had to do some work to get it to perform what I was after.

My task was this, when someone puts their finger down on a ‘Record’ button I need to store what the sound level was every 10/1000 of a second.  So here’s what I did.

Using Gestures Touch to Define “Hold”

When the page is loaded I assign the button element to a variable then I add the on touch function.  It’s important to declare that button’s variable at the beginning of your JS so it’s available in all functions.  Also important to require your view module and gesture.  Here’s a snippet that covers all of that:

var viewModule = require("ui/core/view");
var audio = require("nativescript-audio");
var timer = require("timer");
var gestures = require("ui/gestures");
var documents = fs.knownFolders.currentApp();
var recorder = new audio.TNSRecorder();
var isRec = false;
var timeRec;
var currentRecording = new Array();
var page;
var soundlevel;

exports.loaded = function(args) {
    page = args.object;
    soundlevel = viewModule.getViewById(page, "soundlevel");

    soundlevel.on(gestures.GestureTypes.touch, function (args) {
        if(args.action == "down" || args.action == "up") {
            recordVolume( args.action );
        }
    });
}

Now that I’m calling a function, let’s tell that function what to do.  We have our “soundlevel” view which is just a button with id=”soundlevel” and we’ve assigned an action of “on” for it when gesture touch occurs.  The gesture touch passes args that include “action” as a property.  That property can be “down”, “up” amongst other things, but these are what we need.  So now let’s build a function to handle the hold.

function recordVolume(action) {
    var recorderOptions = {
        filename: documents.path + "/recording.caf",
        infoCallback: function() {
            console.log();
        },

        errorCallback: function() {
            console.log();
        }
    };
    if( action == "up" ) {
        recorder.stop();
        isRec = false;
        timer.clearInterval(timeRec);
    } else {
        recorder.start( recorderOptions );
        currentRecording = new Array();
        isRec = true;
        timeRec = timer.setInterval( function() {
            var barWidth = Math.round((160+Number(recorder.getMeters(0, "average")))/160*100);
            currentRecording.push(barWidth);
        }, 10);
    }
}

Note: barWidth is an integer I end up assigning as a percentage of the screen width.  This way I can give a visualization of sound level.

Here we check wether the action is up or down and do our desired functionality based on that.  If it’s “up” that means the finger has been lifted from the screen so we stop everything.

If the action is “down” that means the user has just pressed the button.  So we user the timer library and set an interval.  Basically every 10/1000 of a second we perform a calculation of the sound level and push that to our array of sound levels.  Now we can analyze that and do what we want to do!

 

Don’t Touch Things In /platform/

I learned this the hard way just like every thing else in here related to NativeScript.  It’s a young framework and there is not a ton of documentation or much of a community yet.  But I love it and the slack community is pretty active.

I finally finished an app and needed to get it in iTunes for my boss.  I started following build instructions using Xcode which involved following along with NativeScript’s docs and also getting linked over to Apple’s dev docs a lot.  Somewhere along the way I felt that I needed to make some changes to my app in Xcode so that it would be able to get submitted for review as it kept hitting errors when trying to upload.

Icons

Icons are handled in a way that’s not overly complex but super easy to screw up.  When you open your project that NativeScript generates you will see a lot of stuff going on.  One of those is the “Use Asset Catalog” option for your icons.

If you have any issues with icons when trying to upload to iTunes you will probably start toying with the Asset Catalog and your info.plist.  One word of advice: DON’T!

NativeScript handles this for you just fine.  Make sure you have all required icons in your /app/App_Resources/iOS directory (including your 120px ones! I.e. 40@3x and so on, these are new and screwed me up bad).

tns prepare ios

The point is, when you want to build your app just run tns prepare ios then open the project in Xcode.  You can have it handle some of the automated tasks you might need when it pops up its warnings, but don’t screw with the .plist file or the asset catalog stuff or you will spend an entire day like me trying to figure out what’s wrong.

When you run the prepare CLI command NativeScript builds out a fully functional Xcode project for you.  Other than dealing with setting up your Apple developer account and connecting it to Xcode there’s not much else for you to do.  When you are ready to build and submit to the app store all you have to do is make sure the device selected is “Generic iOS Device” hit Product > Archive then when the Organizer window opens Validate then Upload to iTunes.

Tutorial, Shmutorial, Learn New Stuff By Switching It Up

My new job needs an iPhone and Android app.  I’ve built a few shaky Android apps using java and a LOT of google.  They worked but not being a java developer I know my methods and coding standards were sub-par at best.  I’ve never attempted an iOS app.

Thankfully there are few pretty badass frameworks out now that let you use javascript or something similar to build native apps.  A friend recommended NativeScript.js and so far I’m really liking it.  But I’m learning a brand new technology so I do the same thing most of us do.  I go through the official, beginners tutorial example to build out a sample app.

Make It Your Project

But I like to do it different.  I find this method not only immerses you in the new language or framework but also helps you start solving problems and learning deeper parts of the system at the same time.

I need a fairly basic app.  It needs to let users log some information kind of like MyFitnessPal and keep track with analytics.  Nothing crazy but will include a couple features that dive into some deeper modules like using the camera.

So I start the tutorial going step by step which gets a nice boilerplate up, gives you a hello world and then moves onto building out features for their sample which is a groceries list app.  This is when I start changing things to do what I want them to do.

For Example

Chapter 2 gets into building out a view for the initial screen which is a login or sign up screen.  Hey, we need this in our app! Awesome, so I’m going to follow this part exactly for now.

The tutorial taught me how to implement css stylesheets app wide or specific to individual pages.  We styled up the login inputs and buttons and now I want to side-track a bit.  I want my companies logo at the top.  I leave the tutorial right where it is and I pop open a new tab and find the developers resources on the website.  I find UI Components section, Images is listed in there, I click and now I dive into how images work.

I already know how to put UI components in the layout from the tutorial so this should be easy.

Nope

OK, so I have to load the Image module and I also have to then create an Image instance.  I’ve loaded the main module already and that was in the main app file, so do I do it there or do I do it in the page’s JS file? Let’s experiment!!

This is how I find the learning process really starts getting more intuitive.  I’m forced to solve a problem instead of just following a line by line tutorial on creating an app.  For me, and I suspect for others, this process actually makes learning a lot faster and more in depth.  It might not work for everyone but I have to suggest giving it a go.

Note: They actually show how to add an image not even a section further down the tutorial but I still find the fact I took the time to dig through developer resources for a few a great learning experience.

In Conclusion

This won’t work for everything.  Some languages and frameworks, especially those you have absolutely zero knowledge of, following the initial tutorials exactly is not a bad idea for introducing yourself to the system.  But this comes in handy and again, I do recommend giving it a go sometime.