Tuesday 8 September 2015

Adding image rotate with the nativescript camera functionality

You may be aware that you can take photos just using nativescript.

One thing I found on my Galaxy Note 4 though is that the images will always appear in landscape mode regardless of how you took them.

So I thought I would post some code to show you how they can be rotated.

First here is how it looks in the app.





And here is the XML and code.

<Page xmlns="http://www.nativescript.org/tns.xsd" shownModally="onShownModally" loaded="onLoaded" backgroundColor="transparent"  xmlns:imageButton="helpers/buttonhelper">
  <GridLayout columns="*" rows="auto,*" width="300" height="550" cssClass="popup">
    <Label text="Take Photo" row="0" col="0" cssClass="popuptitle"/>
    <StackLayout  row="1" col="0" cssClass="popupouter">
      <GridLayout columns="*" rows="*,auto,auto" cssClass="popupinner" horizontalAlignment="center" verticalAlignment="center">
        <Image imageSource="{{photo}}" row="0" col="0"/>
        <StackLayout orientation="horizontal" horizontalAlignment="center" row="1" col="0">
          <imageButton:ImageButton textPadded="" tap="{{rotateLeft}}" icon="rotateleft"/>
          <imageButton:ImageButton textPadded="" tap="{{takePhoto}}" icon="camera"/>
          <imageButton:ImageButton textPadded="" tap="{{rotateRight}}" icon="rotateright"/>
        </StackLayout>
        <StackLayout orientation="horizontal" horizontalAlignment="center" row="2" col="0">
          <imageButton:ImageButton textPadded="{{languageData.OK}}" tap="{{ok}}" icon="tick"/>
          <imageButton:ImageButton textPadded="{{languageData.Cancel}}"  tap="{{cancel}}" icon="cross"/>
        </StackLayout>
      </GridLayout>
    </StackLayout>
  </GridLayout>
</Page>
/* tslint:disable:use-strict triple-equals max-line-length one-line */

import observableHelper = require("../../helpers/observablehelper");
import pages = require("ui/page");
import camera = require("camera");
import imageSource = require("image-source");
import fs = require("file-system");
import enums = require("ui/enums");

export interface ICallbackResult { photo: imageSource.ImageSource }
export interface ICallback { (result: ICallbackResult, success: boolean): void };

export function display(page: pages.Page, action: ICallback) {
    page.showModal("./dialogs/photo/photo-page", page, action);
}

enum fields { photo };

export class PhotoModel extends observableHelper.PopupModel<ICallbackResult, fields> {
    constructor() {
        super(fields);
        this.setValue(fields.photo, null);
    }

    public returnTrue() {
        this.returnValue = { photo: this.getValue(fields.photo) };
        this.returnResult(this.returnValue, true);
    }

    public rotateRight() {
        var picture = <imageSource.ImageSource>this.getValue(fields.photo);
        if (picture != null) {
            var newImage = new imageSource.ImageSource();

            if (picture.ios) {
                var cgImage = new CIImage(picture.ios);
                newImage.ios = picture.ios.initWithCGImageScaleOrientation(cgImage, 1, UIImageOrientation.UIImageOrientationRight);
            }

            if (picture.android) {
                var matrix = new android.graphics.Matrix();
                matrix.postRotate(90);
                newImage.android = android.graphics.Bitmap.createBitmap(picture.android, 0, 0, picture.android.getWidth(), picture.android.getHeight(), matrix, true);
            }
            this.setValue(fields.photo, newImage);
        }
    }

    public rotateLeft() {
        var picture = <imageSource.ImageSource>this.getValue(fields.photo);
        if (picture != null) {
            var newImage = new imageSource.ImageSource();

            if (picture.ios) {
                var cgImage = new CIImage(picture.ios);
                newImage.ios = picture.ios.initWithCGImageScaleOrientation(cgImage, 1, UIImageOrientation.UIImageOrientationLeft);
            }

            if (picture.android) {
                var matrix = new android.graphics.Matrix();
                matrix.postRotate(-90);
                newImage.android = android.graphics.Bitmap.createBitmap(picture.android, 0, 0, picture.android.getWidth(), picture.android.getHeight(), matrix, true);
            }
            this.setValue(fields.photo, newImage);
        }
    }

    public takePhoto() {
        camera.takePicture({ width: 200, height: 200, keepAspectRatio: true }).then((picture) => {
            this.setValue(fields.photo, picture);
        });
    }
}

export var viewModel = new PhotoModel();
As can be seen then code is quite small. I have put in a request on github to get image rotation in the core code.

I haven't tested the functionality in IOS yet, but from reading it should work.

Sunday 6 September 2015

Demoing your nativescript app with Appetize

You can upload your nativescript IOS or android project to appetize.io.

This allows users to use your IOS/android app in a web browser.

You can upload your app by entering your email address at the site or using their api.

After the first upload, you get a management page which allows you to upload new versions of your project.

Below is an example of project I am currently working on. It logs on with a demo logon "nativescript" with some booklets pre-generated.

Known Bug : The image buttons are not displaying correctly, but everything else seems to work ok.



A larger version link is available here.