This workshop requires a Macintosh computer and an iOS device (version 8 or above).
We will be building a very simple Instagram like app using iOS native features and web tech.
- Follow steps in phonegap-webview-ios to set up a new iOS native project
- Updating
www - Clone phonegap-start somewhere temporary
- Delete
www/index.html,www/css/index.cssandwww/js/index.jsunderPods/Pods/phonegap-ios-template/Resources. - Replace files from Step 2.2 with respective files from the
phonegap-startrepository.

- Run the app

- Adding native view elements to storyboard
- Click on
Main.storyboardand drag & drop aViewto it. Add constraint to to center theViewhorizontally

- Drag&drop a
Buttonto storyboard and give and label it "Surprise". Add constraint to it to center horizontally - Replace ViewController.h with the following code
#import <UIKit/UIKit.h> #import <Cordova/CDVViewController.h> #import <AVFoundation/AVCaptureSession.h> #import <AVFoundation/AVCaptureVideoPreviewLayer.h> #import <AVFoundation/AVMediaFormat.h> #import <AVFoundation/AVCaptureInput.h> #import <AVFoundation/AVCaptureOutput.h> #import <AVFoundation/AVVideoSettings.h> #import <ImageIO/ImageIO.h> @interface ViewController : CDVViewController @property(nonatomic, weak) IBOutlet UIView *vImagePreview; @property (weak, nonatomic) IBOutlet UIButton *surpriseBtn; @property(nonatomic, retain) AVCaptureStillImageOutput *stillImageOutput; - (void)captureNow:(void (^)(NSData*))sendImageToJS; @end
- In Main.storyboard make sure "ViewController" is selected (as shown below).

- Go back to storybord, select the assistant editor on the top right, ctrl-click on UIView and drag it to the
vImagePreviewproperty on the right editor
<img src="img/imagedrag.png" width="760px" />
- Repeat same operation on Button, drag it to the surprise
surpriseBtnproperty on the right editor
<img src="img/buttondrag.png" width="760px" />
-
add the following code to the
ViewController.mfile- (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; // hide button self.surpriseBtn.hidden = YES; // creating capture session AVCaptureSession *session = [[AVCaptureSession alloc] init]; session.sessionPreset = AVCaptureSessionPresetMedium; // creating a view layer to preview the capture using the session above CALayer *viewLayer = self.vImagePreview.layer; NSLog(@"viewLayer = %@", viewLayer); AVCaptureVideoPreviewLayer *captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session]; // setting the session bounds captureVideoPreviewLayer.frame = self.vImagePreview.bounds; [captureVideoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill]; // adding the preview layer to the view [self.vImagePreview.layer addSublayer:captureVideoPreviewLayer]; // setting the device (front vs back facing camera) AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; NSError *error = nil; AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error]; if(!input) { NSLog(@"Error: trying to open camera: %@", error); } [session addInput:input]; self.stillImageOutput =[[AVCaptureStillImageOutput alloc] init]; NSDictionary *outputSettings = @{ AVVideoCodecKey: AVVideoCodecJPEG }; [self.stillImageOutput setOutputSettings:outputSettings]; [session addOutput:self.stillImageOutput]; [session startRunning]; } - (void) captureNow:(void (^)(NSData*)) sendImageToJS { AVCaptureConnection *videoConnection = nil; for(AVCaptureConnection *connection in self.stillImageOutput.connections) { for(AVCaptureInputPort *port in [connection inputPorts]) { if([[port mediaType] isEqual:AVMediaTypeVideo]) { videoConnection = connection; break; } } if(videoConnection) { break; } } NSLog(@"Requesting capture from: %@", self.stillImageOutput); if([videoConnection isEnabled]) { [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) { CFDictionaryRef exifAttachments = CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, NULL); if(exifAttachments) { NSLog(@"attachments: %@", exifAttachments); } else { NSLog(@"No attachments"); } NSData* imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; sendImageToJS(imageData); }]; } else { NSLog(@"Video connection is disabled"); } self.surpriseBtn.hidden = NO; }
-
Click on File > New > File. Select iOS/Source -> Cocoa Touch Class. Click Next and name it
MyCustomCameraPlugin
-
Your
MyCustomCameraPlugin.hshould look like this#import <Cordova/CDVPlugin.h> @interface MyCustomCameraPlugin : CDVPlugin -(void)captureNow:(CDVInvokedUrlCommand*) command; -(void)surprise; @end
-
Your
MyCustomCameraPlugin.mshould look like this#import "MyCustomCameraPlugin.h" #import "ViewController.h" @implementation MyCustomCameraPlugin -(void)captureNow:(CDVInvokedUrlCommand *)command { NSLog(@"Capturing now..."); ViewController* mvc = (ViewController*)[self viewController]; [mvc captureNow:^(NSData* imageData) { NSLog(@"Sending imageData back to Javascript"); CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; } -(void)surprise { [self.commandDelegate evalJs:@"app.surprise();"]; } @end
-
Add the following lines in
Pods/phonegap-ios-template/Resources/www/index.htmlafter line 38
<section id="imageSection">
<img src="" alt="image will be displayed here..." id="myPic">
</section>
<button type="button" id="captureNowBtn">Capture Nao!</button>- Also add the following css link tag
```HTML
<link rel="stylesheet" type="text/css" href="css/image.css">
```
- Your
Pods/phonegap-ios-template/www/js/index.jsshould look like this
```JavaScript
var app = {
clean: false,
// Application Constructor
initialize: function() {
this.bindEvents();
},
// Bind Event Listeners
//
// Bind any events that are required on startup. Common events are:
// 'load', 'deviceready', 'offline', and 'online'.
bindEvents: function() {
document.addEventListener('deviceready', this.onDeviceReady, false);
},
// deviceready Event Handler
//
// The scope of 'this' is the event. In order to call the 'receivedEvent'
// function, we must explicitly call 'app.receivedEvent(...);'
onDeviceReady: function() {
app.receivedEvent('deviceready');
},
// Update DOM on a Received Event
receivedEvent: function(id) {
var parentElement = document.getElementById(id);
var listeningElement = parentElement.querySelector('.listening');
var receivedElement = parentElement.querySelector('.received');
listeningElement.setAttribute('style', 'display:none;');
receivedElement.setAttribute('style', 'display:block;');
console.log('Received Event: ' + id);
var captureNowBtn = document.getElementById("captureNowBtn");
captureNowBtn.addEventListener('click', this.captureNow);
},
captureNow: function() {
console.log("JS::captureNow");
var win = function(r) {
if(app.clean === false) {
// deleting deviceready
var deviceready = document.getElementById("deviceready");
var drp = deviceready.parentElement;
drp.removeChild(deviceready);
// changing app css a bit
var appCss = document.getElementsByClassName("app")[0];
appCss.style.top = "25%";
appCss.style.background = "none";
// deleting title
var h1 = document.getElementsByTagName("h1")[0];
var parentH1 = h1.parentElement;
parentH1.removeChild(h1);
app.clean = true;
}
var myPic = document.getElementById("myPic");
myPic.src = "data:image/jpeg;base64,"+r;
};
var fail = function(e) {
console.log("An error occurred", e.message)
};
cordova.exec(win, fail, 'MyCamera', 'captureNow', []);
},
surprise: function() {
var filters = ["grayscale", "blur", "saturate", "sepia", "invert", "brightness", "contrast", "opacity"];
var imageSection = document.getElementById("imageSection");
imageSection.className = filters[Math.floor((Math.random() * 10) + 1)];
}
};
app.initialize();
```
-
Create a new file under
Pods/phonegap-ios-template/Resources/www/css, name itimage.cssand add this to it.grayscale img { filter: grayscale(1); -webkit-filter: grayscale(1); -moz-filter: grayscale(1); -o-filter: grayscale(1); -ms-filter: grayscale(1); } .grayscale img:hover img:active { filter: grayscale(0); -webkit-filter: grayscale(0); -moz-filter: grayscale(0); -o-filter: grayscale(0); -ms-filter: grayscale(0); } .blur img { filter: blur(5px); -webkit-filter: blur(5px); -moz-filter: blur(5px); -o-filter: blur(5px); -ms-filter: blur(5px); } .blur img:hover img:active { filter: blur(0); -webkit-filter: blur(0); -moz-filter: blur(0); -o-filter: blur(0); -ms-filter: blur(0); } .saturate img { filter: saturate(500%); -webkit-filter: saturate(500%); -moz-filter: saturate(500%); -o-filter: saturate(500%); -ms-filter: saturate(500%); } .saturate img:hover img:active { filter: saturate(100%); -webkit-filter: saturate(100%); -moz-filter: saturate(100%); -o-filter: saturate(100%); -ms-filter: saturate(100%); } .sepia img { filter: sepia(1); -webkit-filter: sepia(1); -moz-filter: sepia(1); -o-filter: sepia(1); -ms-filter: sepia(1); } .sepia img:hover img:active { filter: sepia(0); -webkit-filter: sepia(0); -moz-filter: sepia(0); -o-filter: sepia(0); -ms-filter: sepia(0); } .invert img { filter: invert(1); -webkit-filter: invert(1); -moz-filter: invert(1); -o-filter: invert(1); -ms-filter: invert(1); } .invert img:hover img:active { filter: invert(0); -webkit-filter: invert(0); -moz-filter: invert(0); -o-filter: invert(0); -ms-filter: invert(0); } .brightness img { filter: brightness(50%); -webkit-filter: brightness(50%); -moz-filter: brightness(50%); -o-filter: brightness(50%); -ms-filter: brightness(50%); } .brightness img:hover img:active { filter: brightness(100%); -webkit-filter: brightness(100%); -moz-filter: brightness(100%); -o-filter: brightness(100%); -ms-filter: brightness(100%); } .contrast img { filter: contrast(0.3); -webkit-filter: contrast(0.3); -moz-filter: contrast(0.3); -o-filter: contrast(0.3); -ms-filter: contrast(0.3); } .contrast img:hover img:active { filter: contrast(1); -webkit-filter: contrast(1); -moz-filter: contrast(1); -o-filter: contrast(1); -ms-filter: contrast(1); } .opacity img { opacity:0.3; } .opacity img:hover img:active { opacity:1; } img{ -webkit-transition: all 0.3s ease-in-out; -moz-transition: all 0.3s ease-in-out; -o-transition: all 0.3s ease-in-out; -ms-transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out; width: 240px; } img:hover img:active{ -webkit-transform: scale(1.1); -moz-transform: scale(1.1); -o-transform: scale(1.1); transform: scale(1.1); }
-
Go back to the assistant editor with
Main.storyboardon the left andViewController.mon the right and Ctrl-drag from Surprise button to just above thecaptureNowmethod.
- create an action and name it
surpriseMeand add the following code to it ```Objective-C - (IBAction)surpriseMe:(id)sender { MyCustomCameraPlugin *plugin = [self.pluginObjects objectForKey:@"MyCustomCameraPlugin"]; [plugin surprise]; }
```
- Also add this header at the very top
```Objective-C
#import "MyCustomCameraPlugin.h"
```
- Add the following lines to your
Pods/phonegap-ios-template/Resources/www/config.xmlbetween the two<widget>tags
<feature name="MyCamera">
<param name="ios-package" value="MyCustomCameraPlugin" />
</feature>- Run the app!


