Skip to content

Instantly share code, notes, and snippets.

@grantges
Last active October 31, 2020 20:13
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save grantges/1c5df7f7b0eada2c4610 to your computer and use it in GitHub Desktop.
Save grantges/1c5df7f7b0eada2c4610 to your computer and use it in GitHub Desktop.
Shapes With Appcelerator Titanium and Hyperloop

Custom Alloy Tags based on Appcelerator Hyperloop modules

With Alloy and Hyperloop, you can quickly and easily expose native UI as Custom Alloy tags by leveraging the namespace (ns) attribute and commonjs modules.

Alloy allows you to create your own UI element that can be included into the XML View heirarchy in one of two ways, an Alloy Widget or through the use of Custom Tags.

To create your own custom tag, you link the tag to the commonjs module with the namespace attribute (ns). Here is an example using a custom tag to render a standard Titanium View:

<!-- index.xml -->

<Alloy>
  <Window class="container" >
    
    <!-- Standard Titanium View with a red backgroundColor-->
    <View id="viewContainer" backgroundColor="red">
    
      <!-- 
        Custom Tag, leverages a commonjs module to create the view based on its name.
        Any attributes on the tag are passed into the associated funciton as an object
      -->
      <Square module="square" color="blue" size="100" />
      <!--    ^             ^
              |             |------- local attribute, will be passed in as part of the object passed to the commonjs function
              |
              |----- This is the namespace attribute, it links this tag to a commonjs module file in app/lib/square.js 
      -->
    </View>
    
  </Window>
</Alloy>

For the tag to actually render, you need to have a function that corresponds to the tag within the XML View

// app/lib/square.js

/**
 * Square creation function - creates a small square view 
 * @params options {Object} - xml tag attributes passed in as a singular object
 */
exports.createSquare = function(options) {
  
  // Create a new Ti View
  var square = Ti.UI.createView();

  // Update View based on XML attributes
  square.backgroundColor = options.color || "black";
  square.height = options.size || 50;
  square.width = options.size || 50;

  // Return the View, this will be added automatically by Alloy to the parent view element
  return square;

};

In the commonjs module above, its important to note that we're export a function called createSquare. This is necessary since the Alloy Custom Tag (Square) is going to look for that particular naming convention.

Tag Names and createFunction declarations.

If your tag name was <BarChart ns="charts" /> then you would use the following notation in your commonjs module located at app/lib/charts.js:

exports.createBarChart = function(options){

};

Namespaces

The namespace reference is a way to point the custom tag to the appropriate commonjs module. This can be nested under multiple folders or can just be a file under the app/lib folder of your Alloy app.

Note: If you are referencing the file from within the lib folder, you do not have to specifically call out lib in the namespace reference. Child folders under the lib folder would need to be included in the namespace (Ex. <BarChart ns="charts/barchart" /> would be correct if you had a file app/lib/charts/barchart.js).

<Alloy>
<Window class="container">
<Square module="shapes" top="25" height="100" width="100" />
<Circle module="shapes" top="200" height="100" width="100" />
<Arc module="shapes" top="375" height="100" width="100" startAngle="0" endAngle="270"/>
</Window>
</Alloy>
/**
* Hyperloop library for creating shapes
* This file should go in your `app/lib` folder
*/
var Hyperloop = require('Hyperloop');
var CGRect = require('CGRect');
var CGPoint = require('CGPoint');
var UIView = require('UIView');
var UIColor = require('UIColor');
var UIBezierPath = require('UIBezierPath');
var CAShapeLayer = require('CAShapeLayer');
var M_PI = 3.14159265358979323846;
/** Utility Functions **/
function DEGREES_TO_RADIANS(angle) { return (Number(angle) / 180.0 * Math.PI) };
function RGB_TO_HEX(r, g, b) {
function componentToHex(c) { var hex = c.toString(16); return hex.length == 1 ? "0" + hex : hex;}
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
/**
* Square Class
* Creates a circular view object using Hyperloop with an appropriate clipping mask.
*
* Objective-C Reference Code
* ==========================
* UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
* view.backgroundColor = [UIColor blueColor];
*
*/
exports.createSquare = function _creaetSquare(_params){
var left = _params.left || (Ti.Platform.displayCaps.platformWidth/2 - _params.width/2);
var top = _params.top || (Ti.Platform.displayCaps.platformHeight/2 - _params.height/2);
var view = UIView.initWithFrame(CGRect.Make(left, top, _params.width, _params.height));
view.backgroundColor = UIColor.blueColor();
return view.native;
};
/**
* Circle Class
* Creates a circular view object using Hyperloop with an appropriate clipping mask.
*
* Objective-C Reference Code
* ==========================
* UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
* view.backgroundColor = [UIColor blueColor];
*
* CAShapeLayer *shape = [CAShapeLayer layer];
* UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:view.center radius:(view.bounds.size.width / 2) startAngle:0 endAngle:(2 * M_PI) clockwise:YES];
* shape.path = path.CGPath;
* view.layer.mask = shape;
*/
exports.createCircle = function _createCircle(_params){
var left = _params.left || (Ti.Platform.displayCaps.platformWidth/2 - _params.width/2);
var top = _params.top || (Ti.Platform.displayCaps.platformHeight/2 - _params.height/2);
var view = UIView.initWithFrame(CGRect.Make( left , top, _params.width, _params.height));
view.backgroundColor = UIColor.blueColor();
var rect = view.frame;
var centerPoint = CGPoint.Make(rect.width / 2, rect.height / 2);
var path = UIBezierPath.bezierPath();
path.addArcWithCenterRadiusStartAngleEndAngleClockwise(centerPoint, rect.width / 2, DEGREES_TO_RADIANS(0), DEGREES_TO_RADIANS(360), true);
var shapeLayer = CAShapeLayer.layer();
shapeLayer.path = path.getCGPath();
shapeLayer.strokeColor = UIColor.blackColor().getCGColor();
shapeLayer.fillColor = UIColor.blackColor().getCGColor();
shapeLayer.lineWidth = 0;
view.layer.mask= shapeLayer;
return view.native;
};
/**
* Arc Class
* Creates a circular view object using Hyperloop with an appropriate clipping mask.
*/
exports.createArc = function _createArc(_params){
var left = _params.left || (Ti.Platform.displayCaps.platformWidth/2 - _params.width/2);
var top = _params.top || (Ti.Platform.displayCaps.platformHeight/2 - _params.height/2);
var view = UIView.initWithFrame(CGRect.Make( left , top, _params.width, _params.height));
view.backgroundColor = UIColor.clearColor();
var rect = view.frame;
var centerPoint = CGPoint.Make(rect.width / 2, rect.height / 2);
var path = UIBezierPath.bezierPath();
path.addArcWithCenterRadiusStartAngleEndAngleClockwise(centerPoint, rect.width / 2, DEGREES_TO_RADIANS(_params.startAngle), DEGREES_TO_RADIANS(_params.endAngle), true);
var shapeLayer = CAShapeLayer.layer();
shapeLayer.path = path.getCGPath();
shapeLayer.strokeColor = UIColor.blueColor().getCGColor();
shapeLayer.fillColor = UIColor.clearColor().getCGColor();
shapeLayer.lineWidth = _params.lineWidth || 25;
view.layer.addSublayer(shapeLayer);
return view.native;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment